Run API tests serially and support external targets without stale coverage logs
This commit is contained in:
1 parent
e18769e8cf
commit
4e47e350bf
3 files changed
+25
-6
No files matched your search
@@ -20,8 +20,10 @@ export default defineConfig({
|
|||||||
testDir: './tests/api',
|
testDir: './tests/api',
|
||||||
testMatch: '**/*.spec.ts',
|
testMatch: '**/*.spec.ts',
|
||||||
outputDir: 'test-results/api',
|
outputDir: 'test-results/api',
|
||||||
|
// Specs share one seeded database and a few mutable globals (the shared users,
|
||||||
|
// the global 2FA requirement toggled by admin.spec.ts), so they must run serially.
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
workers: process.env.API_WORKERS ? Number(process.env.API_WORKERS) : 1,
|
workers: 1,
|
||||||
// Specs are deterministic and share coverage state; retries would only hide flakes.
|
// Specs are deterministic and share coverage state; retries would only hide flakes.
|
||||||
retries: 0,
|
retries: 0,
|
||||||
forbidOnly: !!process.env.CI,
|
forbidOnly: !!process.env.CI,
|
||||||
|
|||||||
+18
-1
@@ -20,6 +20,9 @@ const COMPOSE_FILE = 'docker-compose.test.yml';
|
|||||||
const PROJECT_NAME = process.env.COMPOSE_PROJECT_NAME || 'umami-api-test';
|
const PROJECT_NAME = process.env.COMPOSE_PROJECT_NAME || 'umami-api-test';
|
||||||
const PORT = process.env.UMAMI_TEST_PORT || '3100';
|
const PORT = process.env.UMAMI_TEST_PORT || '3100';
|
||||||
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || `http://localhost:${PORT}`;
|
const BASE_URL = process.env.PLAYWRIGHT_BASE_URL || `http://localhost:${PORT}`;
|
||||||
|
// PLAYWRIGHT_BASE_URL selects an already-running server: no Compose stack is
|
||||||
|
// built, started, or torn down for it.
|
||||||
|
const EXTERNAL_TARGET = !!process.env.PLAYWRIGHT_BASE_URL;
|
||||||
const HEARTBEAT_TIMEOUT_MS = 180_000;
|
const HEARTBEAT_TIMEOUT_MS = 180_000;
|
||||||
// Mirrors RUNTIME_DIR in tests/api/paths.ts (one state dir per target host).
|
// Mirrors RUNTIME_DIR in tests/api/paths.ts (one state dir per target host).
|
||||||
const LOG_FILE = path.join(
|
const LOG_FILE = path.join(
|
||||||
@@ -46,6 +49,8 @@ Environment:
|
|||||||
UMAMI_TEST_PORT Host port for the app (default 3100)
|
UMAMI_TEST_PORT Host port for the app (default 3100)
|
||||||
UMAMI_TEST_IMAGE Image tag to build/run (default umami-test:local)
|
UMAMI_TEST_IMAGE Image tag to build/run (default umami-test:local)
|
||||||
COMPOSE_PROJECT_NAME Compose project name (default umami-api-test)
|
COMPOSE_PROJECT_NAME Compose project name (default umami-api-test)
|
||||||
|
PLAYWRIGHT_BASE_URL Test an already-running server instead of starting the
|
||||||
|
Compose stack (pass --clickhouse if it stores analytics there)
|
||||||
API_SKIP_SEED=1 Reuse the previous seed when re-running against a kept stack
|
API_SKIP_SEED=1 Reuse the previous seed when re-running against a kept stack
|
||||||
(each run seeds additively otherwise)
|
(each run seeds additively otherwise)
|
||||||
`;
|
`;
|
||||||
@@ -193,6 +198,14 @@ async function main() {
|
|||||||
run('docker', composeArgs(['postgres', 'clickhouse'], ['down', '-v', '--remove-orphans']));
|
run('docker', composeArgs(['postgres', 'clickhouse'], ['down', '-v', '--remove-orphans']));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (EXTERNAL_TARGET && (options.upOnly || options.down)) {
|
||||||
|
console.error(
|
||||||
|
'PLAYWRIGHT_BASE_URL selects an already-running server; --up-only and --down manage ' +
|
||||||
|
'the local Compose stack and cannot be combined with it.',
|
||||||
|
);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.down) {
|
if (options.down) {
|
||||||
teardown();
|
teardown();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -226,6 +239,9 @@ async function main() {
|
|||||||
process.on('SIGTERM', onSignal);
|
process.on('SIGTERM', onSignal);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (EXTERNAL_TARGET) {
|
||||||
|
console.log(`Using the already-running server at ${BASE_URL} (PLAYWRIGHT_BASE_URL is set).`);
|
||||||
|
} else {
|
||||||
console.log(`Starting test stack (profile: ${profile}, port: ${PORT})...`);
|
console.log(`Starting test stack (profile: ${profile}, port: ${PORT})...`);
|
||||||
|
|
||||||
const upArgs = ['up', '-d', '--wait', '--wait-timeout', '300'];
|
const upArgs = ['up', '-d', '--wait', '--wait-timeout', '300'];
|
||||||
@@ -241,6 +257,7 @@ async function main() {
|
|||||||
dumpLogs();
|
dumpLogs();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await waitForHeartbeat(BASE_URL, HEARTBEAT_TIMEOUT_MS);
|
await waitForHeartbeat(BASE_URL, HEARTBEAT_TIMEOUT_MS);
|
||||||
|
|
||||||
@@ -272,7 +289,7 @@ async function main() {
|
|||||||
process.env.API_COVERAGE ?? (options.grep || options.coverageReport ? 'report' : 'enforce'),
|
process.env.API_COVERAGE ?? (options.grep || options.coverageReport ? 'report' : 'enforce'),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0 && started) {
|
||||||
dumpLogs();
|
dumpLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ export default async function globalSetup(config: FullConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// API_SKIP_SEED=1 reuses the seed from a previous run against a kept stack
|
// API_SKIP_SEED=1 reuses the seed from a previous run against a kept stack
|
||||||
// (fast iteration on a single spec); coverage logs are then accumulated too.
|
// (fast iteration on a single spec).
|
||||||
const skipSeed = !!process.env.API_SKIP_SEED && existsSync(SEED_FILE);
|
const skipSeed = !!process.env.API_SKIP_SEED && existsSync(SEED_FILE);
|
||||||
|
|
||||||
if (!skipSeed) {
|
// Coverage must reflect this run only: the per-process logs are append-only and
|
||||||
|
// the reporter merges every file in the directory, so stale logs from an earlier
|
||||||
|
// run would let removed endpoint calls still count as covered.
|
||||||
rmSync(COVERAGE_DIR, { recursive: true, force: true });
|
rmSync(COVERAGE_DIR, { recursive: true, force: true });
|
||||||
}
|
|
||||||
|
|
||||||
mkdirSync(COVERAGE_DIR, { recursive: true });
|
mkdirSync(COVERAGE_DIR, { recursive: true });
|
||||||
|
|
||||||
console.log(`Waiting for ${baseURL}/api/heartbeat ...`);
|
console.log(`Waiting for ${baseURL}/api/heartbeat ...`);
|
||||||
|
|||||||
Reference in new issue
Block a user