Files
openmaic/OpenMAIC/tests/config/docker-deployment-role.test.ts
2026-08-16 14:58:47 +08:00

77 lines
2.7 KiB
TypeScript

import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { load } from 'js-yaml';
import { describe, expect, it } from 'vitest';
interface ComposeBuild {
context?: string;
args?: Record<string, string>;
}
interface ComposeService {
build?: string | ComposeBuild;
environment?: Record<string, string> | string[];
profiles?: string[];
}
interface ComposeConfig {
services: Record<string, ComposeService>;
}
const dockerfile = readFileSync(resolve(process.cwd(), 'Dockerfile'), 'utf8');
const composeSource = readFileSync(resolve(process.cwd(), 'docker-compose.yml'), 'utf8');
const compose = load(composeSource) as ComposeConfig;
function appService(role: 'learner' | 'ops' | 'server') {
const service = compose.services[role];
expect(service, `Compose must define the ${role} service`).toBeDefined();
expect(typeof service.build).toBe('object');
expect(Array.isArray(service.environment)).toBe(false);
return service as ComposeService & {
build: ComposeBuild;
environment: Record<string, string>;
};
}
describe('Docker deployment-role contract', () => {
it('makes the public deployment role available while Next.js builds the browser bundle', () => {
const builder = dockerfile.match(
/FROM base AS builder([\s\S]*?)FROM node:22-alpine AS runner/,
)?.[1];
expect(builder).toBeDefined();
expect(builder).toContain('ARG NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE=learner');
expect(builder).toContain(
'ENV NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE=$NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE',
);
expect(builder!.indexOf('ENV NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE')).toBeLessThan(
builder!.indexOf('RUN pnpm build'),
);
});
it('uses the build role as the fail-closed runtime default in the image', () => {
const runner = dockerfile.match(/FROM node:22-alpine AS runner([\s\S]*)/)?.[1];
expect(runner).toBeDefined();
expect(runner).toContain('ARG NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE=learner');
expect(runner).toContain('ENV OPENMAIC_DEPLOYMENT_ROLE=$NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE');
});
it.each(['learner', 'ops', 'server'] as const)(
'keeps the %s image build and runtime roles identical',
(role) => {
const service = appService(role);
expect(service.build.context).toBe('.');
expect(service.build.args?.NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE).toBe(role);
expect(service.environment.OPENMAIC_DEPLOYMENT_ROLE).toBe(role);
expect(service.environment.NEXT_PUBLIC_OPENMAIC_DEPLOYMENT_ROLE).toBe(role);
},
);
it('does not compile the server-only publishing credential into any role image', () => {
expect(composeSource).not.toContain('NEXT_PUBLIC_COURSEWARE_PUBLISH_TOKEN');
});
});