Skip to content

Commit 016e9c6

Browse files
authored
Merge pull request #2 from SharedIntellect/fix/verdict-json-none
test: regression tests for verdict.json serialization (never None)
2 parents 68b357a + 7a44960 commit 016e9c6

2 files changed

Lines changed: 57 additions & 2 deletions

File tree

.github/workflows/quorum-validate.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SPDX-License-Identifier: MIT
22
# Quorum Self-Validation CI Gate
33
# Runs standard-depth validation on changed files for every PR to main.
4-
# Requires ANTHROPIC_API_KEY in repository secrets.
4+
# Requires ANTHROPIC_API_KEY_GH_SHAREDIN_QUORUM in repository secrets.
55

66
name: Quorum Validation
77

@@ -79,7 +79,7 @@ jobs:
7979
if: steps.changes.outputs.skip != 'true'
8080
working-directory: reference-implementation
8181
env:
82-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
82+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY_GH_SHAREDIN_QUORUM }}
8383
run: |
8484
DEPTH="${{ github.event.inputs.depth || 'standard' }}"
8585
MAX_COST="${{ github.event.inputs.max_cost || '5.00' }}"

reference-implementation/tests/test_aggregator.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,3 +394,58 @@ def test_full_run_empty_results(self, aggregator):
394394
verdict = aggregator.run([])
395395
assert verdict.status == VerdictStatus.PASS
396396
assert verdict.confidence == 0.0
397+
398+
def test_verdict_json_serialization_never_none(self, aggregator):
399+
"""Regression test: verdict.json must always have a valid status string,
400+
never None. See: fix/verdict-json-none."""
401+
import json
402+
403+
# Test all verdict paths
404+
test_cases = [
405+
([], VerdictStatus.PASS), # empty → PASS
406+
([make_critic_result("c", [make_finding(severity=Severity.INFO)])],
407+
VerdictStatus.PASS_WITH_NOTES),
408+
([make_critic_result("c", [make_finding(severity=Severity.HIGH)])],
409+
VerdictStatus.REVISE),
410+
([make_critic_result("c", [make_finding(severity=Severity.CRITICAL)])],
411+
VerdictStatus.REJECT),
412+
]
413+
414+
valid_statuses = {s.value for s in VerdictStatus}
415+
416+
for critic_results, expected_status in test_cases:
417+
verdict = aggregator.run(critic_results)
418+
419+
# Verify the object
420+
assert verdict.status == expected_status
421+
assert verdict.status is not None
422+
423+
# Verify serialization (what gets written to verdict.json)
424+
dumped = verdict.model_dump()
425+
assert dumped["status"] is not None, f"verdict.json status is None for {expected_status}"
426+
assert dumped["status"] in valid_statuses, f"verdict.json status '{dumped['status']}' not in {valid_statuses}"
427+
428+
# Verify round-trip through JSON (what gets read back)
429+
json_str = json.dumps(dumped)
430+
loaded = json.loads(json_str)
431+
assert loaded["status"] is not None
432+
assert loaded["status"] in valid_statuses
433+
434+
def test_verdict_fallback_serialization(self, aggregator):
435+
"""Regression test: even the aggregator-crash fallback path must serialize
436+
a valid status, never None."""
437+
import json
438+
439+
fallback = Verdict(
440+
status=VerdictStatus.REJECT,
441+
reasoning="Aggregator failed: test",
442+
confidence=0.0,
443+
report=None,
444+
)
445+
dumped = fallback.model_dump()
446+
assert dumped["status"] == "REJECT"
447+
assert dumped["status"] is not None
448+
449+
json_str = json.dumps(dumped)
450+
loaded = json.loads(json_str)
451+
assert loaded["status"] == "REJECT"

0 commit comments

Comments
 (0)