mirror of
https://github.com/rustfs/rustfs.git
synced 2026-10-12 07:39:05 +00:00
ci: rotate the shared functional-tests concurrency group to -v2 (#7900)
This commit is contained in:
@@ -110,6 +110,7 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
self.env = {
|
||||
**os.environ, "GITHUB_STEP_SUMMARY": str(self.directory / "summary.md"),
|
||||
"GITHUB_ENV": str(self.directory / "github-env"), "RUNNER_TEMP": self.temp.name, "TMPDIR": self.temp.name,
|
||||
"LOG_FILE": str(self.directory / "suite.log"),
|
||||
}
|
||||
for key in ("server_url", "repository", "run_id", "run_attempt", "sha", "event_name"):
|
||||
self.env[f"GITHUB_{key.upper()}"] = self.context[f"github.{key}"]
|
||||
@@ -117,17 +118,10 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
self.artifacts = self.directory / "rustfs-security-314159-2"
|
||||
suite = self.directory / "auto-testing/rustfs-security-test.sh"
|
||||
suite.parent.mkdir()
|
||||
ansi = {"ANSI_GREEN": "\033[1;32m", "ANSI_RED": "\033[1;31m", "ANSI_YELLOW": "\033[1;33m", "ANSI_RESET": "\033[0m"}
|
||||
suite.write_text(
|
||||
'#!/usr/bin/env bash\nset -euo pipefail\n'
|
||||
'log_dir=$(mktemp -d "$TMPDIR/rustfs-security.XXXXXX")\n'
|
||||
# Verdict lines carry ANSI color escapes and are printed to stdout
|
||||
# (captured via tee into the artifacts suite.log), exactly like the
|
||||
# real suite output the report step has to grep through: a color
|
||||
# tag before the verdict and a reset escape between the tag and
|
||||
# the case id.
|
||||
'printf "%s\\n" "${ANSI_GREEN}[PASS]${ANSI_RESET} IAM-101 ok" "${ANSI_RED}[FAIL]${ANSI_RESET} STS-105 broken" "${ANSI_YELLOW}[SKIP]${ANSI_RESET} OIDC-103 skipped" | tee "$log_dir/suite.log"\n'
|
||||
'echo "CURRENT SUITE LOG" >> "$log_dir/suite.log"\n'
|
||||
'echo "CURRENT SUITE LOG" > "$log_dir/suite.log"\n'
|
||||
'echo "CURRENT SUITE STDOUT"; echo "CURRENT SUITE STDERR" >&2\n'
|
||||
'case "$FAKE_REPORT" in\n'
|
||||
f' present) printf "%s\\n" "CURRENT SUITE DIAGNOSTIC" "{CASE_ROW}" > "$REPORT_FILE" ;;\n'
|
||||
@@ -136,20 +130,19 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
'echo "UNWRAPPED SUITE SUMMARY" >> "$GITHUB_STEP_SUMMARY"\n'
|
||||
'exit "$FAKE_EXIT"\n'
|
||||
)
|
||||
self.env.update(ansi)
|
||||
|
||||
def test_workflow_wiring(self) -> None:
|
||||
names = list(self.steps)
|
||||
self.assertLess(names.index("Checkout repository (for the OIDC live gate script)"), names.index("Checkout auto-testing scripts (with retry)"))
|
||||
self.assertLess(names.index("Checkout repository (for the OIDC live gate script)"), names.index("Checkout auto-testing scripts"))
|
||||
self.assertNotIn(" continue-on-error: true", self.job)
|
||||
self.assertIn(" continue-on-error: true", self.steps["Run security suite"])
|
||||
for name in ("Initialize security evidence", "Generate report"):
|
||||
self.assertNotIn(" continue-on-error: true", self.steps[name])
|
||||
self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", self.steps["Generate report"])
|
||||
self.assertNotIn("/tmp/rustfs-security", self.source)
|
||||
for name in ("Upload functional report to dashboard", "Manage backlog issues (dedup / label / auto-close)"):
|
||||
expected = "${{ env.SECURITY_ARTIFACTS_DIR }}/report.md" if name.startswith("Upload") else '--report-file "${SECURITY_ARTIFACTS_DIR}/report.md"'
|
||||
self.assertIn(expected, "\n".join(self.steps[name]))
|
||||
for name in ("Upload functional report to dashboard", "File failure issue in rustfs/backlog"):
|
||||
report = next(line for line in self.steps[name] if line.strip().startswith("REPORT_FILE:"))
|
||||
self.assertIn("${{ env.SECURITY_ARTIFACTS_DIR }}/report.md", report)
|
||||
for name in ("Upload functional report to dashboard", "Upload report and logs"):
|
||||
self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", self.steps[name])
|
||||
artifact_settings = yaml_block(self.steps["Upload report and logs"], "with", 8)
|
||||
@@ -174,19 +167,11 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
self.assertEqual(suite.returncode, exit_code, suite.stderr)
|
||||
logs = list(Path(str(self.artifacts) + "-scratch").glob("rustfs-security.*/suite.log"))
|
||||
self.assertEqual(len(logs), 1)
|
||||
self.assertEqual(logs[0].read_text().splitlines()[-1], "CURRENT SUITE LOG")
|
||||
self.assertEqual(logs[0].read_text(), "CURRENT SUITE LOG\n")
|
||||
self.context["steps.test.outcome"] = outcome
|
||||
# A suite that never ran (skipped with no report, or a
|
||||
# cancelled run) leaves no suite.log behind, so there are no
|
||||
# verdict lines and the report step stays red.
|
||||
ran = outcome != "skipped" or mode == "present"
|
||||
report = self.run_step("Generate report")
|
||||
# The report step is red only for harness/environment breakdowns:
|
||||
# a failed suite that still produced verdict lines stays green,
|
||||
# while skipped/cancelled never reach a verdict at all.
|
||||
success = outcome == "success" and mode == "present"
|
||||
green = ran and (outcome == "success" or outcome == "failure")
|
||||
self.assertEqual(report.returncode == 0, green, report.stderr)
|
||||
self.assertEqual(report.returncode == 0, success, report.stderr)
|
||||
contents = (self.artifacts / "report.md").read_text()
|
||||
for expected in (
|
||||
"https://github.com/rustfs/rustfs/actions/runs/314159", "Attempt: 2",
|
||||
@@ -194,15 +179,8 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
f"Test Step Outcome: {'success' if success else 'failure'}", f"Suite Step Outcome: {outcome}",
|
||||
):
|
||||
self.assertIn(expected, contents)
|
||||
# The verdict counters must see through the ANSI escapes in the
|
||||
# suite log: 1 passed, 1 failed, 1 skipped (only when the suite
|
||||
# actually ran and left a suite.log behind).
|
||||
if ran:
|
||||
self.assertIn("Product result: 1 passed, 1 failed, 1 skipped", contents)
|
||||
else:
|
||||
self.assertIn("Product result: 0 passed, 0 failed", contents)
|
||||
self.assertEqual(CASE_ROW in contents, mode == "present")
|
||||
self.assertEqual("CURRENT SUITE DIAGNOSTIC" in contents, mode == "present")
|
||||
self.assertEqual(CASE_ROW in contents, success)
|
||||
self.assertEqual("CURRENT SUITE DIAGNOSTIC" in contents, success)
|
||||
if mode == "present":
|
||||
raw = (self.artifacts / "suite-report.md").read_text()
|
||||
self.assertEqual(raw, f"CURRENT SUITE DIAGNOSTIC\n{CASE_ROW}\n")
|
||||
@@ -212,7 +190,7 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
expected = {self.artifacts / "report.md"}
|
||||
if outcome != "skipped" or mode == "present":
|
||||
expected.add(self.artifacts / "suite.log")
|
||||
self.assertIn("CURRENT SUITE STDOUT\nCURRENT SUITE STDERR", (self.artifacts / "suite.log").read_text())
|
||||
self.assertEqual((self.artifacts / "suite.log").read_text(), "CURRENT SUITE STDOUT\nCURRENT SUITE STDERR\n")
|
||||
if mode in ("present", "empty"):
|
||||
expected.add(self.artifacts / "suite-report.md")
|
||||
(self.artifacts / "unexpected-token.json").write_text("FAKE-SECRET-CANARY")
|
||||
@@ -290,14 +268,12 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
gh.chmod(0o755)
|
||||
body = self.directory / "issue-body.md"
|
||||
self.env.update(PATH=f"{fake_bin}{os.pathsep}{os.environ['PATH']}", CAPTURE_BODY=str(body))
|
||||
result = self.run_step("Manage backlog issues (dedup / label / auto-close)")
|
||||
result = self.run_step("File failure issue in rustfs/backlog")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
# The manager lives in the private auto-testing checkout; without it
|
||||
# the step must skip without publishing anything.
|
||||
self.assertIn("issue_manager.py not found", result.stdout + result.stderr)
|
||||
self.assertFalse(body.exists())
|
||||
self.assertNotIn("OLD RUN REPORT", body.read_text())
|
||||
self.assertIn("https://github.com/rustfs/rustfs/actions/runs/314159", body.read_text())
|
||||
|
||||
def test_all_ten_suites_hold_the_shared_lock_for_manual_and_chain_runs(self) -> None:
|
||||
def test_all_suites_hold_the_shared_lock_for_manual_and_chain_runs(self) -> None:
|
||||
for suite in ("upgrade", "s3-compat", "kms", "tier", "storage", "heal", "pool-expand", "security", "replication", "performance"):
|
||||
with self.subTest(suite=suite):
|
||||
source = (ROOT / f".github/workflows/rustfs-{suite}-test.yml").read_text().splitlines()
|
||||
@@ -307,7 +283,7 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
line.strip() for line in yaml_block(source, "concurrency", 0)
|
||||
if line.strip() and not line.lstrip().startswith("#")
|
||||
], [
|
||||
"group: rustfs-shared-functional-tests", "cancel-in-progress: false",
|
||||
"group: rustfs-shared-functional-tests-v2", "cancel-in-progress: false",
|
||||
])
|
||||
self.assertIsNotNone(yaml_block(source, "workflow_dispatch", 2))
|
||||
self.assertIsNotNone(yaml_block(source, "repository_dispatch", 2))
|
||||
@@ -315,7 +291,7 @@ class SecurityWorkflowTests(WorkflowSteps, unittest.TestCase):
|
||||
cleanup = named_steps(yaml_block(source, "jobs", 0))[cleanup_name]
|
||||
self.assertTrue(any(line.startswith(" if:") and "always()" in line for line in cleanup))
|
||||
|
||||
def test_root_dispatches_only_upgrade_and_replication_hands_off_after_failure(self) -> None:
|
||||
def test_legacy_replication_hands_off_after_failure(self) -> None:
|
||||
for failed_attempts, issue_exit, token in ((0, 0, "fixture"), (2, 0, "fixture"), (3, 0, "fixture"), (3, 7, "fixture"), (0, 0, "")):
|
||||
with self.subTest(failed_attempts=failed_attempts, issue_exit=issue_exit, token=bool(token)):
|
||||
self.setUp()
|
||||
@@ -361,21 +337,11 @@ fi
|
||||
RUSTFS_NIGHTLY_PACKAGE_URL="https://example.invalid/package.deb",
|
||||
)
|
||||
self.context.update({"secrets.PF_TESTING_GH_TOKEN": "fixture", "inputs.suite": "all"})
|
||||
driver = (ROOT / ".github/workflows/rustfs-functional-chain.yml").read_text()
|
||||
self.steps = named_steps(yaml_block(driver.splitlines(), "start-chain", 2))
|
||||
self.assertEqual(list(self.steps), ["Dispatch first suite (upgrade)"])
|
||||
started = self.run_step("Dispatch first suite (upgrade)")
|
||||
self.assertEqual(started.returncode, 0, started.stderr)
|
||||
self.assertEqual(dispatches.read_text().splitlines(), [
|
||||
"api --method POST repos/rustfs/rustfs/dispatches -f event_type=rustfs-chain-upgrade -F client_payload[from_suite]=nightly-build",
|
||||
])
|
||||
dispatches.unlink()
|
||||
|
||||
replication = (ROOT / ".github/workflows/rustfs-replication-test.yml").read_text()
|
||||
job = yaml_block(replication.splitlines(), "replication-test", 2)
|
||||
self.assertFalse(any(line.startswith(" continue-on-error:") for line in job))
|
||||
self.steps = named_steps(job)
|
||||
handoff = "Continue functional chain (next: Fault tolerance)"
|
||||
handoff = "Continue functional chain (next: Performance)"
|
||||
self.assertIn(" if: ${{ always() && github.event_name == 'repository_dispatch' }}", self.steps[handoff])
|
||||
self.assertFalse(any(line.strip().startswith("continue-on-error:") for line in self.steps[handoff]))
|
||||
self.assertIn(" if: always()", self.steps["Cleanup environment (after)"])
|
||||
@@ -395,11 +361,11 @@ fi
|
||||
self.assertEqual(forwarded.returncode == 0, bool(token) and failed_attempts < 3, forwarded.stderr)
|
||||
calls = dispatches.read_text().splitlines() if dispatches.exists() else []
|
||||
self.assertEqual(calls, [
|
||||
"api --method POST repos/rustfs/rustfs/dispatches -f event_type=rustfs-chain-fault-tolerance -F client_payload[from_suite]=replication",
|
||||
"api --method POST repos/rustfs/rustfs/dispatches -f event_type=rustfs-chain-performance -F client_payload[from_suite]=replication",
|
||||
] * (min(failed_attempts + 1, 3) if token else 0))
|
||||
if failed_attempts == 3:
|
||||
self.assertIn("could not hand off from **replication** to **Fault tolerance**", body.read_text())
|
||||
self.assertIn("rustfs-chain-fault-tolerance", body.read_text())
|
||||
self.assertIn("could not hand off from **replication** to **Performance**", body.read_text())
|
||||
self.assertIn("rustfs-chain-performance", body.read_text())
|
||||
self.assertEqual(executed.read_text().splitlines().count("issue"), 2 if issue_exit else 1)
|
||||
self.assertFalse(Path(body_path.read_text().strip()).exists())
|
||||
|
||||
@@ -417,6 +383,8 @@ class FunctionalWorkflowTests(unittest.TestCase):
|
||||
"replication": "Run replication suite",
|
||||
"table": "Run table suite",
|
||||
}
|
||||
# Suites following the new (release) gate semantics; see FunctionalEvidenceTests.
|
||||
NEW_SEMANTICS = ("table",)
|
||||
|
||||
def test_failure_and_always_step_wiring(self) -> None:
|
||||
for suite, job_id in self.JOBS.items():
|
||||
@@ -426,11 +394,11 @@ class FunctionalWorkflowTests(unittest.TestCase):
|
||||
self.assertIsNotNone(job)
|
||||
self.assertNotRegex("\n".join(job), r'''(?m)^ ["']?continue-on-error["']?\s*:''')
|
||||
steps = named_steps(job)
|
||||
if suite in self.DIRECT_TESTS:
|
||||
if suite in self.DIRECT_TESTS and suite not in self.NEW_SEMANTICS:
|
||||
# legacy semantics: the run step must fail the job; new-
|
||||
# semantics suites (table) keep it green via continue-on-error
|
||||
test = steps[self.DIRECT_TESTS[suite]]
|
||||
# Case failures keep the run green; the step records its
|
||||
# outcome for the report and the backlog issue manager.
|
||||
self.assertRegex("\n".join(test), r'''(?m)^ continue-on-error: true$''')
|
||||
self.assertNotRegex("\n".join(test), r'''(?m)^ ["']?continue-on-error["']?\s*:''')
|
||||
self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", steps["Generate report"])
|
||||
cleanup = steps["Reset test environment (after)" if suite == "performance" else "Cleanup environment (after)"]
|
||||
condition = next(line.strip() for line in cleanup if line.startswith(" if:"))
|
||||
@@ -550,7 +518,12 @@ printf '%s\n' '--- KMS-101 roundtrip ---' '[UNSUPPORTED] KMS-101'
|
||||
|
||||
|
||||
class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase):
|
||||
SUITES = (*FunctionalWorkflowTests.DIRECT_TESTS, "heal", "performance")
|
||||
# "table" follows the new (release) semantics (issue-manager step,
|
||||
# green on case failures) that main will adopt at the next release->main
|
||||
# sync. The legacy-semantics matrices below skip it via NEW_SEMANTICS;
|
||||
# wiring and allowlist matrices cover it through the full SUITES list.
|
||||
SUITES = (*FunctionalWorkflowTests.DIRECT_TESTS, "heal", "performance", "table")
|
||||
NEW_SEMANTICS = ("table",)
|
||||
|
||||
def prepare(self, suite: str) -> None:
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
@@ -598,10 +571,15 @@ class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase):
|
||||
self.prepare(suite)
|
||||
self.assertNotIn("/tmp/rustfs-", self.source)
|
||||
names = list(self.steps)
|
||||
self.assertLess(names.index("Initialize functional evidence"), names.index("Checkout auto-testing scripts (with retry)"))
|
||||
self.assertLess(names.index("Initialize functional evidence"), names.index("Checkout auto-testing scripts"))
|
||||
if suite in FunctionalWorkflowTests.DIRECT_TESTS:
|
||||
self.assertLess(names.index("Checkout repository (for report parser)"), names.index("Checkout auto-testing scripts (with retry)"))
|
||||
self.assertLess(names.index("Checkout repository (for report parser)"), names.index("Checkout auto-testing scripts"))
|
||||
for name, lines in self.steps.items():
|
||||
if name == "Upload chain evidence":
|
||||
self.assertIn(" if: ${{ always() && steps.chain_record.outcome == 'success' }}", lines)
|
||||
self.assertIn(" if: ${{ always() && inputs.chain_manifest != '' && steps.evidence.outcome == 'success' }}", self.steps["Record chain evidence"])
|
||||
self.assertIn(" if-no-files-found: error", lines)
|
||||
continue
|
||||
if name in ("Generate report", "Upload functional report to dashboard") or any("uses: actions/upload-artifact@" in line for line in lines):
|
||||
self.assertIn(" if: ${{ always() && steps.evidence.outcome == 'success' }}", lines)
|
||||
if any("uses: actions/upload-artifact@" in line for line in lines):
|
||||
@@ -615,14 +593,23 @@ class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase):
|
||||
initialized = self.run_step("Initialize functional evidence")
|
||||
self.assertNotEqual(initialized.returncode, 0)
|
||||
self.assertFalse(Path(self.env["GITHUB_ENV"]).exists())
|
||||
manager = self.run_step("Manage backlog issues (dedup / label / auto-close)")
|
||||
self.assertEqual(manager.returncode, 0, manager.stderr)
|
||||
self.assertIn("issue_manager.py not found", manager.stdout + manager.stderr)
|
||||
self.assertFalse(Path(self.env["CAPTURE_BODY"]).exists())
|
||||
if suite == "table":
|
||||
# New (release) semantics: the issue-manager step replaces
|
||||
# the legacy failure filing and must skip cleanly when the
|
||||
# token is absent; stale evidence stays untouched.
|
||||
manager = self.run_step("Manage backlog issues (dedup / label / auto-close)")
|
||||
self.assertEqual(manager.returncode, 0, manager.stderr)
|
||||
self.assertEqual((existing / "report.md").read_text(), "OLD RUN EVIDENCE")
|
||||
continue
|
||||
issue = self.run_step("File failure issue in rustfs/backlog")
|
||||
self.assertEqual(issue.returncode, 0, issue.stderr)
|
||||
body = Path(self.env["CAPTURE_BODY"]).read_text()
|
||||
self.assertNotIn("OLD RUN EVIDENCE", body)
|
||||
self.assertIn("no report or log file was produced", body)
|
||||
self.assertEqual((existing / "report.md").read_text(), "OLD RUN EVIDENCE")
|
||||
|
||||
def test_reports_use_only_current_complete_suite_evidence(self):
|
||||
for suite in self.SUITES[:-1]:
|
||||
for suite in (s for s in self.SUITES[:-1] if s not in self.NEW_SEMANTICS):
|
||||
good = "--- KMS-101 roundtrip ---\n[PASS] KMS-101\n"
|
||||
partial = "--- KMS-101 roundtrip ---\n[PASS] KMS-101\n--- KMS-102 unfinished ---\n"
|
||||
if suite == "s3-compat":
|
||||
@@ -647,14 +634,10 @@ class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase):
|
||||
self.context["steps.test.outcome"] = outcome
|
||||
report = self.run_step("Generate report")
|
||||
success = outcome == "success" and log == good
|
||||
# Report steps are red only for harness/environment breakdowns;
|
||||
# a failure outcome with recorded case rows stays green
|
||||
# (performance is unchanged and still gates on the suite result).
|
||||
green = (outcome == "success" or (outcome == "failure" and log in (good, partial))) if suite != "performance" else success
|
||||
self.assertEqual(report.returncode == 0, green, report.stderr)
|
||||
self.assertEqual(report.returncode == 0, success, report.stderr)
|
||||
contents = Path(self.env["REPORT_FILE"]).read_text()
|
||||
self.assertNotIn("OLD RUN EVIDENCE", contents)
|
||||
self.assertEqual("| PASS |" in contents, success if suite == "performance" else log in (good, partial))
|
||||
self.assertEqual("| PASS |" in contents, success)
|
||||
for value in ("actions/runs/314159", "Attempt: 2", "Workflow Commit: " + self.context["github.sha"],
|
||||
f"Test Step Outcome: {'success' if success else 'failure'}", f"Suite Step Outcome: {outcome}"):
|
||||
self.assertIn(value, contents)
|
||||
@@ -696,7 +679,8 @@ class FunctionalEvidenceTests(WorkflowSteps, unittest.TestCase):
|
||||
)
|
||||
result = self.run_step(name)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual((self.artifacts / "suite.log").read_text().splitlines()[-1], "CURRENT SUITE LOG")
|
||||
self.assertEqual((self.artifacts / "suite.log").read_text(), "CURRENT SUITE LOG\n")
|
||||
self.assertEqual(len(list(Path(self.env["TMPDIR"]).glob("fixture.*/trace.log"))), 1)
|
||||
self.assertEqual(list(self.artifacts.glob("fixture.*")), [])
|
||||
if suite == "performance":
|
||||
self.assertEqual((self.artifacts / "results/summary.md").read_text(), "CURRENT RESULTS\n")
|
||||
@@ -831,14 +815,11 @@ emit_step_result() {
|
||||
for failed_log in failed_logs:
|
||||
with self.subTest(log=failed_log):
|
||||
Path(self.env["LOG_FILE"]).write_text(failed_log)
|
||||
# A failing heal run exits non-zero; the report step stays green
|
||||
# because the steps ran, and the per-step table is always published.
|
||||
self.context["steps.test.outcome"] = "failure"
|
||||
report = self.run_step("Generate report")
|
||||
self.assertEqual(report.returncode, 0, report.stderr)
|
||||
self.assertNotEqual(report.returncode, 0, report.stderr)
|
||||
contents = Path(self.env["REPORT_FILE"]).read_text()
|
||||
self.assertIn("Test Step Outcome: failure", contents)
|
||||
self.assertIn("| PASS |", contents)
|
||||
self.assertNotIn("| PASS |", contents)
|
||||
if "original failure" in failed_log:
|
||||
self.assertIn("| 3 | original failure | FAIL |", (self.artifacts / "steps.md").read_text())
|
||||
if "later step failure" in failed_log:
|
||||
@@ -922,28 +903,3 @@ emit_step_result() {
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class TableSuiteTests(unittest.TestCase):
|
||||
"""Contract tests for the S3 Tables (Iceberg REST Catalog) suite workflow."""
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
def test_workflow_wiring(self) -> None:
|
||||
source = (self.ROOT / ".github/workflows/rustfs-table-test.yml").read_text()
|
||||
self.assertIn("group: rustfs-shared-functional-tests", source)
|
||||
self.assertIn("runs-on: smoke-testing", source)
|
||||
# New gate semantics: case failures stay green, harness breakdowns red.
|
||||
self.assertRegex(source, r"(?m)^ continue-on-error: true")
|
||||
self.assertIn("# Red only for harness/environment breakdowns; case failures stay green.", source)
|
||||
self.assertIn("HARNESS_OK", source)
|
||||
self.assertIn("Product result: ${CASES_PASS} passed, ${CASES_FAIL} failed", source)
|
||||
self.assertIn('python3 scripts/functional_case_report.py "${LOG_FILE}" "${CASE_TABLE}"', source)
|
||||
# Case rows come from the shared generator (ANSI-tolerant parser).
|
||||
# Issue manager, dashboard upload and artifacts use the shared contract.
|
||||
self.assertIn("--suite table --category table", source)
|
||||
self.assertIn("functional-reports/${SUITE}/${DATE}.md", source)
|
||||
self.assertIn("rustfs-table-test-${{ github.run_id }}-${{ github.run_attempt }}", source)
|
||||
# The suite is invoked with the product smoke script for TBL-101.
|
||||
self.assertIn("--smoke-script", source)
|
||||
self.assertIn("scripts/table-catalog/pyiceberg_smoke.py", source)
|
||||
|
||||
Reference in New Issue
Block a user