import * as Scope from '../../../Scope.js' import * as TestApp from '../../../../test/App.js' import * as Scopes from './scopes.js' test('publishes a generator-ready OpenAPI contract', async () => { const spec = await (await TestApp.create().request('/openapi.json')).json() const operation = spec.paths['/v1/scopes'].get expect(spec.components.schemas.ScopeList.properties.data.examples).toEqual([[]]) expect({ components: ['Scope', 'ScopeList'].filter((name) => spec.components.schemas[name]), errors: { 400: operation.responses[400].content['application/json'].schema, 403: operation.responses[403].content['application/json'].schema, }, operationId: operation.operationId, response: operation.responses[200].content['application/json'].schema, }).toMatchInlineSnapshot(` { "components": [ "Scope", "ScopeList", ], "errors": { "400": { "$ref": "#/components/schemas/ApiKeyMalformedError", }, "403": { "$ref": "#/components/schemas/ForbiddenError", }, }, "operationId": "getScopes", "response": { "$ref": "#/components/schemas/ScopeList", }, } `) }) describe('scopes', () => { test('rejects malformed API key credentials', async () => { const response = await TestApp.create().request('/v1/scopes', { headers: { authorization: 'Basic wrong' }, }) expect(response.status).toBe(400) expect(await response.json()).toMatchObject({ error: { code: 'api_key_malformed' } }) }) test('lists the scope catalog without authentication (public catalog)', async () => { const app = TestApp.create() const response = await app.request('/v1/scopes') const body = await TestApp.json(response, Scopes.schema.getScopes.Response) expect(response.status).toMatchInlineSnapshot(`200`) // Static public data must be cacheable (`stable`); without it every // request pays the full auth/rate-limit floor. expect(response.headers.get('cache-control')).toMatchInlineSnapshot( `"private, max-age=300, stale-while-revalidate=3600"`, ) expect(body.data).toEqual( [...Scope.catalog, Scope.zoneEntry, Scope.zoneWriteEntry].map((entry) => ({ ...entry })), ) expect(body.data.map((entry) => entry.scope)).toMatchInlineSnapshot(` [ "data:read", "funding:read", "funding:write", "indexer:query", "management:read", "management:write", "mpp:write", "rpc-relay:read", "rpc-relay:sponsor", "webhooks:read", "webhooks:write", "zone::read", "zone::write", ] `) }) test('includes host scopes configured on the app', async () => { const custom = { description: 'Read project accounts.', scope: 'accounts:read', selfServe: true, } as const satisfies Scope.Entry const app = TestApp.create({ scopes: [custom] }) const response = await app.request('/v1/scopes') const body = await TestApp.json(response, Scopes.schema.getScopes.Response) expect(response.status).toBe(200) expect(body.data).toEqual([...Scope.catalog, Scope.zoneEntry, Scope.zoneWriteEntry, custom]) }) })