console/integration-tests/tests/apollo-router/apollo-router.test.ts
Copilot aac23596ec
refactor: shared internal postgres package around slonik (#7887)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: n1ru4l <14338007+n1ru4l@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Laurin Quast <laurinquast@googlemail.com>
2026-03-27 10:20:05 +01:00

161 lines
4.6 KiB
TypeScript

import { existsSync, rmSync, writeFileSync } from 'node:fs';
import { createServer } from 'node:http';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { ProjectType } from 'testkit/gql/graphql';
import { initSeed } from 'testkit/seed';
import { getServiceHost } from 'testkit/utils';
import { execa } from '@esm2cjs/execa';
type MaybePromise<T> = T | Promise<T>;
describe('Apollo Router Integration', () => {
const getAvailablePort = () =>
new Promise<number>(resolve => {
const server = createServer();
server.listen(0, () => {
const address = server.address();
if (address && typeof address === 'object') {
const port = address.port;
server.close(() => resolve(port));
} else {
throw new Error('Could not get available port');
}
});
});
function defer(deferFn: () => MaybePromise<void>) {
return {
async [Symbol.asyncDispose]() {
return deferFn();
},
};
}
it('fetches the supergraph and sends usage reports', async () => {
const { createOrg } = await initSeed().createOwner();
const { createProject } = await createOrg();
const { createTargetAccessToken, createCdnAccess, target, waitForOperationsCollected } =
await createProject(ProjectType.Federation);
const writeToken = await createTargetAccessToken({});
// Publish Schema
const publishSchemaResult = await writeToken
.publishSchema({
author: 'Arda',
commit: 'abc123',
sdl: /* GraphQL */ `
type Query {
me: User
}
type User {
id: ID!
name: String!
}
`,
service: 'users',
url: 'https://federation-demo.theguild.workers.dev/users',
})
.then(r => r.expectNoGraphQLErrors());
expect(publishSchemaResult.schemaPublish.__typename).toBe('SchemaPublishSuccess');
const cdnAccessResult = await createCdnAccess();
const usageAddress = await getServiceHost('usage', 8081);
const routerBinPath = join(__dirname, '../../../target/debug/router');
if (!existsSync(routerBinPath)) {
throw new Error(
`Apollo Router binary not found at path: ${routerBinPath}, make sure to build it first with 'cargo build'`,
);
}
const routerPort = await getAvailablePort();
const routerConfigContent = `
supergraph:
listen: 0.0.0.0:${routerPort}
plugins:
hive.usage: {}
`.trim();
const routerConfigPath = join(tmpdir(), `apollo-router-config-${Date.now()}.yaml`);
writeFileSync(routerConfigPath, routerConfigContent, 'utf-8');
const cdnEndpoint = await getServiceHost('server', 8082).then(
v => `http://${v}/artifacts/v1/${target.id}`,
);
const routerProc = execa(routerBinPath, ['--dev', '--config', routerConfigPath], {
all: true,
env: {
HIVE_CDN_ENDPOINT: cdnEndpoint,
HIVE_CDN_KEY: cdnAccessResult.secretAccessToken,
HIVE_ENDPOINT: `http://${usageAddress}`,
HIVE_TOKEN: writeToken.secret,
},
});
let log = '';
await new Promise((resolve, reject) => {
routerProc.catch(err => {
if (!err.isCanceled) {
reject(err);
}
});
const routerProcOut = routerProc.all;
if (!routerProcOut) {
return reject(new Error('No stdout from Apollo Router process'));
}
routerProcOut.on('data', data => {
log += data.toString();
if (log.includes('GraphQL endpoint exposed at')) {
resolve(true);
}
process.stdout.write(log);
});
});
await using _ = defer(() => {
rmSync(routerConfigPath);
routerProc.cancel();
});
const url = `http://localhost:${routerPort}/`;
async function sendOperation(i: number) {
const response = await fetch(url, {
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
query: `
query Query${i} {
me {
id
name
}
}
`,
}),
});
expect(response.status).toBe(200);
const result = await response.json();
expect(result).toEqual({
data: {
me: {
id: '1',
name: 'Ada Lovelace',
},
},
});
}
const cnt = 1000;
const jobs = [];
for (let i = 0; i < cnt; i++) {
if (i % 100 === 0) {
await new Promise(res => setTimeout(res, 500));
}
jobs.push(sendOperation(i));
}
await Promise.all(jobs);
await waitForOperationsCollected(cnt);
});
});