krona-staff/tests/e2e/40-compose-pilot.e2e.ts
grok 0453168d70 test(e2e): двойные ID «владелец/мой» в названиях сценариев и known-defects
Зачем: аудит владельца (docs/audit/2026-09-26/AUDIT.md) стал основным
реестром дефектов; без сопоставления исполнители и приёмка говорят о
разных ID для одной ошибки (TASK-GROK-2 §2).

Что: теги переименованы по таблице владельца ([K3/N2], [K13/SYNC-A],
[S5/SYNC-C], [D6/OWN-01], [K17/OWN-10] и т. д.), ключи known-defects.json —
так же; N9–N12, F7, F10 остаются одиночными, как в таблице. Сверх таблицы,
по тексту AUDIT.md: K1/SYNC-I (анонимный доступ без токенов — это K1 на
/sync), K1/PILOT-1 (подстановки без :? в пилотном compose прямо названы в
K1), K2/PILOT-2 (NODE_ENV=development в пилоте — суть K2). SYNC-C0, SYNC-K,
OWN-03..06/08/09, PILOT-3 ID владельца не имеют. run-e2e принимает «/» в
теге. Прогон: 38 red / 9 green, храповик OK — те же сценарии, новые имена.
2026-09-26 15:21:22 +07:00

56 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Статическая проверка deploy/pilot/compose.yaml (аналог tests/unit/deployment.test.ts
// для корневого compose.yaml). Лежит в tests/e2e, а не в tests/unit: у исполнителя
// теста нет права менять tests/unit, а красные пункты (открытые дефекты deploy/)
// должны идти через known-defects.json, не ломая `npm run check`.
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import { parse } from 'yaml';
const composePath = path.resolve(import.meta.dirname, '../../deploy/pilot/compose.yaml');
const raw = readFileSync(composePath, 'utf8');
const compose = parse(raw) as {
services: Record<string, { environment?: Record<string, string | number> | string[] }>;
};
function environmentOf(service: string): Record<string, string> {
const env = compose.services[service]?.environment ?? {};
if (Array.isArray(env)) {
return Object.fromEntries(
env.map((entry) => {
const index = entry.indexOf('=');
return index < 0 ? [entry, ''] : [entry.slice(0, index), entry.slice(index + 1)];
}),
);
}
return Object.fromEntries(Object.entries(env).map(([key, value]) => [key, String(value)]));
}
describe('deploy/pilot/compose.yaml', () => {
it('[K1/PILOT-1] все подставляемые переменные обязательны: ${VAR:?сообщение}', () => {
const interpolations = [...raw.matchAll(/\$\{([^}]*)\}/g)].map((match) => match[1]!);
expect(interpolations.length).toBeGreaterThan(0);
const optional = interpolations.filter((inner) => !/^[A-Z0-9_]+:\?.+$/.test(inner));
expect(optional, 'переменные без :? — пустое значение молча попадёт в контейнер').toEqual([]);
});
it('[K2/PILOT-2] ни один сервис не запускается с NODE_ENV=development', () => {
const offenders = Object.keys(compose.services).filter(
(service) => environmentOf(service).NODE_ENV === 'development',
);
expect(offenders).toEqual([]);
});
it('[PILOT-3] у сервиса app нет учётных данных мигратора', () => {
const env = environmentOf('app');
const keys = Object.keys(env).filter((key) =>
['DATABASE_MIGRATION_URL', 'POSTGRES_MIGRATION_PASSWORD', 'POSTGRES_MIGRATION_USER'].includes(
key,
),
);
const values = Object.entries(env)
.filter(([, value]) => /POSTGRES_MIGRATION_PASSWORD|krona_migrator/.test(value))
.map(([key]) => key);
expect({ keys, values }).toEqual({ keys: [], values: [] });
});
});