From a905d20763dbe5467132f0ed3724fbb8f28489e3 Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Mon, 20 Apr 2026 10:12:49 +0200 Subject: [PATCH 1/4] Add tests for archive-artifacts Issue: ZENKO-5222 --- .github/actions/archive-artifacts/action.yaml | 6 + tests/workflows/archive-artifacts.spec.ts | 326 ++++++++++++++++++ tests/workflows/test-archive-artifacts.yaml | 14 + 3 files changed, 346 insertions(+) create mode 100644 tests/workflows/archive-artifacts.spec.ts create mode 100644 tests/workflows/test-archive-artifacts.yaml diff --git a/.github/actions/archive-artifacts/action.yaml b/.github/actions/archive-artifacts/action.yaml index a53d127648..ec8e40da10 100644 --- a/.github/actions/archive-artifacts/action.yaml +++ b/.github/actions/archive-artifacts/action.yaml @@ -20,6 +20,10 @@ inputs: trunk_token: description: Trunk token required: false + github_token: + description: GitHub token used for publishing test report annotations + default: ${{ github.token }} + required: false zenko-name: description: Zenko name default: end2end @@ -91,6 +95,7 @@ runs: - name: Publish test report uses: mikepenz/action-junit-report@v6 with: + token: ${{ inputs.github_token }} annotate_only: true check_name: ${{ steps.download-previous-reports.outputs.check-names }} report_paths: ${{ steps.download-previous-reports.outputs.report-paths }} @@ -105,6 +110,7 @@ runs: - name: Compute build success uses: mikepenz/action-junit-report@v6 with: + token: ${{ inputs.github_token }} annotate_only: true check_name: ${{ inputs.stage }} skip_annotations: true diff --git a/tests/workflows/archive-artifacts.spec.ts b/tests/workflows/archive-artifacts.spec.ts new file mode 100644 index 0000000000..e3e27bbab8 --- /dev/null +++ b/tests/workflows/archive-artifacts.spec.ts @@ -0,0 +1,326 @@ +import { Act, Mockapi } from "@kie/act-js"; +import { MockGithub, Moctokit } from "@kie/mock-github"; +import path from "path"; +import { exec as execCb } from "node:child_process"; +import { promisify } from "node:util"; +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; + +const exec = promisify(execCb); + +let github: MockGithub; +let mockapi: Mockapi; +let moctokit: Moctokit; +let act: Act; + +function createJUnitReport( + name: string, + tests: number, + failures: number = 0, + errors: number = 0, + skipped: number = 0, +): string { + const testcases = []; + for (let i = 0; i < tests - failures - errors - skipped; i++) + testcases.push(` `); + for (let i = 0; i < failures; i++) + testcases.push(` \n expected\n `); + for (let i = 0; i < errors; i++) + testcases.push(` \n bang\n `); + for (let i = 0; i < skipped; i++) + testcases.push(` \n \n `); + + return ` + + +${testcases.join("\n")} + + +`; +} + +async function getCommitHash(repo: string = "zenko") { + const { stdout } = await exec("git -C " + github.repo.getPath(repo) + " rev-parse HEAD"); + return stdout.trim(); +} + +beforeEach(async () => { + github = new MockGithub({ + repo: { + zenko: { + currentBranch: "development/2.14", + files: [ + { + src: path.resolve(__dirname, "../..", ".github"), + dest: ".github", + }, + { + src: path.resolve(__dirname, "test-archive-artifacts.yaml"), + dest: ".github/workflows/test-archive-artifacts.yaml", + }, + ], + }, + }, + }); + await github.setup(); + + mockapi = new Mockapi({ + artifacts: { + baseUrl: "https://artifacts.scality.net", + endpoints: { + root: { + listReports: { + path: "/builds/{artifact}/data/test-archive.{attempt}/reports/?format=txt", + method: "get", + parameters: { + query: [], + path: ["artifact", "attempt"], + body: [], + }, + }, + downloadReport: { + path: "/builds/{artifact}/data/test-archive.{attempt}/reports/{filename}", + method: "get", + parameters: { + query: [], + path: ["artifact", "attempt", "filename"], + body: [], + }, + }, + uploadReport: { + path: "/upload/{artifact}/data/test-archive.{attempt}/reports/{report}", + method: "put", + parameters: { + query: [], + path: ["artifact", "attempt", "report"], + body: [], + }, + }, + uploadMergedReport: { + path: "/upload/{artifact}/data/test-archive.{attempt}/junit-merged.xml", + method: "put", + parameters: { + query: [], + path: ["artifact", "attempt"], + body: [], + }, + }, + uploadKindLogs: { + path: "/upload/{artifact}/data/test-archive.{attempt}/kind-logs/{file}", + method: "put", + parameters: { + query: [], + path: ["artifact", "attempt", "file"], + body: [], + }, + }, + uploadLogsArchive: { + path: "/upload/{artifact}/{file}", + method: "put", + parameters: { + query: [], + path: ["artifact", "file"], + body: [], + }, + }, + // action-artifacts@v4 GETs /version/2/{name}/{file} before uploading + // when run_attempt != 1; response body must end with 'PASSED\n'. + versionFile: { + path: "/version/2/{artifact}/{file}", + method: "get", + parameters: { + query: [], + path: ["artifact", "file"], + body: [], + }, + }, + }, + }, + }, + }); + + moctokit = new Moctokit("http://api.github.com"); + + act = new Act(github.repo.getPath("zenko")); + act.setWorkflowFile(".github/workflows/test-archive-artifacts.yaml"); + + act.setEnv("GITHUB_JOB", "test-archive"); + act.setEnv("GITHUB_RUN_ATTEMPT", "1"); + act.setEnv("GITHUB_SHA", await getCommitHash()); + act.setEnv("GITHUB_REPOSITORY", "scality/zenko"); + act.setEnv("GITHUB_API_URL", "http://api.github.com"); + act.setEnv("GITHUB_RUN_ID", "1"); + + act.setPlatforms("ubuntu-24.04", "ghcr.io/catthehacker/ubuntu:act-24.04"); +}); + +afterEach(async () => { + await github.teardown(); +}); + +/** Common mock APIs needed by every test: artifacts service + GitHub checks API. */ +function commonMockApi(...extraMocks: any[]) { + return [ + // GitHub Checks API used by mikepenz/action-junit-report + moctokit.rest.checks.create().reply({ status: 201, data: { id: 1 }, repeat: 10 }), + moctokit.rest.checks.update().reply({ status: 200, data: { id: 1 }, repeat: 10 }), + moctokit.rest.checks.listForRef().reply({ status: 200, data: { total_count: 0, check_runs: [] }, repeat: 10 }), + // GitHub Actions run API used by mikepenz/action-junit-report + moctokit.rest.actions.getWorkflowRun().reply({ + status: 200, + data: { id: 1, status: "completed", conclusion: "success", head_sha: "abc", html_url: "https://github.com" }, + repeat: 10, + }), + mockapi.mock.artifacts.root.uploadKindLogs().reply({ status: 200, data: {}, repeat: 10 }), + mockapi.mock.artifacts.root.uploadMergedReport().reply({ status: 200, data: {}, repeat: 10 }), + mockapi.mock.artifacts.root.uploadLogsArchive().reply({ status: 200, data: {}, repeat: 10 }), + ...extraMocks, + ]; +} + +/** + * act >=0.2.81 appends a timing suffix to success/failure lines, which + * act-js splits into a named entry followed by an unnamed status entry. + * The real status of result[i] lives on result[i + 1]. + */ +function findStep(result: { name: string; status: number }[], nameFragment: string) { + const idx = result.findIndex(r => r.name?.includes(nameFragment)); + if (idx < 0) { + return undefined; + } + + expect(result.length).toBeGreaterThan(idx + 1); + expect(result[idx + 1].name).not.toBeDefined(); + expect(result[idx + 1].status).toBeDefined(); + if (result.length > idx + 2) { + expect(result[idx + 2].name).toBeDefined(); + } + + return { name: result[idx].name, status: result[idx + 1]?.status }; +} + +describe("archive-artifacts action", () => { + describe("reporting logic", () => { + it("should merge a single test report", async () => { + const repoPath = github.repo.getPath("zenko")!; + mkdirSync(path.join(repoPath, "artifacts/data/reports"), { recursive: true }); + writeFileSync(path.join(repoPath, "artifacts/data/reports/test.xml"), createJUnitReport("Suite", 5)); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-single.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.uploadReport({ report: "test.xml" }).reply({ status: 200, data: {} }), + ), + bind: true, + }); + + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(0); + }); + + it("should merge multiple test reports", async () => { + const repoPath = github.repo.getPath("zenko")!; + const dir = path.join(repoPath, "artifacts/data/reports"); + mkdirSync(dir, { recursive: true }); + writeFileSync(path.join(dir, "a.xml"), createJUnitReport("SuiteA", 5, 1)); + writeFileSync(path.join(dir, "b.xml"), createJUnitReport("SuiteB", 3, 0, 1)); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-multiple.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.uploadReport({ report: "a.xml" }).reply({ status: 200, data: {} }), + mockapi.mock.artifacts.root.uploadReport({ report: "b.xml" }).reply({ status: 200, data: {} }), + ), + bind: true, + }); + + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(1); + }); + + it("should report failures", async () => { + const repoPath = github.repo.getPath("zenko")!; + mkdirSync(path.join(repoPath, "artifacts/data/reports"), { recursive: true }); + writeFileSync( + path.join(repoPath, "artifacts/data/reports/test.xml"), + createJUnitReport("Suite", 10, 2), + ); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-failures.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.uploadReport({ report: "test.xml" }).reply({ status: 200, data: {} }), + ), + bind: true, + }); + + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(1); + }); + + it("should download reports from previous attempt", async () => { + act.setEnv("GITHUB_RUN_ATTEMPT", "2"); + + const repoPath = github.repo.getPath("zenko")!; + mkdirSync(path.join(repoPath, "artifacts/data/reports"), { recursive: true }); + writeFileSync( + path.join(repoPath, "artifacts/data/reports/current.xml"), + createJUnitReport("Suite", 3), + ); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-retry.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.listReports({ attempt: 1 }).reply({ status: 200, data: "prev.xml" }), + mockapi.mock.artifacts.root.downloadReport({ attempt: 1 }).reply({ status: 200, data: createJUnitReport("Suite", 5, 2) }), + mockapi.mock.artifacts.root.uploadReport({ report: "current.xml" }).reply({ status: 200, data: {} }), + mockapi.mock.artifacts.root.versionFile().reply({ status: 200, data: "PASSED\n", repeat: 20 }), + ), + bind: true, + }); + + expect(findStep(result, "Main Download previous JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(0); + }); + + it("should report errors", async () => { + const repoPath = github.repo.getPath("zenko")!; + mkdirSync(path.join(repoPath, "artifacts/data/reports"), { recursive: true }); + writeFileSync( + path.join(repoPath, "artifacts/data/reports/test.xml"), + createJUnitReport("Suite", 8, 0, 2), + ); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-errors.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.uploadReport({ report: "test.xml" }).reply({ status: 200, data: {} }), + ), + bind: true, + }); + + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(1); + }); + + it("should handle skipped tests", async () => { + const repoPath = github.repo.getPath("zenko")!; + const dir = path.join(repoPath, "artifacts/data/reports"); + mkdirSync(dir, { recursive: true }); + writeFileSync(path.join(dir, "a.xml"), createJUnitReport("Suite", 5, 0, 0, 1)); + writeFileSync(path.join(dir, "b.xml"), createJUnitReport("Suite", 4, 0, 0, 2)); + + const result = await act.runEvent("workflow_dispatch", { + logFile: process.env.ACT_LOG ? path.join(__dirname, "act-archive-skipped.log") : undefined, + mockApi: commonMockApi( + mockapi.mock.artifacts.root.uploadReport({ report: "a.xml" }).reply({ status: 200, data: {} }), + mockapi.mock.artifacts.root.uploadReport({ report: "b.xml" }).reply({ status: 200, data: {} }), + ), + bind: true, + }); + + expect(findStep(result, "Main Merge JUnit reports")?.status).toBe(0); + expect(findStep(result, "Main Compute build success")?.status).toBe(0); + }); + }); +}); diff --git a/tests/workflows/test-archive-artifacts.yaml b/tests/workflows/test-archive-artifacts.yaml new file mode 100644 index 0000000000..ec862672fc --- /dev/null +++ b/tests/workflows/test-archive-artifacts.yaml @@ -0,0 +1,14 @@ +--- +name: Test Archive Artifacts +on: workflow_dispatch +jobs: + test-archive: + runs-on: ubuntu-latest + steps: + - name: Archive artifacts + uses: ./.github/actions/archive-artifacts + with: + user: testuser + password: testpass + junit-paths: ${{ github.workspace }}/artifacts/data/reports/*.xml + github_token: fake-token From 0da305febbc290e34ce417cfb22dcae2c84b48a5 Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Mon, 20 Apr 2026 10:28:01 +0200 Subject: [PATCH 2/4] Add merge-junit-reports tests Issue: ZENKO-5222 --- .github/workflows/end2end.yaml | 15 ++ tests/scripts/test_merge_junit_reports.py | 284 ++++++++++++++++++++++ 2 files changed, 299 insertions(+) create mode 100644 tests/scripts/test_merge_junit_reports.py diff --git a/.github/workflows/end2end.yaml b/.github/workflows/end2end.yaml index ed45fe6754..e7605d3356 100644 --- a/.github/workflows/end2end.yaml +++ b/.github/workflows/end2end.yaml @@ -144,6 +144,20 @@ jobs: uses: ./.github/workflows/alerts.yaml secrets: inherit + check-scripts: + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Install pytest + run: pip install pytest + - name: Run scripts tests + run: pytest tests/scripts/ + check-workflows: runs-on: ubuntu-24.04 steps: @@ -772,6 +786,7 @@ jobs: needs: - check-alerts - check-dashboard-versions + - check-scripts - check-workflows - build-doc - build-iso diff --git a/tests/scripts/test_merge_junit_reports.py b/tests/scripts/test_merge_junit_reports.py new file mode 100644 index 0000000000..fdb8adb5db --- /dev/null +++ b/tests/scripts/test_merge_junit_reports.py @@ -0,0 +1,284 @@ +import importlib.util +import textwrap +import xml.etree.ElementTree as ET +from pathlib import Path + +import pytest + + +@pytest.fixture(scope="module") +def merge_module(): + module_path = ( + Path(__file__).resolve().parents[2] + / ".github" + / "scripts" + / "merge-junit-reports.py" + ) + spec = importlib.util.spec_from_file_location("merge_junit_reports", module_path) + module = importlib.util.module_from_spec(spec) + assert spec is not None and spec.loader is not None + spec.loader.exec_module(module) + return module + + +def _write_xml(path: Path, content: str) -> None: + path.write_text(textwrap.dedent(content).strip() + "\n", encoding="utf-8") + + +def test_merge_preserves_duplicate_testcases_for_flaky_detection(tmp_path, merge_module): + run1 = tmp_path / "run1.xml" + run2 = tmp_path / "run2.xml" + merged = tmp_path / "merged.xml" + + _write_xml( + run1, + """ + + + + + + failed on first attempt + + + + """, + ) + + _write_xml( + run2, + """ + + + + + + + + """, + ) + + merge_module.merge_reports(str(merged), [str(run1), str(run2)]) + + root = ET.parse(merged).getroot() + assert root.tag == "testsuites" + assert root.get("tests") == "4" + assert root.get("failures") == "1" + assert root.get("errors") == "0" + assert root.get("skipped") == "0" + + suites = root.findall("testsuite") + assert len(suites) == 1 + + suite = suites[0] + assert suite.get("name") == "ExampleSuite" + assert suite.get("package") == "pkg" + assert suite.get("tests") == "4" + assert suite.get("failures") == "1" + assert suite.get("errors") == "0" + assert suite.get("skipped") == "0" + + testcases = suite.findall("testcase") + assert len(testcases) == 4 + + flaky_cases = [ + tc + for tc in testcases + if tc.get("name") == "test_flaky" + and tc.get("classname") == "pkg.ExampleSuite" + and tc.get("file") == "suite.py" + ] + assert len(flaky_cases) == 2 + assert sum(1 for tc in flaky_cases if tc.find("failure") is not None) == 1 + + +def test_merge_keeps_distinct_suites_and_recomputes_totals(tmp_path, merge_module): + first = tmp_path / "first.xml" + second = tmp_path / "second.xml" + merged = tmp_path / "merged.xml" + + _write_xml( + first, + """ + + + + + + + + error + + + + """, + ) + + _write_xml( + second, + """ + + + + + + + + + + failure + + + + """, + ) + + merge_module.merge_reports(str(merged), [str(first), str(second)]) + + root = ET.parse(merged).getroot() + assert root.get("tests") == "4" + assert root.get("failures") == "1" + assert root.get("errors") == "1" + assert root.get("skipped") == "1" + + suites_by_name = {suite.get("name"): suite for suite in root.findall("testsuite")} + assert set(suites_by_name.keys()) == {"SuiteA", "SuiteB", "SuiteC"} + + suite_a = suites_by_name["SuiteA"] + assert suite_a.get("tests") == "2" + assert suite_a.get("failures") == "0" + assert suite_a.get("errors") == "0" + assert suite_a.get("skipped") == "1" + assert len(suite_a.findall("testcase")) == 2 + + +def test_single_testsuite_root(tmp_path, merge_module): + """A file whose root element is (not ) is accepted.""" + src = tmp_path / "bare.xml" + out = tmp_path / "merged.xml" + + _write_xml( + src, + """ + + + + + """, + ) + + merge_module.merge_reports(str(out), [str(src)]) + + root = ET.parse(out).getroot() + assert root.tag == "testsuites" + assert root.get("tests") == "1" + suites = root.findall("testsuite") + assert len(suites) == 1 + assert suites[0].get("name") == "Bare" + + +def test_missing_file_logs_warning_and_continues(tmp_path, capsys, merge_module): + """A non-existent input file emits a warning but does not abort the merge.""" + real = tmp_path / "real.xml" + out = tmp_path / "merged.xml" + + _write_xml( + real, + """ + + + + + + + """, + ) + + missing = str(tmp_path / "does_not_exist.xml") + merge_module.merge_reports(str(out), [str(real), missing]) + + root = ET.parse(out).getroot() + assert root.get("tests") == "1" + assert f"::warning::File not found: {missing}" in capsys.readouterr().err + + +def test_get_suite_key(merge_module): + """get_suite_key returns 'package::name'.""" + suite = ET.Element("testsuite") + suite.set("name", "MyTests") + suite.set("package", "com.example") + + assert merge_module.get_suite_key(suite) == "com.example::MyTests" + + +def test_get_suite_key_missing_attrs(merge_module): + """get_suite_key handles missing name/package gracefully.""" + suite = ET.Element("testsuite") + assert merge_module.get_suite_key(suite) == "::" + + +def test_time_accumulated_when_merging(tmp_path, merge_module): + """Time values are summed across merged suites.""" + run1 = tmp_path / "r1.xml" + run2 = tmp_path / "r2.xml" + out = tmp_path / "merged.xml" + + _write_xml( + run1, + """ + + + + + + + """, + ) + _write_xml( + run2, + """ + + + + + + + """, + ) + + merge_module.merge_reports(str(out), [str(run1), str(run2)]) + + root = ET.parse(out).getroot() + suite = root.find("testsuite") + assert float(suite.get("time")) == 4.0 + + +def test_github_output_written(tmp_path, monkeypatch, merge_module): + """merge_reports writes totals to GITHUB_OUTPUT when the env var is set.""" + src = tmp_path / "src.xml" + out = tmp_path / "merged.xml" + gh_out = tmp_path / "gh_output.txt" + + _write_xml( + src, + """ + + + + + + oops + + + + """, + ) + + monkeypatch.setenv("GITHUB_OUTPUT", str(gh_out)) + merge_module.merge_reports(str(out), [str(src)]) + + content = gh_out.read_text() + assert "tests=2" in content + assert "failures=1" in content + assert "errors=0" in content + assert "skipped=0" in content From 8adcadc6a6aeb47226b7d7cf1a22cec598cc2a91 Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Thu, 23 Apr 2026 01:00:39 +0200 Subject: [PATCH 3/4] review: inherit secrets Issue: ZENKO-5222 --- .github/workflows/review.yaml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/.github/workflows/review.yaml b/.github/workflows/review.yaml index c6823749f0..7fcceda62e 100644 --- a/.github/workflows/review.yaml +++ b/.github/workflows/review.yaml @@ -17,24 +17,13 @@ jobs: if: github.event_name != 'pull_request_target' && github.actor != 'dependabot[bot]' uses: scality/workflows/.github/workflows/claude-code-review.yml@v2 with: - plugin_marketplaces: scality/agent-hub allowed-tools: >- ${{ github.event_name == 'workflow_dispatch' && '"Bash(gh api repos/*/contents)"' || '' }} - secrets: - GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} - ANTHROPIC_VERTEX_PROJECT_ID: ${{ secrets.ANTHROPIC_VERTEX_PROJECT_ID }} - CLOUD_ML_REGION: ${{ secrets.CLOUD_ML_REGION }} - ACTIONS_APP_PRIVATE_KEY: ${{ secrets.ACTIONS_APP_PRIVATE_KEY }} + secrets: inherit review-dependency-bump: if: github.event_name == 'pull_request_target' && github.actor == 'dependabot[bot]' uses: scality/workflows/.github/workflows/claude-code-dependency-review.yml@v2 with: ACTIONS_APP_ID: ${{ vars.ACTIONS_APP_ID }} - secrets: - GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }} - GCP_SERVICE_ACCOUNT: ${{ secrets.GCP_SERVICE_ACCOUNT }} - ANTHROPIC_VERTEX_PROJECT_ID: ${{ secrets.ANTHROPIC_VERTEX_PROJECT_ID }} - CLOUD_ML_REGION: ${{ secrets.CLOUD_ML_REGION }} - ACTIONS_APP_PRIVATE_KEY: ${{ secrets.ACTIONS_APP_PRIVATE_KEY }} + secrets: inherit From 0eaffd87ba4b31239c89c815669c6e38b465f39a Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Wed, 22 Apr 2026 19:42:32 +0200 Subject: [PATCH 4/4] Move workflow test resources to folder Issue: ZENKO-5222 --- tests/workflows/archive-artifacts.spec.ts | 2 +- .../create-component-deployments.spec.ts | 37 ++----------------- tests/workflows/{ => fixtures}/VERSION-2.3.7 | 0 .../workflows/{ => fixtures}/VERSION-2.3.7-1 | 0 .../{ => fixtures}/VERSION-2.3.7-rc.1 | 0 .../test-archive-artifacts.yaml | 0 .../test-create-component-deployments.yaml | 0 .../{ => fixtures}/test-deps-base.yaml | 0 tests/workflows/{ => fixtures}/test-deps.yaml | 0 tests/workflows/fixtures/test-private-key.ts | 29 +++++++++++++++ tests/workflows/parse-deps.spec.ts | 2 +- tests/workflows/release.spec.ts | 37 ++----------------- 12 files changed, 39 insertions(+), 68 deletions(-) rename tests/workflows/{ => fixtures}/VERSION-2.3.7 (100%) rename tests/workflows/{ => fixtures}/VERSION-2.3.7-1 (100%) rename tests/workflows/{ => fixtures}/VERSION-2.3.7-rc.1 (100%) rename tests/workflows/{ => fixtures}/test-archive-artifacts.yaml (100%) rename tests/workflows/{ => fixtures}/test-create-component-deployments.yaml (100%) rename tests/workflows/{ => fixtures}/test-deps-base.yaml (100%) rename tests/workflows/{ => fixtures}/test-deps.yaml (100%) create mode 100644 tests/workflows/fixtures/test-private-key.ts diff --git a/tests/workflows/archive-artifacts.spec.ts b/tests/workflows/archive-artifacts.spec.ts index e3e27bbab8..c2709d190a 100644 --- a/tests/workflows/archive-artifacts.spec.ts +++ b/tests/workflows/archive-artifacts.spec.ts @@ -54,7 +54,7 @@ beforeEach(async () => { dest: ".github", }, { - src: path.resolve(__dirname, "test-archive-artifacts.yaml"), + src: path.resolve(__dirname, "fixtures/test-archive-artifacts.yaml"), dest: ".github/workflows/test-archive-artifacts.yaml", }, ], diff --git a/tests/workflows/create-component-deployments.spec.ts b/tests/workflows/create-component-deployments.spec.ts index 3a35ee3701..c89b45a26f 100644 --- a/tests/workflows/create-component-deployments.spec.ts +++ b/tests/workflows/create-component-deployments.spec.ts @@ -4,39 +4,10 @@ import nock from "nock"; import path from "path"; import { exec as execCb } from "node:child_process"; import { promisify } from "node:util"; +import { TEST_PRIVATE_KEY } from "./fixtures/test-private-key"; const exec = promisify(execCb); -// Test-only RSA key (not used anywhere else, generated for act.js mock) -const TEST_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDSSu4ghRVAKyjX -c25FKdE+sARk3Jai8k8DCjJU/DMNskgNvGh7JPLDww98Ts9E3ddMNt06oBt5p8qc -YfaInUR0poPcJG6JbPWVEefNC00dTiHlXEGU8Ih8Dc2Ezf/zb45my9DmmGXeVScf -LNYtSSqxfqdjFIOvr8XJ8kuB7L3jyFTBN9xjfdTnmsJ0ilSatV50o6RdVjiZvf2r -z8uhlIw/b0xw8ZZ5rRXNr1VgDW7kcK952OYIDo9qvGMxOASjx9cpUJBkE/nrWF8I -HAGACAqUZJaF4p/CQFjd/7cmUpA+Shh31UdD/rSXzC1bTEB5vaJN8LyVEBYw+n19 -iv6QO/K1AgMBAAECggEAaRNksd4dlK8cHK+CQU/YTGzx/R3VrPzLKxcsuBc+QVE8 -PJTQVfvLy7JLKg9M9LmuStg9KX53zA1hqUsvvupqGqlbSKPxkXxep4pHW0aS1RpF -yI+U+2FGqUnST9II2q/6pPWhX591gybkQekK6ZzeFstUwyaseBwphbMqNHTBGy+F -9A52Zg42QBQbQoIsOiqpJTKxhDpdEx+AZqrG1EawQkygVeNyRnOaJgCgVTIxqk+m -lENCchKeZmo6aul/4LwH9GPDF+1ftMIlAwFXwQgp/IuzFmNf564NSIEZbWgYC8aN -gl3ZU5K6mrwbKpGg2HXPDb6AA0U4WN1Bmw8wLyWhjwKBgQDsgNSi4+kfjLJM9oHQ -PkNTfZ7rmrSdPf8gAodGsrABKz9IhfoCQJ0jNtegaGq3JB+YVJUUWtw4cnyBGOcE -f1F9JIRMcq885p9zwX0jDLLe1vR8305tmYJFweqAViwWQ2riy2IqupsWEhgKev1S -8QgSsaDcP1wksDWokykui85TzwKBgQDjoPOc1zs1teMG5zpSQVm3QT2C3ryRSBRo -+1jyszl8Rrm7x9IcmiyBL5XQOpHoNHH24wUDte+t08V/34mhEwaa3tl1MUmJLsBG -+LkTVXdRcFnHoqCbqFqYeV8eXQgY+Narq245VGGa1CfkhHvqQvuKVlIDLgPPrutg -czA0MpW+OwKBgQDU4ImFLTwzR8Nd/yyNst2LEzGuxIv6VUmFGIGHI2PFSZYmw2Fs -EZjfj4e7PQGBY6SEyu19auN6c6KZ2T5oD+nbiLkEzt3pJXU1Dl6C4/VFG5rpo17G -zDw0af2YEviP+ZMGHSd5aooZ7aNyG45Vz9sCaJxwYx+fbnR+DigtW24WhQKBgQCf -sPXXTWO7jYvk9ukCddhT6NAXdN2Darbu446GTdgBaLi6lTfBWyPnyZNnjv93kPt2 -wdNtxACOyWfgCtnKB8f1dGvIfLhjJko8QBfPCYF4v8IsfNoB+bz9BQEHEyswIbqw -msbsL1d+QGJwPcWVFkLTzTUiB/EijUuR0Z26sNY+qwKBgQDWDEEBjZ+63kttpp4c -EAyXAIwDT+KhVppmXvIAjVkqP6+I8yqUSCFjXMTT1Bubovqk6wAnpYdA559LgRjc -gfkN+TRRaIeVB9jxzFHszenX6CVswwBtwSj331N/87GnI7fF7/ZDmMKRSiRjIQyL -6c4hJmUD3bnLspBgcbLD33c7Dw== ------END PRIVATE KEY-----`; - let github: MockGithub; let moctokit: Moctokit; let act: Act; @@ -80,11 +51,11 @@ beforeEach(async () => { dest: ".github", }, { - src: path.resolve(__dirname, "test-deps-base.yaml"), + src: path.resolve(__dirname, "fixtures/test-deps-base.yaml"), dest: "solution/deps.yaml", }, { - src: path.resolve(__dirname, "test-create-component-deployments.yaml"), + src: path.resolve(__dirname, "fixtures/test-create-component-deployments.yaml"), dest: ".github/workflows/test-create-component-deployments.yaml", }, ], @@ -95,7 +66,7 @@ beforeEach(async () => { // Create feature branch with updated deps (sorbet v1.2.1 -> v1.2.2) const repoPath = github.repo.getPath("zenko"); - const depsUpdate = path.resolve(__dirname, "test-deps.yaml"); + const depsUpdate = path.resolve(__dirname, "fixtures/test-deps.yaml"); await exec(`git -C ${repoPath} checkout -b improvement/ZENKO-5210`); await exec(`cp ${depsUpdate} ${repoPath}/solution/deps.yaml`); await exec(`git -C ${repoPath} add solution/deps.yaml && git -C ${repoPath} commit -m "bump sorbet"`); diff --git a/tests/workflows/VERSION-2.3.7 b/tests/workflows/fixtures/VERSION-2.3.7 similarity index 100% rename from tests/workflows/VERSION-2.3.7 rename to tests/workflows/fixtures/VERSION-2.3.7 diff --git a/tests/workflows/VERSION-2.3.7-1 b/tests/workflows/fixtures/VERSION-2.3.7-1 similarity index 100% rename from tests/workflows/VERSION-2.3.7-1 rename to tests/workflows/fixtures/VERSION-2.3.7-1 diff --git a/tests/workflows/VERSION-2.3.7-rc.1 b/tests/workflows/fixtures/VERSION-2.3.7-rc.1 similarity index 100% rename from tests/workflows/VERSION-2.3.7-rc.1 rename to tests/workflows/fixtures/VERSION-2.3.7-rc.1 diff --git a/tests/workflows/test-archive-artifacts.yaml b/tests/workflows/fixtures/test-archive-artifacts.yaml similarity index 100% rename from tests/workflows/test-archive-artifacts.yaml rename to tests/workflows/fixtures/test-archive-artifacts.yaml diff --git a/tests/workflows/test-create-component-deployments.yaml b/tests/workflows/fixtures/test-create-component-deployments.yaml similarity index 100% rename from tests/workflows/test-create-component-deployments.yaml rename to tests/workflows/fixtures/test-create-component-deployments.yaml diff --git a/tests/workflows/test-deps-base.yaml b/tests/workflows/fixtures/test-deps-base.yaml similarity index 100% rename from tests/workflows/test-deps-base.yaml rename to tests/workflows/fixtures/test-deps-base.yaml diff --git a/tests/workflows/test-deps.yaml b/tests/workflows/fixtures/test-deps.yaml similarity index 100% rename from tests/workflows/test-deps.yaml rename to tests/workflows/fixtures/test-deps.yaml diff --git a/tests/workflows/fixtures/test-private-key.ts b/tests/workflows/fixtures/test-private-key.ts new file mode 100644 index 0000000000..54588e2e30 --- /dev/null +++ b/tests/workflows/fixtures/test-private-key.ts @@ -0,0 +1,29 @@ +// Test-only RSA key (not used anywhere else, generated for act.js mock) +export const TEST_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDSSu4ghRVAKyjX +c25FKdE+sARk3Jai8k8DCjJU/DMNskgNvGh7JPLDww98Ts9E3ddMNt06oBt5p8qc +YfaInUR0poPcJG6JbPWVEefNC00dTiHlXEGU8Ih8Dc2Ezf/zb45my9DmmGXeVScf +LNYtSSqxfqdjFIOvr8XJ8kuB7L3jyFTBN9xjfdTnmsJ0ilSatV50o6RdVjiZvf2r +z8uhlIw/b0xw8ZZ5rRXNr1VgDW7kcK952OYIDo9qvGMxOASjx9cpUJBkE/nrWF8I +HAGACAqUZJaF4p/CQFjd/7cmUpA+Shh31UdD/rSXzC1bTEB5vaJN8LyVEBYw+n19 +iv6QO/K1AgMBAAECggEAaRNksd4dlK8cHK+CQU/YTGzx/R3VrPzLKxcsuBc+QVE8 +PJTQVfvLy7JLKg9M9LmuStg9KX53zA1hqUsvvupqGqlbSKPxkXxep4pHW0aS1RpF +yI+U+2FGqUnST9II2q/6pPWhX591gybkQekK6ZzeFstUwyaseBwphbMqNHTBGy+F +9A52Zg42QBQbQoIsOiqpJTKxhDpdEx+AZqrG1EawQkygVeNyRnOaJgCgVTIxqk+m +lENCchKeZmo6aul/4LwH9GPDF+1ftMIlAwFXwQgp/IuzFmNf564NSIEZbWgYC8aN +gl3ZU5K6mrwbKpGg2HXPDb6AA0U4WN1Bmw8wLyWhjwKBgQDsgNSi4+kfjLJM9oHQ +PkNTfZ7rmrSdPf8gAodGsrABKz9IhfoCQJ0jNtegaGq3JB+YVJUUWtw4cnyBGOcE +f1F9JIRMcq885p9zwX0jDLLe1vR8305tmYJFweqAViwWQ2riy2IqupsWEhgKev1S +8QgSsaDcP1wksDWokykui85TzwKBgQDjoPOc1zs1teMG5zpSQVm3QT2C3ryRSBRo ++1jyszl8Rrm7x9IcmiyBL5XQOpHoNHH24wUDte+t08V/34mhEwaa3tl1MUmJLsBG ++LkTVXdRcFnHoqCbqFqYeV8eXQgY+Narq245VGGa1CfkhHvqQvuKVlIDLgPPrutg +czA0MpW+OwKBgQDU4ImFLTwzR8Nd/yyNst2LEzGuxIv6VUmFGIGHI2PFSZYmw2Fs +EZjfj4e7PQGBY6SEyu19auN6c6KZ2T5oD+nbiLkEzt3pJXU1Dl6C4/VFG5rpo17G +zDw0af2YEviP+ZMGHSd5aooZ7aNyG45Vz9sCaJxwYx+fbnR+DigtW24WhQKBgQCf +sPXXTWO7jYvk9ukCddhT6NAXdN2Darbu446GTdgBaLi6lTfBWyPnyZNnjv93kPt2 +wdNtxACOyWfgCtnKB8f1dGvIfLhjJko8QBfPCYF4v8IsfNoB+bz9BQEHEyswIbqw +msbsL1d+QGJwPcWVFkLTzTUiB/EijUuR0Z26sNY+qwKBgQDWDEEBjZ+63kttpp4c +EAyXAIwDT+KhVppmXvIAjVkqP6+I8yqUSCFjXMTT1Bubovqk6wAnpYdA559LgRjc +gfkN+TRRaIeVB9jxzFHszenX6CVswwBtwSj331N/87GnI7fF7/ZDmMKRSiRjIQyL +6c4hJmUD3bnLspBgcbLD33c7Dw== +-----END PRIVATE KEY-----`; diff --git a/tests/workflows/parse-deps.spec.ts b/tests/workflows/parse-deps.spec.ts index dcc6e18e5e..cfa13326f6 100644 --- a/tests/workflows/parse-deps.spec.ts +++ b/tests/workflows/parse-deps.spec.ts @@ -80,7 +80,7 @@ describe('parseDeps', () => { }); it('sets empty repo for playground images', () => { - const testDeps = yamlToJson(path.join(__dirname, 'test-deps.yaml')); + const testDeps = yamlToJson(path.join(__dirname, 'fixtures/test-deps.yaml')); const { components } = parseDeps(testDeps, 'scality/zenko'); const playground = components.find((c: { image: string }) => c.image.includes('playground')); diff --git a/tests/workflows/release.spec.ts b/tests/workflows/release.spec.ts index 775d60eec9..88ca97bb71 100644 --- a/tests/workflows/release.spec.ts +++ b/tests/workflows/release.spec.ts @@ -5,39 +5,10 @@ import { exec as execCb } from "node:child_process"; import { promisify } from "node:util"; import assert from "assert"; import nock from "nock"; +import { TEST_PRIVATE_KEY } from "./fixtures/test-private-key"; const exec = promisify(execCb); -// Test-only RSA key (not used anywhere else, generated for act.js mock) -const TEST_PRIVATE_KEY = `-----BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDSSu4ghRVAKyjX -c25FKdE+sARk3Jai8k8DCjJU/DMNskgNvGh7JPLDww98Ts9E3ddMNt06oBt5p8qc -YfaInUR0poPcJG6JbPWVEefNC00dTiHlXEGU8Ih8Dc2Ezf/zb45my9DmmGXeVScf -LNYtSSqxfqdjFIOvr8XJ8kuB7L3jyFTBN9xjfdTnmsJ0ilSatV50o6RdVjiZvf2r -z8uhlIw/b0xw8ZZ5rRXNr1VgDW7kcK952OYIDo9qvGMxOASjx9cpUJBkE/nrWF8I -HAGACAqUZJaF4p/CQFjd/7cmUpA+Shh31UdD/rSXzC1bTEB5vaJN8LyVEBYw+n19 -iv6QO/K1AgMBAAECggEAaRNksd4dlK8cHK+CQU/YTGzx/R3VrPzLKxcsuBc+QVE8 -PJTQVfvLy7JLKg9M9LmuStg9KX53zA1hqUsvvupqGqlbSKPxkXxep4pHW0aS1RpF -yI+U+2FGqUnST9II2q/6pPWhX591gybkQekK6ZzeFstUwyaseBwphbMqNHTBGy+F -9A52Zg42QBQbQoIsOiqpJTKxhDpdEx+AZqrG1EawQkygVeNyRnOaJgCgVTIxqk+m -lENCchKeZmo6aul/4LwH9GPDF+1ftMIlAwFXwQgp/IuzFmNf564NSIEZbWgYC8aN -gl3ZU5K6mrwbKpGg2HXPDb6AA0U4WN1Bmw8wLyWhjwKBgQDsgNSi4+kfjLJM9oHQ -PkNTfZ7rmrSdPf8gAodGsrABKz9IhfoCQJ0jNtegaGq3JB+YVJUUWtw4cnyBGOcE -f1F9JIRMcq885p9zwX0jDLLe1vR8305tmYJFweqAViwWQ2riy2IqupsWEhgKev1S -8QgSsaDcP1wksDWokykui85TzwKBgQDjoPOc1zs1teMG5zpSQVm3QT2C3ryRSBRo -+1jyszl8Rrm7x9IcmiyBL5XQOpHoNHH24wUDte+t08V/34mhEwaa3tl1MUmJLsBG -+LkTVXdRcFnHoqCbqFqYeV8eXQgY+Narq245VGGa1CfkhHvqQvuKVlIDLgPPrutg -czA0MpW+OwKBgQDU4ImFLTwzR8Nd/yyNst2LEzGuxIv6VUmFGIGHI2PFSZYmw2Fs -EZjfj4e7PQGBY6SEyu19auN6c6KZ2T5oD+nbiLkEzt3pJXU1Dl6C4/VFG5rpo17G -zDw0af2YEviP+ZMGHSd5aooZ7aNyG45Vz9sCaJxwYx+fbnR+DigtW24WhQKBgQCf -sPXXTWO7jYvk9ukCddhT6NAXdN2Darbu446GTdgBaLi6lTfBWyPnyZNnjv93kPt2 -wdNtxACOyWfgCtnKB8f1dGvIfLhjJko8QBfPCYF4v8IsfNoB+bz9BQEHEyswIbqw -msbsL1d+QGJwPcWVFkLTzTUiB/EijUuR0Z26sNY+qwKBgQDWDEEBjZ+63kttpp4c -EAyXAIwDT+KhVppmXvIAjVkqP6+I8yqUSCFjXMTT1Bubovqk6wAnpYdA559LgRjc -gfkN+TRRaIeVB9jxzFHszenX6CVswwBtwSj331N/87GnI7fF7/ZDmMKRSiRjIQyL -6c4hJmUD3bnLspBgcbLD33c7Dw== ------END PRIVATE KEY-----`; - let github: MockGithub; let mockapi: Mockapi; let moctokit: Moctokit @@ -87,7 +58,7 @@ function withVersionFile(versionFile: string, repo: string = 'zenko') { const targetVersionFile = github.repo.getPath(repo) + '/VERSION'; // Commit the new VERSION file - await exec('cp ' + path.resolve(__dirname, versionFile) + ' ' + targetVersionFile); + await exec('cp ' + path.resolve(__dirname, 'fixtures', versionFile) + ' ' + targetVersionFile); await exec('git -C ' + github.repo.getPath(repo) + ' commit --no-sign -m "bump version" -- ' + targetVersionFile); // Update artifact name to match the new version @@ -108,11 +79,11 @@ beforeEach(async () => { dest: ".github", }, { - src: path.resolve(__dirname, "VERSION-2.3.7-rc.1"), + src: path.resolve(__dirname, "fixtures/VERSION-2.3.7-rc.1"), dest: "VERSION", }, { - src: path.resolve(__dirname, "test-deps-base.yaml"), + src: path.resolve(__dirname, "fixtures/test-deps-base.yaml"), dest: "solution/deps.yaml", }, ],