Files
Codewalkers/apps/server/preview/config-reader.test.ts
Lukas May 714262fb83 feat: Add seed command support to preview deployments
Run project-specific initialization commands (DB migrations, fixture
loading, etc.) automatically after containers are healthy, before the
preview is marked ready. Configured via per-service `seed` arrays in
.cw-preview.yml.
2026-03-05 12:39:02 +01:00

180 lines
4.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { parseCwPreviewConfig } from './config-reader.js';
describe('parseCwPreviewConfig', () => {
it('parses minimal single-service config', () => {
const raw = `
version: 1
services:
app:
build: "."
port: 3000
`;
const config = parseCwPreviewConfig(raw);
expect(config.version).toBe(1);
expect(Object.keys(config.services)).toEqual(['app']);
expect(config.services.app.port).toBe(3000);
expect(config.services.app.build).toBe('.');
});
it('parses multi-service config with routes and healthchecks', () => {
const raw = `
version: 1
services:
frontend:
build:
context: "."
dockerfile: apps/web/Dockerfile
port: 3000
route: /
healthcheck:
path: /
interval: 5s
retries: 10
env:
VITE_API_URL: /api
backend:
build:
context: "."
dockerfile: packages/api/Dockerfile
port: 8080
route: /api
healthcheck:
path: /health
env:
DATABASE_URL: "postgres://db:5432/app"
db:
image: postgres:16-alpine
port: 5432
internal: true
env:
POSTGRES_PASSWORD: preview
`;
const config = parseCwPreviewConfig(raw);
expect(Object.keys(config.services)).toHaveLength(3);
// Frontend
expect(config.services.frontend.port).toBe(3000);
expect(config.services.frontend.route).toBe('/');
expect(config.services.frontend.healthcheck?.path).toBe('/');
expect(config.services.frontend.healthcheck?.retries).toBe(10);
expect(config.services.frontend.env?.VITE_API_URL).toBe('/api');
expect(config.services.frontend.build).toEqual({
context: '.',
dockerfile: 'apps/web/Dockerfile',
});
// Backend
expect(config.services.backend.port).toBe(8080);
expect(config.services.backend.route).toBe('/api');
// DB (internal)
expect(config.services.db.internal).toBe(true);
expect(config.services.db.image).toBe('postgres:16-alpine');
});
it('rejects config without services', () => {
expect(() => parseCwPreviewConfig('version: 1\n')).toThrow('missing "services"');
});
it('rejects service without port (unless internal)', () => {
const raw = `
version: 1
services:
app:
build: "."
`;
expect(() => parseCwPreviewConfig(raw)).toThrow('must specify a "port"');
});
it('allows internal service without port', () => {
const raw = `
version: 1
services:
redis:
image: redis:7
internal: true
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.redis.internal).toBe(true);
expect(config.services.redis.port).toBe(0);
});
it('normalizes string build to string', () => {
const raw = `
version: 1
services:
app:
build: "./app"
port: 3000
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.app.build).toBe('./app');
});
it('parses dev section with image, command, and workdir', () => {
const raw = `
version: 1
services:
frontend:
build: "."
port: 3000
route: /
dev:
image: node:20-alpine
command: npm run dev -- --host 0.0.0.0
workdir: /app
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.frontend.dev).toBeDefined();
expect(config.services.frontend.dev!.image).toBe('node:20-alpine');
expect(config.services.frontend.dev!.command).toBe('npm run dev -- --host 0.0.0.0');
expect(config.services.frontend.dev!.workdir).toBe('/app');
});
it('parses seed array on a service', () => {
const raw = `
version: 1
services:
app:
build: "."
port: 3000
seed:
- npm run db:migrate
- npm run db:seed
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.app.seed).toEqual(['npm run db:migrate', 'npm run db:seed']);
});
it('omits seed when not specified', () => {
const raw = `
version: 1
services:
app:
build: "."
port: 3000
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.app.seed).toBeUndefined();
});
it('parses dev section with only image', () => {
const raw = `
version: 1
services:
app:
build: "."
port: 3000
dev:
image: node:20-alpine
`;
const config = parseCwPreviewConfig(raw);
expect(config.services.app.dev).toBeDefined();
expect(config.services.app.dev!.image).toBe('node:20-alpine');
expect(config.services.app.dev!.command).toBeUndefined();
expect(config.services.app.dev!.workdir).toBeUndefined();
});
});