mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
test: add the non-blocking 'marathon' E2E tier
Full start-to-finish flows move out of the blocking lanes. E2E_TIERS and
E2ETier gain 'marathon'; describeE2ETier('marathon') is enabled only when
EVALS_TIER=marathon, so the gate/PR and periodic lanes (and the gate census)
never run those cases. The PR profile accepts marathon ids as scheduled
elsewhere and defers them with their own reason, even on full fallback.
This commit is contained in:
1 parent
8c30ee9595
commit
a41cdb7d7d
7 files changed
+43
-14
No files matched your search
@@ -24,11 +24,12 @@
|
||||
|
||||
import { describe } from 'bun:test';
|
||||
|
||||
export type E2ETier = 'gate' | 'periodic';
|
||||
export type E2ETier = 'gate' | 'periodic' | 'marathon';
|
||||
|
||||
/**
|
||||
* True when this process should run whole-file-gated paid tests of `tier`:
|
||||
* EVALS=1 AND EVALS_TIER exactly equals the tier.
|
||||
* EVALS=1 AND EVALS_TIER exactly equals the tier. 'marathon' cases (full
|
||||
* end-to-end flows) therefore never run in the gate/PR or periodic lanes.
|
||||
*
|
||||
* Deliberate consequence: EVALS=1 with EVALS_TIER unset is false for BOTH
|
||||
* tiers. Tierless runs (`test:evals` / `eval:bg` / `eval:bg:all`) skip every
|
||||
|
||||
@@ -64,6 +64,19 @@ describe('e2e-gate: env matrix (read at call time)', () => {
|
||||
expect(describeE2ETier('periodic')).toBe(describe.skip);
|
||||
});
|
||||
|
||||
test('marathon runs only in its own lane; gate and periodic lanes skip it', () => {
|
||||
process.env.EVALS = '1';
|
||||
for (const lane of ['gate', 'periodic']) {
|
||||
process.env.EVALS_TIER = lane;
|
||||
expect(e2eTierEnabled('marathon')).toBe(false);
|
||||
expect(describeE2ETier('marathon')).toBe(describe.skip);
|
||||
}
|
||||
process.env.EVALS_TIER = 'marathon';
|
||||
expect(describeE2ETier('marathon')).toBe(describe);
|
||||
expect(describeE2ETier('gate')).toBe(describe.skip);
|
||||
expect(describeE2ETier('periodic')).toBe(describe.skip);
|
||||
});
|
||||
|
||||
test('EVALS=1 + EVALS_TIER unset → skip both tiers (the tierless test:evals / eval:bg:all trap)', () => {
|
||||
process.env.EVALS = '1';
|
||||
expect(e2eTierEnabled('gate')).toBe(false);
|
||||
|
||||
@@ -116,9 +116,10 @@ export let selectedTests: string[] | null = resolveModuleSelection(
|
||||
// EVALS_TIER: filter tests by tier after diff-based selection.
|
||||
// 'gate' = gate tests only (CI default — blocks merge)
|
||||
// 'periodic' = periodic tests only (weekly cron / manual)
|
||||
// 'marathon' = full end-to-end flows only (non-blocking marathon lane)
|
||||
// not set = run all selected tests (local dev default, backward compat)
|
||||
if (evalsEnabled && process.env.EVALS_TIER) {
|
||||
const tier = process.env.EVALS_TIER as 'gate' | 'periodic';
|
||||
const tier = process.env.EVALS_TIER as 'gate' | 'periodic' | 'marathon';
|
||||
const tierTests = Object.entries(E2E_TIERS)
|
||||
.filter(([, t]) => t === tier)
|
||||
.map(([name]) => name);
|
||||
|
||||
@@ -1118,10 +1118,11 @@ export const E2E_TOUCHFILES: Record<string, string[]> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* E2E test tiers — 'gate' blocks PRs, 'periodic' runs weekly/on-demand.
|
||||
* E2E test tiers — 'gate' blocks PRs, 'periodic' runs weekly/on-demand,
|
||||
* 'marathon' keeps full start-to-finish flows in a non-blocking lane only.
|
||||
* Must have exactly the same keys as E2E_TOUCHFILES.
|
||||
*/
|
||||
export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
|
||||
export const E2E_TIERS: Record<string, 'gate' | 'periodic' | 'marathon'> = {
|
||||
'ship-skipped-queued-finding': 'gate',
|
||||
'investigate-owned-completion': 'gate',
|
||||
'investigate-owned-abort': 'gate',
|
||||
|
||||
@@ -46,7 +46,7 @@ if (evalsEnabled && !process.env.EVALS_ALL) {
|
||||
|
||||
// Apply EVALS_TIER filter (same logic as e2e-helpers.ts)
|
||||
if (evalsEnabled && process.env.EVALS_TIER) {
|
||||
const tier = process.env.EVALS_TIER as 'gate' | 'periodic';
|
||||
const tier = process.env.EVALS_TIER as 'gate' | 'periodic' | 'marathon';
|
||||
const tierTests = Object.entries(E2E_TIERS)
|
||||
.filter(([, t]) => t === tier)
|
||||
.map(([name]) => name);
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('fast PR coverage policy', () => {
|
||||
expect(PR_PROFILE_CASE_IDS).toContain('plan-ceo-review-benefits');
|
||||
expect(PR_PROFILE_CASE_IDS).toContain('plan-review-report');
|
||||
expect(PR_PROFILE_CASE_IDS).toContain('auq-format-gate');
|
||||
for (const tier of Object.values(PR_PROFILE_MAPS.tiers)) expect(['gate', 'periodic']).toContain(tier);
|
||||
for (const tier of Object.values(PR_PROFILE_MAPS.tiers)) expect(['gate', 'periodic', 'marathon']).toContain(tier);
|
||||
});
|
||||
|
||||
test('keeps relevant short probes and the failing CEO quality obligation; reports broad work', () => {
|
||||
@@ -151,6 +151,17 @@ describe('fast PR coverage policy', () => {
|
||||
expect(result.needsFullValidation).toBe(false);
|
||||
});
|
||||
|
||||
test('marathon cases are deferred to their non-blocking lane, even on full fallback', () => {
|
||||
const marathon: PrProfileMaps = { ...maps, tiers: { ...maps.tiers, 'ceo-full': 'marathon' } };
|
||||
for (const changedFiles of [['plan-ceo-review/SKILL.md.tmpl'], ['lib/new-runtime.ts']]) {
|
||||
const result = select({ maps: marathon, changedFiles });
|
||||
expect(result.e2e).not.toContain('ceo-full');
|
||||
expect(result.deferred.find(({ id }) => id === 'ceo-full')).toEqual({ id: 'ceo-full', tier: 'marathon',
|
||||
reason: 'Full end-to-end marathon coverage; non-blocking lane, not executed by the PR gate' });
|
||||
}
|
||||
expect(() => select({ maps: marathon, profile: ['ceo-full'] })).toThrow('broad gate census');
|
||||
});
|
||||
|
||||
test('rejects stale profile IDs, incorrect tiers, missing cadence, and unknown selections', () => {
|
||||
expect(() => select({ profile: ['missing'] })).toThrow('broad gate census');
|
||||
expect(() => select({ profile: ['ceo-periodic'] })).toThrow('broad gate census');
|
||||
|
||||
Reference in new issue
Block a user