mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-30 08:17:06 +02:00
Co-authored-by: Svetoslav Dekov <svetoslav.dekov@n8n.io> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
86 lines
2.7 KiB
TypeScript
86 lines
2.7 KiB
TypeScript
import { nanoid } from 'nanoid';
|
|
|
|
import { test, expect } from '../../../fixtures/base';
|
|
|
|
test.describe(
|
|
'API Discovery',
|
|
{
|
|
annotation: [{ type: 'owner', description: 'Catalysts' }],
|
|
},
|
|
() => {
|
|
test('owner sees all available operations', async ({ api }) => {
|
|
const discovery = await api.publicApi.getDiscovery();
|
|
|
|
expect(discovery.scopes.length).toBeGreaterThan(0);
|
|
expect(discovery.resources).toBeDefined();
|
|
expect(discovery.specUrl).toBe('/api/v1/openapi.yml');
|
|
|
|
// Owner's default scopes include workflow endpoints
|
|
expect(discovery.resources.workflow).toBeDefined();
|
|
expect(discovery.resources.workflow.endpoints.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('every endpoint has method, path, and operationId', async ({ api }) => {
|
|
const discovery = await api.publicApi.getDiscovery();
|
|
|
|
for (const [, resource] of Object.entries(discovery.resources)) {
|
|
for (const endpoint of resource.endpoints) {
|
|
expect(endpoint.method).toMatch(/^(GET|POST|PUT|DELETE|PATCH)$/);
|
|
expect(endpoint.path).toMatch(/^\/api\/v1\//);
|
|
expect(endpoint.operationId).toBeTruthy();
|
|
}
|
|
}
|
|
});
|
|
|
|
test('member can discover API capabilities', async ({ api }) => {
|
|
const member = await api.publicApi.createUser({
|
|
email: `member-discover-${nanoid()}@test.com`,
|
|
role: 'global:member',
|
|
});
|
|
|
|
const memberApi = await api.createApiForUser(member);
|
|
await memberApi.publicApi.createApiKey(`member-key-${nanoid()}`, [
|
|
'workflow:create',
|
|
'workflow:read',
|
|
'workflow:list',
|
|
'credential:create',
|
|
]);
|
|
|
|
const discovery = await memberApi.publicApi.getDiscovery();
|
|
|
|
expect(discovery.scopes.length).toBeGreaterThan(0);
|
|
expect(discovery.resources).toBeDefined();
|
|
expect(discovery.specUrl).toBe('/api/v1/openapi.yml');
|
|
|
|
// Member should see workflow endpoints (included in default API key scopes)
|
|
expect(discovery.resources.workflow).toBeDefined();
|
|
expect(
|
|
discovery.resources.workflow.endpoints.some((e) => e.operationId === 'getWorkflows'),
|
|
).toBe(true);
|
|
});
|
|
|
|
test('discovery includes known endpoints with correct shape', async ({ api }) => {
|
|
const discovery = await api.publicApi.getDiscovery();
|
|
|
|
// Verify well-known endpoints are correctly described
|
|
const workflowList = discovery.resources.workflow?.endpoints.find(
|
|
(e) => e.operationId === 'getWorkflows',
|
|
);
|
|
expect(workflowList).toEqual({
|
|
method: 'GET',
|
|
path: '/api/v1/workflows',
|
|
operationId: 'getWorkflows',
|
|
});
|
|
|
|
const createTag = discovery.resources.tags?.endpoints.find(
|
|
(e) => e.operationId === 'createTag',
|
|
);
|
|
expect(createTag).toEqual({
|
|
method: 'POST',
|
|
path: '/api/v1/tags',
|
|
operationId: 'createTag',
|
|
});
|
|
});
|
|
},
|
|
);
|