test: add coverage for uncovered CoreIdent.Core models, stores, and i… #92
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: .NET | |
| on: | |
| push: | |
| branches: ["main"] | |
| pull_request: | |
| branches: ["main"] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| build_and_test: | |
| name: Build and Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "10.0.x" | |
| dotnet-quality: "preview" | |
| cache: true | |
| cache-dependency-path: | | |
| **/*.csproj | |
| **/*.props | |
| **/*.targets | |
| **/packages.lock.json | |
| - name: Restore | |
| run: dotnet restore CoreIdent.sln | |
| - name: Build | |
| run: dotnet build CoreIdent.sln -c Release --no-restore | |
| - name: Test (with coverage, excluding E2E) | |
| run: | | |
| dotnet test tests/CoreIdent.Core.Tests -c Release --no-build --collect:"XPlat Code Coverage" --logger "console;verbosity=minimal" | |
| dotnet test tests/CoreIdent.Integration.Tests -c Release --no-build --collect:"XPlat Code Coverage" --logger "console;verbosity=minimal" | |
| dotnet test tests/CoreIdent.Cli.Tests -c Release --no-build --logger "console;verbosity=minimal" | |
| dotnet test tests/CoreIdent.Templates.Tests -c Release --no-build --logger "console;verbosity=minimal" | |
| - name: OpenAPI Validation Gate | |
| run: dotnet test tests/CoreIdent.Integration.Tests/CoreIdent.Integration.Tests.csproj -c Release --no-build --filter "FullyQualifiedName~CoreIdent.Integration.Tests.Infrastructure.OpenApiFixtureTests" --logger "console;verbosity=minimal" | |
| - name: Coverage Gate (CoreIdent.Core >= 90%) | |
| run: | | |
| python3 - <<'PY' | |
| import xml.etree.ElementTree as ET | |
| from pathlib import Path | |
| import glob | |
| from collections import defaultdict | |
| THRESHOLD = 90.0 | |
| def norm(p: str) -> str: | |
| return p.replace('\\\\', '/') | |
| def canonical(p: str) -> str: | |
| p = norm(p) | |
| if p.startswith('src/'): | |
| p = p[4:] | |
| return p | |
| candidates = [Path(p) for p in glob.glob('tests/*/TestResults/*/coverage.cobertura.xml')] | |
| if not candidates: | |
| raise SystemExit('No coverage.cobertura.xml files found under tests/*/TestResults/*/') | |
| by_project = defaultdict(list) | |
| for p in candidates: | |
| parts = p.parts | |
| idx = parts.index('tests') | |
| project = parts[idx + 1] | |
| by_project[project].append(p) | |
| selected = {project: max(files, key=lambda x: x.stat().st_mtime) for project, files in by_project.items()} | |
| canon_files = defaultdict(dict) # canonical filename -> {line:int -> covered:bool} | |
| for p in selected.values(): | |
| root = ET.parse(p).getroot() | |
| for cls in root.findall('.//class'): | |
| fn = cls.attrib.get('filename') | |
| if not fn: | |
| continue | |
| fn = canonical(fn) | |
| lines_node = cls.find('lines') | |
| if lines_node is None: | |
| continue | |
| m = canon_files[fn] | |
| for ln in lines_node.findall('line'): | |
| num = ln.attrib.get('number') | |
| hits = ln.attrib.get('hits') | |
| if num is None or hits is None: | |
| continue | |
| try: | |
| n = int(num) | |
| h = int(hits) | |
| except ValueError: | |
| continue | |
| cov = h > 0 | |
| prev = m.get(n) | |
| m[n] = cov if prev is None else (prev or cov) | |
| core = {fn: m for fn, m in canon_files.items() if fn.startswith('CoreIdent.Core/')} | |
| valid = sum(len(m) for m in core.values()) | |
| covered = sum(1 for m in core.values() for v in m.values() if v) | |
| rate = (covered / valid * 100.0) if valid else 0.0 | |
| print(f'Normalized merged CoreIdent.Core line coverage: {covered}/{valid} = {rate:.2f}%') | |
| if rate + 1e-9 < THRESHOLD: | |
| raise SystemExit(f'Coverage gate failed: CoreIdent.Core line coverage {rate:.2f}% is below {THRESHOLD:.2f}%') | |
| PY |