diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c474a8..ed0c588 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,75 @@ jobs: name: packages path: ./artifacts/*.nupkg + # Measures test coverage and publishes it to Codacy (OPS-157454). + # pdl-public, not a GitHub-hosted runner and not pdl-prod-cluster. This repository is public, so a + # fork pull request is untrusted code from a stranger: pdl-prod-cluster runs a privileged + # Docker-in-Docker sidecar with hostPath mounts on a production node and must never run it. + # pdl-public is the hardened scale set that may: no dind, no privileged container, no hostPath. + coverage: + runs-on: [pdl-public] + timeout-minutes: 30 + + # The actions-runner image our scale sets use does not offer the Node version several of these + # actions request by default; without this they fail before their first step runs. + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + DOTNET_NOLOGO: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Nerdbank.GitVersioning needs the history to compute a version. + + # The runner user cannot write /usr/share/dotnet, so the SDK goes under the job's temp directory. + - name: Setup .NET + uses: actions/setup-dotnet@v6 + env: + DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet + with: + dotnet-version: '10.0.x' + + - name: Build tests + run: dotnet build AutoTask.Psa.Api.Test/AutoTask.Psa.Api.Test.csproj --configuration Debug + + # The test project is a Microsoft.Testing.Platform application (UseMicrosoftTestingPlatformRunner), + # so coverage is collected by running it directly with Microsoft.Testing.Extensions.CodeCoverage + # rather than through `dotnet test`. On Linux the executable has no .exe extension. The runner + # image ships no pwsh, hence plain bash. + # + # CI runs only the tests that need no credentials. Every class deriving from TestBase calls the + # live AutoTask API with credentials from user secrets, which CI does not hold; TestBase carries + # [Trait("Category", "Integration")] and they are excluded here. xunit.runner.json sets + # failSkips: true, so leaving them in would turn this job red rather than skipping them. + - name: Run tests with coverage + run: >- + ./AutoTask.Psa.Api.Test/bin/Debug/net10.0/AutoTask.Psa.Api.Test + --coverage + --coverage-settings coverage.config + --coverage-output-format cobertura + --coverage-output coverage.cobertura.xml + --filter "Category!=Integration" + + # continue-on-error: a Codacy outage must not turn a passing test run red. The trade-off is that + # an expired or missing project token fails silently, so check the log for + # "Coverage received successfully". + - name: Upload coverage to Codacy + continue-on-error: true + uses: codacy/codacy-coverage-reporter-action@v1 + with: + project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} + coverage-reports: AutoTask.Psa.Api.Test/bin/Debug/net10.0/TestResults/coverage.cobertura.xml + + - name: Upload coverage artifact + if: always() + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: coverage + path: AutoTask.Psa.Api.Test/bin/Debug/net10.0/TestResults/coverage.cobertura.xml + retention-days: 7 + publish: needs: build runs-on: ubuntu-latest diff --git a/AutoTask.Psa.Api.Test/AutoTask.Psa.Api.Test.csproj b/AutoTask.Psa.Api.Test/AutoTask.Psa.Api.Test.csproj index 8d8ee76..51cba72 100644 --- a/AutoTask.Psa.Api.Test/AutoTask.Psa.Api.Test.csproj +++ b/AutoTask.Psa.Api.Test/AutoTask.Psa.Api.Test.csproj @@ -2,6 +2,7 @@ net10.0 + true enable latest enable diff --git a/AutoTask.Psa.Api.Test/TestBase.cs b/AutoTask.Psa.Api.Test/TestBase.cs index abef043..0522b35 100644 --- a/AutoTask.Psa.Api.Test/TestBase.cs +++ b/AutoTask.Psa.Api.Test/TestBase.cs @@ -2,7 +2,10 @@ namespace AutoTask.Psa.Api.Test; +// Every test deriving from this class calls the live AutoTask API with credentials from user +// secrets, which CI does not have. CI excludes them with --filter "Category!=Integration". [CollectionDefinition("Dependency Injection")] +[Trait("Category", "Integration")] public class TestBase : TestBed { public AutoTaskClient AutoTaskClient { get; } diff --git a/coverage.config b/coverage.config new file mode 100644 index 0000000..cf54802 --- /dev/null +++ b/coverage.config @@ -0,0 +1,33 @@ + + + + + + + .*Test\.dll$ + .*Tests\.dll$ + + + + + ^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$ + ^System\.Runtime\.CompilerServices\.CompilerGeneratedAttribute$ + ^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$ + ^System\.ObsoleteAttribute$ + + + + + .*\\obj\\.* + + + False + True + +