Files
breakpilot-compliance/backend-compliance/tests/test_company.py
T
Benjamin Admin 8c893ca783 feat(company): Company Intelligence 2A — Company Capability Profile foundation
HEAD of the spine Company->Capability->Product->Regulation->Obligation->Procedure
->Evidence. New compliance/company/ package: CompanyContext container + a four-state
trust model (declared/inferred/confirmed/unknown).

Hard rule (structural): a certification yields at most an INFERRED candidate and is
never auto-treated as CONFIRMED/"erfuellt". A certification produces evidence-of-
capability; only real ExistingEvidence promotes a capability to CONFIRMED.

Ownership: Reasoning owns the container + trust-state; the Certification->Capability
mapping is Execution's domain, consumed via an injected contract. No mapping data in
product code (tests inject mocks). No endpoint/UI/RAG/new regs/controls; no meta-model
classes (freeze v1.0 untouched). 8 tests; mypy --strict clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 14:59:42 +02:00

128 lines
5.6 KiB
Python

"""Tests for Company Intelligence (Phase 2A) — Company Capability Profile.
Acceptance: from a CompanyContext (certifications, declarations, evidence) the
engine derives operational capabilities with a four-state trust model and a HARD
RULE: a certification is NEVER auto-treated as "erfuellt" — at most INFERRED.
The Certification->Capability mapping is Execution's domain. It is injected here as
a MOCK (the yaml-like dict below lives ONLY in tests); product code ships no table.
"""
from __future__ import annotations
from compliance.company import (
CapabilityMappingEntry,
Certification,
CompanyContext,
Declaration,
ExistingEvidence,
VerificationStatus,
build_company_profile,
)
from compliance.reasoning.enums import Confidence
# --- MOCK mapping (Execution-owned in reality; here only for the tests) -------
# mapping:
# ISO27001 -> [cap_patch_management, cap_supplier_management]
MOCK_MAPPING = {
"ISO27001": CapabilityMappingEntry(
capability_ids=["cap_patch_management", "cap_supplier_management"],
confidence=Confidence.MEDIUM,
)
}
def _candidate(profile, capability_id):
return [c for c in profile.candidate_capabilities if c.capability_id == capability_id]
def _confirmed_ids(profile):
return {c.capability_id for c in profile.confirmed_capabilities}
# A certification yields INFERRED candidates via the injected mapping.
def test_certification_infers_candidates_via_injected_mapping():
ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
profile = build_company_profile(ctx, MOCK_MAPPING)
ids = {c.capability_id for c in profile.candidate_capabilities}
assert ids == {"cap_patch_management", "cap_supplier_management"}
for c in profile.candidate_capabilities:
assert c.verification_status == VerificationStatus.INFERRED
assert c.source == "certification:ISO27001"
# Without an injected mapping there are NO inferred capabilities — only the claim.
# This is the architectural guarantee that the table lives only in Execution.
def test_no_mapping_no_inferred_capabilities():
ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
profile = build_company_profile(ctx) # default EMPTY mapping
assert profile.candidate_capabilities == []
# the certification still produced evidence-of-claim (refinement 1)
assert len(profile.capability_evidence) == 1
assert profile.capability_evidence[0].source == "certification:ISO27001"
assert profile.capability_evidence[0].certification_id == "ISO27001"
# A customer declaration yields a DECLARED candidate.
def test_declaration_yields_declared_candidate():
ctx = CompanyContext(company_id="acme", declarations=[Declaration(capability_id="cap_patch_management")])
profile = build_company_profile(ctx, MOCK_MAPPING)
cands = _candidate(profile, "cap_patch_management")
assert len(cands) == 1
assert cands[0].verification_status == VerificationStatus.DECLARED
# declared + inferred coexist as distinct signals for the same capability.
def test_declared_and_inferred_coexist():
ctx = CompanyContext(
company_id="acme",
certifications=[Certification(certification_id="ISO27001")],
declarations=[Declaration(capability_id="cap_patch_management")],
)
profile = build_company_profile(ctx, MOCK_MAPPING)
statuses = {c.verification_status for c in _candidate(profile, "cap_patch_management")}
assert statuses == {VerificationStatus.DECLARED, VerificationStatus.INFERRED}
# HARD RULE: a certification alone NEVER yields a confirmed capability.
def test_hard_rule_certification_never_confirmed():
ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
profile = build_company_profile(ctx, MOCK_MAPPING)
assert _confirmed_ids(profile) == set()
for c in profile.candidate_capabilities:
assert c.verification_status != VerificationStatus.CONFIRMED
# Only real evidence confirms a capability — and it leaves the candidate list.
def test_evidence_confirms_capability():
ctx = CompanyContext(
company_id="acme",
certifications=[Certification(certification_id="ISO27001")],
evidence=[ExistingEvidence(evidence_id="pol-1", evidence_type="policy", proves_capability_id="cap_patch_management")],
)
profile = build_company_profile(ctx, MOCK_MAPPING)
assert "cap_patch_management" in _confirmed_ids(profile)
confirmed = [c for c in profile.confirmed_capabilities if c.capability_id == "cap_patch_management"][0]
assert confirmed.verification_status == VerificationStatus.CONFIRMED
assert confirmed.confidence == Confidence.HIGH
assert confirmed.sources == ["pol-1"]
# a confirmed capability is no longer a mere candidate
assert _candidate(profile, "cap_patch_management") == []
# the un-proven capability stays an inferred candidate
assert _candidate(profile, "cap_supplier_management")[0].verification_status == VerificationStatus.INFERRED
# The four-state vocabulary exists and is ordered declared->inferred->confirmed (+unknown).
def test_four_states_present():
assert {s.value for s in VerificationStatus} == {"declared", "inferred", "confirmed", "unknown"}
# verification_status is a FOURTH vocabulary, disjoint from ClaimCoverage and DeltaType.
def test_verification_status_distinct_vocabulary():
from compliance.rci.schemas import DeltaType
from compliance.reasoning.enums import ClaimCoverage
verif = {s.value for s in VerificationStatus}
assert verif.isdisjoint({c.value for c in ClaimCoverage})
assert verif.isdisjoint({d.value for d in DeltaType})