test(runtime): verify live same-origin canary
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { mkdtemp, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildProductionControlPlane,
|
||||
type ProductionControlPlaneOptions,
|
||||
} from './production-control-plane.js';
|
||||
import { GATEWAY_AUTH_HEADER } from './runtime-config.js';
|
||||
|
||||
const cleanup: Array<() => Promise<void>> = [];
|
||||
const gatewayToken = 'production-gateway-token-with-at-least-32-bytes';
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(cleanup.splice(0).map((fn) => fn()));
|
||||
});
|
||||
|
||||
async function fixtureOptions(): Promise<ProductionControlPlaneOptions> {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'production-control-plane-'));
|
||||
cleanup.push(() => rm(directory, { recursive: true, force: true }));
|
||||
return {
|
||||
databasePath: join(directory, 'db.sqlite'),
|
||||
gatewayToken,
|
||||
store: {
|
||||
set: async () => '',
|
||||
get: async () => undefined,
|
||||
delete: async () => false,
|
||||
},
|
||||
upstream: {
|
||||
get: async () => ({ status: 200, headers: {}, body: '' }),
|
||||
request: async () => ({ status: 200, headers: {}, body: '' }),
|
||||
postNetworkRegisterAuto: async () => ({ status: 200 }),
|
||||
},
|
||||
keychainMetadataCheck: async () => true,
|
||||
};
|
||||
}
|
||||
|
||||
describe('production control-plane authentication composition', () => {
|
||||
it('keeps the event route fail-closed when trusted-gateway auth is not configured', async () => {
|
||||
const options = await fixtureOptions();
|
||||
const app = buildProductionControlPlane({
|
||||
databasePath: options.databasePath,
|
||||
store: options.store!,
|
||||
upstream: options.upstream!,
|
||||
keychainMetadataCheck: options.keychainMetadataCheck!,
|
||||
});
|
||||
cleanup.push(() => app.close());
|
||||
|
||||
const response = await app.inject({ method: 'GET', url: '/api/v1/events' });
|
||||
expect(response.statusCode).toBe(401);
|
||||
expect(response.json()).toMatchObject({ code: 'UNAUTHORIZED' });
|
||||
});
|
||||
|
||||
it('opens SSE only after mandatory trusted-gateway authentication succeeds', async () => {
|
||||
const app = buildProductionControlPlane(await fixtureOptions());
|
||||
cleanup.push(() => app.close());
|
||||
|
||||
const direct = await app.inject({ method: 'GET', url: '/api/v1/events' });
|
||||
expect(direct.statusCode).toBe(401);
|
||||
expect(direct.json()).toMatchObject({ code: 'GATEWAY_AUTH_REQUIRED' });
|
||||
|
||||
await app.listen({ host: '127.0.0.1', port: 0 });
|
||||
const address = app.server.address();
|
||||
if (!address || typeof address === 'string') throw new Error('Expected TCP server address');
|
||||
const response = await fetch(`http://127.0.0.1:${address.port}/api/v1/events`, {
|
||||
headers: { [GATEWAY_AUTH_HEADER]: gatewayToken },
|
||||
});
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('content-type')).toMatch(/^text\/event-stream/);
|
||||
await response.body?.cancel();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user