mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
Every first-party Python caller and test now names the mount the API actually answers on. Mechanical: URL literals only, and only where the /api/ was ours -- a preceding word character means it was a module path like insights/api/ or a third-party host like slack.com/api/, and those are untouched. Left saying /api/, each for a reason rather than for safety: - products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints - services/llm-gateway deploys separately and is under a code freeze, so moving its two calls would break it during the skew - the mcp_store and Slack/Teams redirect_uris are registered with third parties and must byte-match what was registered - the Wizard and Raycast client-metadata paths are OAuth client_ids - /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the ingress routes to the Rust fleets, held by released SDK versions Test URLconfs moved with the requests that call them, and the Datastore user-selection fixtures now carry the leading slash that request.path has.
163 lines
5.9 KiB
TypeScript
163 lines
5.9 KiB
TypeScript
import { describe, expect, it, vi } from 'vitest'
|
|
|
|
import externalDataSourcesDbSchema from '@/tools/insightsAiTools/externalDataSourcesDbSchema'
|
|
import externalDataSourcesJobs from '@/tools/insightsAiTools/externalDataSourcesJobs'
|
|
import externalDataSourcesPreview from '@/tools/insightsAiTools/externalDataSourcesPreview'
|
|
import type { Context } from '@/tools/types'
|
|
|
|
function createMockContext(requestMock: ReturnType<typeof vi.fn>): Context {
|
|
return {
|
|
api: { request: requestMock } as any,
|
|
stateManager: { getProjectId: vi.fn().mockResolvedValue('42') } as any,
|
|
env: {} as any,
|
|
sessionManager: {} as any,
|
|
cache: {} as any,
|
|
getDistinctId: async () => 'test-distinct-id',
|
|
trackEvent: async () => {},
|
|
}
|
|
}
|
|
|
|
describe('externalDataSourcesDbSchema', () => {
|
|
it('spreads payload into flat body alongside source_type', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue({ tables: [] })
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesDbSchema()
|
|
|
|
await tool.handler(context, {
|
|
source_type: 'Postgres',
|
|
payload: {
|
|
host: 'localhost',
|
|
port: '5432',
|
|
database: 'mydb',
|
|
user: 'admin',
|
|
password: 'secret',
|
|
schema: 'public',
|
|
},
|
|
})
|
|
|
|
expect(requestMock).toHaveBeenCalledWith({
|
|
method: 'POST',
|
|
path: '/v1/projects/42/external_data_sources/database_schema/',
|
|
body: {
|
|
source_type: 'Postgres',
|
|
host: 'localhost',
|
|
port: '5432',
|
|
database: 'mydb',
|
|
user: 'admin',
|
|
password: 'secret',
|
|
schema: 'public',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('does not nest payload as a sub-object', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue({})
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesDbSchema()
|
|
|
|
await tool.handler(context, {
|
|
source_type: 'MySQL',
|
|
payload: { host: 'db.example.com' },
|
|
})
|
|
|
|
const body = requestMock.mock.calls[0]![0].body
|
|
expect(body).not.toHaveProperty('payload')
|
|
expect(body.host).toBe('db.example.com')
|
|
expect(body.source_type).toBe('MySQL')
|
|
})
|
|
})
|
|
|
|
describe('externalDataSourcesPreview', () => {
|
|
it('posts a nested payload alongside source_type, resource_name and limit', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue({ rows: [], row_count: 0, columns: [], error: null })
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesPreview()
|
|
|
|
await tool.handler(context, {
|
|
source_type: 'Custom',
|
|
payload: { manifest_json: '{"client":{}}', auth_api_key: 'sk_test' },
|
|
resource_name: 'users',
|
|
limit: 5,
|
|
})
|
|
|
|
expect(requestMock).toHaveBeenCalledWith({
|
|
method: 'POST',
|
|
path: '/v1/projects/42/external_data_sources/preview_resource/',
|
|
body: {
|
|
source_type: 'Custom',
|
|
payload: { manifest_json: '{"client":{}}', auth_api_key: 'sk_test' },
|
|
resource_name: 'users',
|
|
limit: 5,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('keeps credentials nested inside payload, not at the top level', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue({})
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesPreview()
|
|
|
|
await tool.handler(context, {
|
|
source_type: 'Custom',
|
|
payload: { manifest_json: '{}', auth_token: 'secret' },
|
|
resource_name: 'users',
|
|
})
|
|
|
|
const body = requestMock.mock.calls[0]![0].body
|
|
expect(body).not.toHaveProperty('auth_token')
|
|
expect(body.payload.auth_token).toBe('secret')
|
|
})
|
|
})
|
|
|
|
describe('externalDataSourcesJobs', () => {
|
|
it('passes all query params as repeated keys when provided', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue([])
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesJobs()
|
|
|
|
await tool.handler(context, {
|
|
id: 'source-123',
|
|
after: '2025-01-01T00:00:00Z',
|
|
before: '2025-12-31T23:59:59Z',
|
|
schemas: ['users', 'orders'],
|
|
})
|
|
|
|
const path = requestMock.mock.calls[0]![0].path as string
|
|
expect(path).toContain('/v1/projects/42/external_data_sources/source-123/jobs/')
|
|
expect(path).toContain('after=2025-01-01T00%3A00%3A00Z')
|
|
expect(path).toContain('before=2025-12-31T23%3A59%3A59Z')
|
|
expect(path).toContain('schemas=users')
|
|
expect(path).toContain('schemas=orders')
|
|
expect(requestMock.mock.calls[0]![0]).not.toHaveProperty('query')
|
|
})
|
|
|
|
it('omits query string when no optional params provided', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue([])
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesJobs()
|
|
|
|
await tool.handler(context, { id: 'source-456' })
|
|
|
|
expect(requestMock).toHaveBeenCalledWith({
|
|
method: 'GET',
|
|
path: '/v1/projects/42/external_data_sources/source-456/jobs/',
|
|
})
|
|
})
|
|
|
|
it('includes only provided optional params in query string', async () => {
|
|
const requestMock = vi.fn().mockResolvedValue([])
|
|
const context = createMockContext(requestMock)
|
|
const tool = externalDataSourcesJobs()
|
|
|
|
await tool.handler(context, {
|
|
id: 'source-789',
|
|
after: '2025-06-01T00:00:00Z',
|
|
})
|
|
|
|
const path = requestMock.mock.calls[0]![0].path as string
|
|
expect(path).toContain('after=2025-06-01T00%3A00%3A00Z')
|
|
expect(path).not.toContain('before=')
|
|
expect(path).not.toContain('schemas=')
|
|
})
|
|
})
|