Shared Rust alert engine: golden corpus, nocturne-alerts-core, FFI bindings, backend seam #622
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tests | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: tests-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| unit: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0" | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "24" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| - name: Cache NuGet | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }} | |
| - name: Test | |
| run: | | |
| # Build per-project to avoid Windows-only projects (Desktop, Widgets) | |
| for proj in tests/Unit/*/; do | |
| csproj=$(find "$proj" -maxdepth 1 -name "*.csproj" | head -1) | |
| [ -z "$csproj" ] && continue | |
| dotnet test "$csproj" \ | |
| --filter "Category!=Integration&Category!=Performance&Category!=E2E" \ | |
| --logger "trx;LogFileName=unit.trx" \ | |
| -p:GenerateNSwagClient=false \ | |
| || exit 1 | |
| done | |
| - uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: unit-trx | |
| path: "**/*.trx" | |
| # The alert-engine golden corpus (tests/Parity/AlertEngineCorpus) is the normative | |
| # spec for the Rust port of the leaf/node evaluation engine. While the C# engine is | |
| # authoritative, any behavioural change to the evaluators/tracker must regenerate the | |
| # corpus deliberately — this job fails if the committed corpus no longer matches what | |
| # the engine produces. | |
| alert-corpus-check: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0" | |
| - name: Cache NuGet | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }} | |
| - name: Verify alert engine corpus is current | |
| run: | | |
| dotnet run --project tests/Parity/Nocturne.Alerts.ParityCorpus.Generator \ | |
| -p:GenerateNSwagClient=false -- --check | |
| # Rust alert engine FFI: lints + tests the crates workspace, builds the | |
| # nocturne_alerts cdylib, then runs the three-way parity suite (C# engine vs | |
| # committed corpus vs Rust engine over the C ABI). NOCTURNE_ALERTS_REQUIRE_NATIVE | |
| # turns silent skips (native lib not found) into a hard failure so the parity | |
| # theories can never quietly stop running here. | |
| alerts-ffi-parity: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0" | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| crates/target | |
| key: cargo-${{ runner.os }}-${{ hashFiles('crates/Cargo.lock') }} | |
| - name: Cache NuGet | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }} | |
| - name: Rust tests, clippy, fmt | |
| working-directory: crates | |
| run: | | |
| cargo test --workspace | |
| cargo clippy --workspace --all-targets -- -D warnings | |
| cargo fmt --check | |
| - name: Build nocturne_alerts cdylib | |
| working-directory: crates | |
| run: cargo build --release -p nocturne-alerts-ffi | |
| - name: Three-way parity tests (C# engine vs corpus vs Rust FFI) | |
| env: | |
| NOCTURNE_ALERTS_NATIVE_DIR: ${{ github.workspace }}/crates/target/release | |
| NOCTURNE_ALERTS_REQUIRE_NATIVE: "1" | |
| run: | | |
| dotnet test tests/Unit/Nocturne.Alerts.Native.Tests/Nocturne.Alerts.Native.Tests.csproj \ | |
| --logger "trx;LogFileName=alerts-ffi-parity.trx" \ | |
| -p:GenerateNSwagClient=false \ | |
| || exit 1 | |
| # Engine-seam tests (IAlertEvaluationEngine): corpus parity for both the managed and | |
| # rust-backed seam adapters, cross-engine timer continuity, shadow-mode divergence | |
| # logging, and Alerts:Engine flag selection. The rust-backed theories require the | |
| # cdylib built above; NOCTURNE_ALERTS_REQUIRE_NATIVE turns silent skips into failures. | |
| - name: Engine seam tests (managed + rust adapters, shadow, flag) | |
| env: | |
| NOCTURNE_ALERTS_NATIVE_DIR: ${{ github.workspace }}/crates/target/release | |
| NOCTURNE_ALERTS_REQUIRE_NATIVE: "1" | |
| Alerts__Engine: rust | |
| run: | | |
| dotnet test tests/Unit/Nocturne.API.Tests/Nocturne.API.Tests.csproj \ | |
| --filter "FullyQualifiedName~Services.Alerts.Engines" \ | |
| --logger "trx;LogFileName=alerts-engine-seam.trx" \ | |
| -p:GenerateNSwagClient=false \ | |
| || exit 1 | |
| - uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: alerts-ffi-parity-trx | |
| path: "**/*.trx" | |
| # Database integration tests: bare-Postgres Testcontainers (no Aspire), so they | |
| # run reliably on GitHub-hosted runners where Docker is available. Other | |
| # integration projects stay deferred until their fixtures/references are fixed. | |
| integration-db: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0" | |
| - name: Cache NuGet | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.nuget/packages | |
| key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }} | |
| - name: Integration tests (Infrastructure.Data — RLS + DataProtection) | |
| run: | | |
| dotnet test tests/Integration/Nocturne.Infrastructure.Data.Tests/Nocturne.Infrastructure.Data.Tests.csproj \ | |
| --filter "Category!=Performance&Category!=E2E" \ | |
| --logger "trx;LogFileName=integration-db.trx" \ | |
| -p:GenerateNSwagClient=false \ | |
| || exit 1 | |
| - uses: actions/upload-artifact@v4 | |
| if: failure() | |
| with: | |
| name: integration-db-trx | |
| path: "**/*.trx" | |
| # E2E tests deferred: Aspire.Hosting.Testing hangs on GitHub Actions runners — | |
| # DCP resource orchestration never completes. Tests pass locally. Revisit when | |
| # Aspire CI support matures or move to a self-hosted runner. |