feat(quick-260909-dgj): Migrationsverbindung von Laufzeitverbindung trennen (Task 2/4)
- apps/api/scripts/migrate-and-start.sh: TESSERA_MIGRATE_DATABASE_URL fuer den Migrationsschritt, DATABASE_URL bleibt unveraendert fuer den Laufzeitschritt; leer/nicht gesetzt = bisheriges Verhalten - Dockerfile kopiert scripts/ und ruft das Skript als CMD auf; exec statt &&-Verkettung, damit Signale den Node-Prozess erreichen - docker-compose.yml/.prod.yml reichen TESSERA_MIGRATE_DATABASE_URL durch (leerer Vorgabewert); docker-compose.dev.yml/.ci.yml unveraendert - .env.example erklaert beide Variablen mit Platzhaltern, Umstellung bleibt auskommentiert - 5 Tests in start-script.spec.ts (rot vor dem Skript, jetzt gruen) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { join } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
/**
|
||||
* Prueft apps/api/scripts/migrate-and-start.sh ausschliesslich ueber die
|
||||
* Ausgabebetriebsart --print-plan — es wird weder Prisma noch Node
|
||||
* aufgerufen, keine Datenbankverbindung aufgebaut (Task 2, WINDOWS #18).
|
||||
*/
|
||||
|
||||
const SCRIPT_PATH = join(__dirname, '../../scripts/migrate-and-start.sh');
|
||||
const SECRET_MARKER = 'tessera-test-secret-marker-9f3c1e';
|
||||
|
||||
function runScript(env: Record<string, string>): { stdout: string; status: number } {
|
||||
try {
|
||||
const stdout = execFileSync('sh', [SCRIPT_PATH, '--print-plan'], {
|
||||
env,
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
return { stdout, status: 0 };
|
||||
} catch (err: any) {
|
||||
return { stdout: (err.stdout ?? '').toString(), status: err.status ?? 1 };
|
||||
}
|
||||
}
|
||||
|
||||
describe('migrate-and-start.sh --print-plan', () => {
|
||||
it('Test 1 (Rueckwaertsvertraeglichkeit): ohne TESSERA_MIGRATE_DATABASE_URL melden beide Schritte DATABASE_URL', () => {
|
||||
const { stdout, status } = runScript({
|
||||
PATH: process.env.PATH ?? '',
|
||||
DATABASE_URL: `postgresql://x:${SECRET_MARKER}@db:5432/tessera`,
|
||||
});
|
||||
|
||||
expect(status).toBe(0);
|
||||
expect(stdout).toContain('DATABASE_URL');
|
||||
// Kein Wert, nur der Variablenname darf erscheinen
|
||||
expect(stdout).not.toContain(SECRET_MARKER);
|
||||
// beide Zeilen (Migration + Laufzeit) nennen DATABASE_URL als Quelle
|
||||
const dbUrlMentions = (stdout.match(/DATABASE_URL/g) ?? []).length;
|
||||
expect(dbUrlMentions).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it('Test 2 (Trennung): mit gesetztem TESSERA_MIGRATE_DATABASE_URL nennt der Migrationsschritt die Migrationsvariable, der Laufzeitschritt bleibt bei DATABASE_URL', () => {
|
||||
const { stdout, status } = runScript({
|
||||
PATH: process.env.PATH ?? '',
|
||||
DATABASE_URL: `postgresql://app:${SECRET_MARKER}@db:5432/tessera`,
|
||||
TESSERA_MIGRATE_DATABASE_URL: `postgresql://owner:${SECRET_MARKER}@db:5432/tessera`,
|
||||
});
|
||||
|
||||
expect(status).toBe(0);
|
||||
expect(stdout).toContain('TESSERA_MIGRATE_DATABASE_URL');
|
||||
expect(stdout).toContain('DATABASE_URL');
|
||||
expect(stdout).not.toContain(SECRET_MARKER);
|
||||
});
|
||||
|
||||
it('Test 3 (leer zaehlt als nicht gesetzt): TESSERA_MIGRATE_DATABASE_URL="" verhaelt sich wie Test 1', () => {
|
||||
const { stdout, status } = runScript({
|
||||
PATH: process.env.PATH ?? '',
|
||||
DATABASE_URL: `postgresql://x:${SECRET_MARKER}@db:5432/tessera`,
|
||||
TESSERA_MIGRATE_DATABASE_URL: '',
|
||||
});
|
||||
|
||||
expect(status).toBe(0);
|
||||
expect(stdout).not.toContain('TESSERA_MIGRATE_DATABASE_URL wird verwendet');
|
||||
const dbUrlMentions = (stdout.match(/DATABASE_URL/g) ?? []).length;
|
||||
expect(dbUrlMentions).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it('Test 4 (kein Geheimnisabfluss): keiner der beiden Verbindungswerte erscheint in der Ausgabe', () => {
|
||||
const { stdout } = runScript({
|
||||
PATH: process.env.PATH ?? '',
|
||||
DATABASE_URL: `postgresql://app:${SECRET_MARKER}@db:5432/tessera`,
|
||||
TESSERA_MIGRATE_DATABASE_URL: `postgresql://owner:${SECRET_MARKER}b@db:5432/tessera`,
|
||||
});
|
||||
|
||||
expect(stdout).not.toContain(SECRET_MARKER);
|
||||
});
|
||||
|
||||
it('Test 5 (fehlende Angabe): ohne DATABASE_URL bricht das Skript mit Ende-Code ungleich 0 ab und nennt den Variablennamen', () => {
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
let status = 0;
|
||||
try {
|
||||
stdout = execFileSync('sh', [SCRIPT_PATH, '--print-plan'], {
|
||||
env: { PATH: process.env.PATH ?? '' },
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
} catch (err: any) {
|
||||
status = err.status ?? 1;
|
||||
stdout = (err.stdout ?? '').toString();
|
||||
stderr = (err.stderr ?? '').toString();
|
||||
}
|
||||
|
||||
expect(status).not.toBe(0);
|
||||
expect(stdout + stderr).toContain('DATABASE_URL');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user