From cc143b52cf04be0b2f08350922a987b2e250bc1d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 25 Aug 2026 11:56:35 -0400 Subject: [PATCH] ci(TST-35): run gateway suite on windev nightly --- .gitea/workflows/ci.yml | 12 ++--- .../2026-08-19/remediation/00-tracking.md | 2 +- docs/GatewayTesting.md | 12 +++-- scripts/ci/README.md | 8 ++- scripts/ci/windev-worker-ci.ps1 | 9 +++- .../ProjectStructure/WindevCiContractTests.cs | 53 +++++++++++++++++++ 6 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 src/ZB.MOM.WW.MxGateway.Tests/ProjectStructure/WindevCiContractTests.cs diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 88f7e47..7c8ed20 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -4,16 +4,16 @@ # .NET Framework 4.8 / x86 and need MXAccess COM installed, so they build ONLY on a Windows host. # They are out of scope for this Linux `portable` job — the `windows-x86` job below builds and # tests them on windev (10.100.0.48) over SSH (see scripts/ci/run-windev-ci.sh), and the nightly -# `nightly-windev` job additionally runs the live-MXAccess smoke, which is scheduled-only so it -# never gates a push. +# `nightly-windev` job additionally runs the full solution build, gateway suite, and live-MXAccess +# smoke, which are scheduled-only so they never gate a push. name: ci on: push: pull_request: schedule: - # 06:00 UTC nightly: the `nightly-windev` job re-runs the x86 Worker build + full Worker.Tests on - # windev, then the live-MXAccess smoke (MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1) and a full-slnx build. + # 06:00 UTC nightly: x86 Worker build + Worker.Tests, full-slnx build, gateway tests, then the + # live-MXAccess smoke (MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1), serialized on windev's host lock. # Push/PR runs skip it — the job is gated `if: github.event_name == 'schedule'`. - cron: '0 6 * * *' @@ -182,7 +182,7 @@ jobs: run: ./scripts/ci/run-windev-ci.sh test nightly-windev: - # TST-25: scheduled deep run — full Worker.Tests + live-MXAccess smoke + full-slnx build on windev. + # TST-35: scheduled deep run — Worker, full-slnx, gateway, and live-smoke tiers on windev. # Gated to the schedule event via `if:` (not an unsatisfiable `runs-on`), so push/PR runs mark it # skipped, not queued. Never gates a push. On failure it opens a Gitea issue so a red nightly is # visible even though nobody watches the Actions page. @@ -198,7 +198,7 @@ jobs: PUBLIC_SERVER_URL: https://gitea.dohertylan.com steps: - uses: actions/checkout@v4 - - name: Full Worker.Tests + live-MXAccess smoke on windev + - name: Worker + gateway tests + live-MXAccess smoke on windev env: # WINDEV_SSH_KEY is stored base64-encoded (single line) so Gitea's line-oriented secret # masker redacts it in the step env echo; run-windev-ci.sh decodes it. A raw multiline PEM diff --git a/archreview/2026-08-19/remediation/00-tracking.md b/archreview/2026-08-19/remediation/00-tracking.md index 256159a..c21860f 100644 --- a/archreview/2026-08-19/remediation/00-tracking.md +++ b/archreview/2026-08-19/remediation/00-tracking.md @@ -111,7 +111,7 @@ The continuation runs serially in the isolated `.worktrees/c3-p2-followups` work | J — secondary contracts | IPC-36/37 | `gpt-5.6-sol` high | Done | Shared launch constants and descriptor parity tests green | | K — client alarm parity | CLI-47/48/49/50 | `gpt-5.6-terra` high | Done | All five client suites green | | L — live watch-list/proto wave | watch-list refresh follow-up + WRK-32 proto comment | `gpt-5.6-sol` high | Done | Codegen clean; portable alarm/contract tests green; windev Worker.Tests green | -| M — Windows gateway CI | TST-35 | `gpt-5.6-terra` medium | Not started | windev `live` mode proves Worker, gateway, full-slnx, and live smoke tiers | +| M — Windows gateway CI | TST-35 | `gpt-5.6-terra` medium | In progress | windev `live` mode proves Worker, gateway, full-slnx, and live smoke tiers | | N — integration and release | publish five clients + Contracts | `gpt-5.6-sol` high | Not started | Full local/windev gates; merged `main`; registry coordinates and Go tag verified | ### Continuation tasklist diff --git a/docs/GatewayTesting.md b/docs/GatewayTesting.md index ec8d771..5208bea 100644 --- a/docs/GatewayTesting.md +++ b/docs/GatewayTesting.md @@ -650,11 +650,13 @@ runtime because the x86 Worker cannot build on Linux: the tier is down, not the change**: an unreachable windev fails the job (never skips), so if it goes red without a related code change, check the tier — `ssh windev`, read `C:\build\mxaccessgw-ci\ci.log` — before assuming a real test failure. -- **`nightly-windev`** (Linux runner, scheduled `0 6 * * *`) — re-runs the full x86 build + - Worker.Tests on windev, then the live-MXAccess smoke (`MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1`) - and a full `slnx` build (which surfaces Windows-only analyzer diagnostics such as - `xUnit1030`). Gated `if: github.event_name == 'schedule'`, so it never gates a push. On - failure it opens a Gitea issue via the Actions token, since nobody watches the Actions page. +- **`nightly-windev`** (Linux runner, scheduled `0 6 * * *`) — runs, in order, the x86 Worker + build, `Worker.Tests`, a full `slnx` build (which surfaces Windows-only analyzer diagnostics + such as `xUnit1030`), the net10 gateway suite, and the live-MXAccess smoke + (`MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1`). The checkout and all five tiers share the windev + host lock, and the first nonzero native exit stops the sequence and propagates to the CI job. + Gated `if: github.event_name == 'schedule'`, so it never gates a push. On failure it opens a + Gitea issue via the Actions token, since nobody watches the Actions page. ### Runner capacity is shared and finite diff --git a/scripts/ci/README.md b/scripts/ci/README.md index aa69bd7..e7526f9 100644 --- a/scripts/ci/README.md +++ b/scripts/ci/README.md @@ -11,11 +11,15 @@ code back so a regression turns the job red. | File | Runs on | Role | |---|---|---| | `run-windev-ci.sh ` | Linux CI job | Writes the SSH key/known-hosts, base64-encodes a bootstrap, SSHes to windev, returns the remote exit code. | -| `windev-worker-ci.ps1 -Sha -Mode` | windev | Locks the CI clone, checks out the SHA, runs the x86 build / `Worker.Tests` / live smoke. | +| `windev-worker-ci.ps1 -Sha -Mode` | windev | Locks the CI clone, checks out the SHA, and runs the requested cumulative Windows tiers. | | `windev.known_hosts` | committed | Pinned windev host keys so `StrictHostKeyChecking=yes` has no TOFU prompt. Host keys are public; committing is intentional. | Modes are cumulative: `build` = x86 Worker build; `test` = build + `Worker.Tests`; -`live` = test + full-`slnx` build + live-MXAccess smoke (`MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1`). +`live` = test + full-`slnx` build + the net10 gateway suite + live-MXAccess smoke +(`MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1`). The `live` order is x86 Worker build, +`Worker.Tests`, full solution build, gateway tests, then live smoke. One host lock covers +checkout and every tier, so concurrent runs cannot overlap; each native command is checked +immediately and the first nonzero exit aborts the remaining tiers and propagates to CI. CI jobs (`.gitea/workflows/ci.yml`): `windows-x86` runs `test` per push/PR; `nightly-windev` runs `live` on the `0 6 * * *` schedule and opens a Gitea issue on failure. diff --git a/scripts/ci/windev-worker-ci.ps1 b/scripts/ci/windev-worker-ci.ps1 index 6d6044a..4c42c3c 100644 --- a/scripts/ci/windev-worker-ci.ps1 +++ b/scripts/ci/windev-worker-ci.ps1 @@ -16,8 +16,8 @@ Modes (cumulative): build x86 Worker build only (net48/CS0246/x86 compile guard — always fast). test build + Worker.Tests (x86). The per-push default. - live test + the live-MXAccess smoke (MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1) and a full - slnx build (surfaces Windows-only analyzer diagnostics, e.g. xUnit1030). Nightly. + live test + full slnx build + the net10 gateway suite + the live-MXAccess smoke + (MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1). Nightly. Runs under Windows PowerShell 5.1 (`powershell.exe`), so it avoids PS7-only syntax and checks $LASTEXITCODE after every native command rather than relying on stderr-as-error @@ -112,6 +112,7 @@ try { $workerProj = 'src/ZB.MOM.WW.MxGateway.Worker/ZB.MOM.WW.MxGateway.Worker.csproj' $workerTests = 'src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj' + $gatewayTests = 'src/ZB.MOM.WW.MxGateway.Tests/ZB.MOM.WW.MxGateway.Tests.csproj' $integrationTests = 'src/ZB.MOM.WW.MxGateway.IntegrationTests/ZB.MOM.WW.MxGateway.IntegrationTests.csproj' # build: always. The x86/net48 compile is the floor guard (catches CS0246 on @@ -127,6 +128,10 @@ try { # that never fire on the macOS/Linux NonWindows build. Invoke-Native 'full slnx build' { dotnet build src/ZB.MOM.WW.MxGateway.slnx -c Release } + Invoke-Native 'gateway tests on Windows' { + dotnet test $gatewayTests -c Release --no-build + } + $env:MXGATEWAY_RUN_LIVE_MXACCESS_TESTS = '1' try { Invoke-Native 'live-MXAccess smoke' { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/ProjectStructure/WindevCiContractTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/ProjectStructure/WindevCiContractTests.cs new file mode 100644 index 0000000..0d59a1e --- /dev/null +++ b/src/ZB.MOM.WW.MxGateway.Tests/ProjectStructure/WindevCiContractTests.cs @@ -0,0 +1,53 @@ +namespace ZB.MOM.WW.MxGateway.Tests.ProjectStructure; + +public sealed class WindevCiContractTests +{ + [Fact] + public void LiveMode_RunsWindowsTiersInDocumentedOrder() + { + DirectoryInfo repositoryRoot = FindRepositoryRoot(); + string script = File.ReadAllText( + Path.Combine(repositoryRoot.FullName, "scripts", "ci", "windev-worker-ci.ps1")); + + string[] orderedSteps = + [ + "Invoke-Native 'x86 Worker build'", + "Invoke-Native 'x86 Worker.Tests'", + "Invoke-Native 'full slnx build'", + "Invoke-Native 'gateway tests on Windows'", + "Invoke-Native 'live-MXAccess smoke'", + ]; + + int previousIndex = -1; + foreach (string step in orderedSteps) + { + int currentIndex = script.IndexOf(step, StringComparison.Ordinal); + Assert.True(currentIndex >= 0, $"Missing windev live-mode step: {step}"); + Assert.True(currentIndex > previousIndex, $"Windev live-mode step is out of order: {step}"); + previousIndex = currentIndex; + } + + Assert.Contains( + "dotnet test $gatewayTests -c Release --no-build", + script, + StringComparison.Ordinal); + } + + private static DirectoryInfo FindRepositoryRoot() + { + DirectoryInfo? current = new(AppContext.BaseDirectory); + while (current is not null) + { + if (File.Exists(Path.Combine(current.FullName, "CLAUDE.md")) + && Directory.Exists(Path.Combine(current.FullName, "src")) + && Directory.Exists(Path.Combine(current.FullName, "scripts"))) + { + return current; + } + + current = current.Parent; + } + + throw new InvalidOperationException("Could not locate the repository root from the test output directory."); + } +}