- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation. - Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments. - Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats. - Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats. - Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction. - Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
5.1 KiB
Policy Determinism Test Design
Document ID: DESIGN-POLICY-DETERMINISM-TESTS-001
Version: 1.0
Status: Published
Last Updated: 2025-12-06
Overview
This document defines the test expectations for ensuring deterministic output from Policy Engine scoring and decision APIs. Determinism is critical for reproducible policy evaluation across environments.
Determinism Requirements
Output Ordering
All collections in API responses must have stable, deterministic ordering:
| Collection | Ordering Rule |
|---|---|
| Findings | By finding_id alphabetically |
| Decisions | By decision_id (timestamp prefix) |
| Signals | By signal name alphabetically |
| Severity counts | By canonical severity order: critical → high → medium → low → info |
| Contributions | By signal name alphabetically |
JSON Serialization
- Property Order: Use
[JsonPropertyOrder]or declare properties in stable order - No Random Elements: No GUIDs, random IDs, or timestamps unless from context
- Stable Key Order: Dictionary keys must serialize in consistent order
Deprecated Field Absence
After v2.0, responses must NOT include:
normalized_scoretop_severity_sourcessource_rank
Test Categories
1. Snapshot Equality Tests
Verify that identical inputs produce byte-for-byte identical JSON outputs.
[Theory]
[MemberData(nameof(DeterminismFixtures))]
public void Scoring_ShouldProduceDeterministicOutput(string inputFile)
{
// Arrange
var input = LoadFixture(inputFile);
// Act - Run twice with same input
var result1 = _scoringService.Score(input);
var result2 = _scoringService.Score(input);
// Assert - Byte-for-byte equality
var json1 = JsonSerializer.Serialize(result1);
var json2 = JsonSerializer.Serialize(result2);
Assert.Equal(json1, json2);
}
2. Cross-Environment Tests
Verify output is identical across different environments (CI, local, prod).
[Theory]
[InlineData("fixture-001")]
public void Scoring_ShouldMatchGoldenFile(string fixtureId)
{
// Arrange
var input = LoadFixture($"{fixtureId}-input.json");
var expected = LoadFixture($"{fixtureId}-expected.json");
// Act
var result = _scoringService.Score(input);
// Assert
AssertJsonEqual(expected, result);
}
3. Ordering Verification Tests
Verify collections are always in expected order.
[Fact]
public void Decisions_ShouldBeOrderedByDecisionId()
{
// Arrange
var input = CreateTestInput();
// Act
var result = _decisionService.Evaluate(input);
// Assert
Assert.True(result.Decisions.SequenceEqual(
result.Decisions.OrderBy(d => d.DecisionId)));
}
4. Deprecated Field Absence Tests
Verify deprecated fields are not serialized (v2.0+).
[Fact]
public void ScoringResult_ShouldNotIncludeNormalizedScore_InV2()
{
// Arrange
var result = new RiskScoringResult(...);
var options = new PolicyScoringOptions { IncludeLegacyNormalizedScore = false };
// Act
var json = JsonSerializer.Serialize(result, CreateJsonOptions(options));
var doc = JsonDocument.Parse(json);
// Assert
Assert.False(doc.RootElement.TryGetProperty("normalized_score", out _));
}
Fixture Structure
Input Fixtures
Located at docs/modules/policy/samples/policy-determinism-fixtures*.json:
{
"$schema": "https://stellaops.org/schemas/policy/determinism-fixture-v1.json",
"fixture_id": "DET-001",
"description": "Basic scoring determinism test",
"input": {
"finding_id": "CVE-2024-1234",
"signals": {
"cvss_base": 7.5,
"exploitability": 2.8
}
},
"expected_output": {
"severity": "high",
"signal_order": ["cvss_base", "exploitability"]
}
}
Golden Files
Pre-computed expected outputs stored alongside inputs:
policy-determinism-fixtures-input.jsonpolicy-determinism-fixtures-expected.json
CI Integration
Pipeline Steps
- Build: Compile with analyzers
- Unit Tests: Run determinism unit tests
- Snapshot Tests: Compare against golden files
- Diff Check: Fail if any unexpected changes
GitHub Action
- name: Run Determinism Tests
run: |
dotnet test --filter "Category=Determinism"
- name: Verify Snapshots
run: |
dotnet run --project tools/SnapshotVerifier -- \
--fixtures docs/modules/policy/samples/policy-determinism-*.json
Maintenance
Updating Golden Files
When intentionally changing output format:
- Update design docs (this file, normalized-field-removal.md)
- Re-run tests with
UPDATE_GOLDEN=true - Review diffs
- Commit new golden files with explanation
Adding New Fixtures
- Create input fixture in
samples/ - Run scoring to generate expected output
- Review for correctness
- Add to test data provider