diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bacfcf7fc7..25627d4b9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -433,7 +433,7 @@ jobs: --outputFile.junit=test-results.xml --outputFile.json=test-results.json - name: Summarize slowest test files - if: needs.changes.outputs.code == 'true' && !cancelled() && hashFiles('test-timings.json') != '' + if: needs.changes.outputs.code == 'true' && !cancelled() env: SHARD_RESULT: ${{ needs.test-shard.result }} SOURCE_REQUIRED: ${{ needs.changes.outputs.tier == 'fast' }} diff --git a/tests/boundary/filesystem/ciTestReports.test.ts b/tests/boundary/filesystem/ciTestReports.test.ts index 48ba18c114..233d7c8eb6 100644 --- a/tests/boundary/filesystem/ciTestReports.test.ts +++ b/tests/boundary/filesystem/ciTestReports.test.ts @@ -140,13 +140,18 @@ function conditionValue( condition: string | undefined, needs: Record, canceled: boolean, + timingReportExists = false, ) { if (!condition) throw new Error("Missing report collection condition"); return runInNewContext( condition .replaceAll("needs.test-shard", 'needs["test-shard"]') .replaceAll("needs.test-source", 'needs["test-source"]'), - { needs, cancelled: () => canceled }, + { + needs, + cancelled: () => canceled, + hashFiles: () => (timingReportExists ? "timing-report" : ""), + }, { timeout: 1_000 }, ); } @@ -207,6 +212,28 @@ it("collects either fast test group while keeping full reports tied to full shar ); }); +it("requires portable report validation even when the timing report is missing", async () => { + const summary = (await workflow()).test.steps.find((step) => + step.run?.includes("scripts/ci/test-report.mjs"), + ); + for (const tier of ["fast", "full"]) { + for (const timingReportExists of [false, true]) { + for (const code of ["true", "false"]) { + for (const canceled of [false, true]) { + expect( + conditionValue( + summary?.if, + { changes: { outputs: { code, tier } } }, + canceled, + timingReportExists, + ), + ).toBe(code === "true" && !canceled); + } + } + } + } +}); + it("keeps diagnosis available after merge failure and publishes only complete successful history", async () => { const steps = (await workflow()).test!.steps; const diagnosis = steps.find((step) => step.with?.name === "test-results"); @@ -270,6 +297,16 @@ it("rejects successful shard reports with missing or duplicate portable files", readFile(join(root, ".cache/ci-test-history/test-results.json")), ).rejects.toMatchObject({ code: "ENOENT" }); } + await run(["tests/first.test.ts", "tests/second.test.ts"]); + await rm(join(root, "test-timings.json")); + await expect( + executeTimingReport(root, { CI_VERIFY_TEST_INVENTORY: "true" }), + ).rejects.toMatchObject({ + stderr: expect.stringContaining("ENOENT"), + }); + await expect( + readFile(join(root, ".cache/ci-test-history/test-results.json")), + ).rejects.toMatchObject({ code: "ENOENT" }); }); it("runs the existing onboarding and Python producer guards from their selected CI owners", async () => {