test(e2e): проверка deploy/pilot/compose.yaml (PILOT-1..3)
Зачем: корневой compose.yaml уже проверяется tests/unit/deployment.test.ts,
а pilot-файл, который реально разворачивается у владельца, — нет. В нём
подстановки вида ${STANDALONE_MANAGER_TOKEN} без :? молча дают пустое
значение, а при пустых токенах standalone выдаёт права анониму (F2);
NODE_ENV=development в pilot отключает production-проверки config.ts.
- PILOT-1: каждая подстановка должна быть ${VAR:?сообщение};
- PILOT-2: ни один сервис не с NODE_ENV=development;
- PILOT-3: у app нет учётных данных мигратора (сейчас зелёный).
Файл в tests/e2e как *.e2e.ts: tests/unit вне разрешённых путей, а
красные пункты (дефекты deploy/, которые правят исполнители) идут через
known-defects.json и не ломают `npm run check`. Сейчас PILOT-1 и PILOT-2
красные: 14 подстановок без :?, app с NODE_ENV=development.
This commit is contained in:
parent
f59be3adf9
commit
a19c06b574
2 changed files with 59 additions and 1 deletions
56
tests/e2e/40-compose-pilot.e2e.ts
Normal file
56
tests/e2e/40-compose-pilot.e2e.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Статическая проверка 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('[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('[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: [] });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
"SYNC-H": "регистрация устройства по cookie без проверки Origin",
|
"SYNC-H": "регистрация устройства по cookie без проверки Origin",
|
||||||
"SYNC-I": "без токенов анонимно регистрируется устройство",
|
"SYNC-I": "без токенов анонимно регистрируется устройство",
|
||||||
"SYNC-J": "sync принимает отметки вне окна времени",
|
"SYNC-J": "sync принимает отметки вне окна времени",
|
||||||
"SYNC-K": "ack принимает курсор больше выданного сервером"
|
"SYNC-K": "ack принимает курсор больше выданного сервером",
|
||||||
|
"PILOT-1": "deploy/pilot/compose.yaml: 14 подстановок без :? (пустые токены/пароли молча проходят)",
|
||||||
|
"PILOT-2": "deploy/pilot/compose.yaml: app с NODE_ENV=development"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue