Files
breakpilot-compliance/ai-compliance-sdk/internal/usecase/compiler_test.go
T
Benjamin Admin 06bfbd1dca
Build + Deploy / build-admin-compliance (push) Successful in 2m46s
Build + Deploy / build-backend-compliance (push) Successful in 26s
Build + Deploy / build-ai-sdk (push) Successful in 52s
Build + Deploy / build-developer-portal (push) Successful in 22s
Build + Deploy / build-tts (push) Successful in 16s
Build + Deploy / build-document-crawler (push) Successful in 12s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
Build + Deploy / build-dsms-node (push) Successful in 16s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 18s
CI / secret-scan (push) Has been skipped
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m16s
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / test-go (push) Successful in 1m0s
CI / test-python-backend (push) Successful in 41s
CI / test-python-document-crawler (push) Successful in 29s
CI / test-python-dsms-gateway (push) Successful in 23s
CI / validate-canonical-controls (push) Successful in 16s
Build + Deploy / trigger-orca (push) Successful in 2m36s
feat(use-case-compiler): MC-based compliance questionnaires with scoring
Implements the Use-Case Compiler that turns Master Controls into
interactive compliance audits. 5 templates (Vendor Check, SAST/DAST,
DSGVO, NIS2, CRA), deterministic + LLM question generation, scoring
engine with regulation/severity breakdown, and gap detection.

- Backend: 9 API endpoints, 22 unit tests (all pass)
- Frontend: Template selector, questionnaire, result dashboard
- Migration 027: usecase_audits + usecase_answers tables

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-12 13:49:16 +02:00

187 lines
4.1 KiB
Go

package usecase
import (
"testing"
)
func TestTemplates_AllPresent(t *testing.T) {
expected := []string{
"vendor_check_cloud",
"sast_dast_audit",
"dsgvo_quick_check",
"nis2_readiness",
"cra_product_check",
}
for _, id := range expected {
if _, ok := Templates[id]; !ok {
t.Errorf("Template %q missing from Templates map", id)
}
}
}
func TestTemplates_HaveQuestions(t *testing.T) {
for id, tmpl := range Templates {
if len(tmpl.Questions) == 0 {
t.Errorf("Template %q has no pre-defined questions", id)
}
if tmpl.Name == "" {
t.Errorf("Template %q has no name", id)
}
if len(tmpl.MCFilters) == 0 {
t.Errorf("Template %q has no MC filters", id)
}
if len(tmpl.Regulations) == 0 {
t.Errorf("Template %q has no regulations", id)
}
}
}
func TestTemplates_QuestionIDs_Unique(t *testing.T) {
for id, tmpl := range Templates {
seen := make(map[string]bool)
for _, q := range tmpl.Questions {
if seen[q.ID] {
t.Errorf("Template %q has duplicate question ID %q", id, q.ID)
}
seen[q.ID] = true
}
}
}
func TestTemplateList_ReturnsAll(t *testing.T) {
list := TemplateList()
if len(list) != len(Templates) {
t.Errorf("TemplateList returned %d, expected %d", len(list), len(Templates))
}
}
func TestDeriveQuestion(t *testing.T) {
tests := []struct {
input string
want string
}{
{"encryption_at_rest", "Ist 'Encryption At Rest' implementiert und dokumentiert?"},
{"access_control", "Ist 'Access Control' implementiert und dokumentiert?"},
}
for _, tt := range tests {
got := deriveQuestion(tt.input)
if got != tt.want {
t.Errorf("deriveQuestion(%q) = %q, want %q", tt.input, got, tt.want)
}
}
}
func TestSplitCriteria(t *testing.T) {
tests := []struct {
input string
expect int
}{
{"", 0},
{"Single criteria", 1},
{"A | B | C", 3},
{"A|B", 2},
}
for _, tt := range tests {
got := splitCriteria(tt.input)
if got == nil && tt.expect == 0 {
continue
}
if len(got) != tt.expect {
t.Errorf("splitCriteria(%q) returned %d items, want %d", tt.input, len(got), tt.expect)
}
}
}
func TestNormalizeSeverity(t *testing.T) {
tests := []struct {
input string
want string
}{
{"HIGH", "HIGH"},
{"high", "HIGH"},
{"CRITICAL", "HIGH"},
{"medium", "MEDIUM"},
{"LOW", "LOW"},
{"unknown", "MEDIUM"},
{"", "MEDIUM"},
}
for _, tt := range tests {
got := normalizeSeverity(tt.input)
if got != tt.want {
t.Errorf("normalizeSeverity(%q) = %q, want %q", tt.input, got, tt.want)
}
}
}
func TestInferMCSeverity(t *testing.T) {
tests := []struct {
name string
want string
}{
{"encryption_at_rest_aes256", "HIGH"},
{"access_control_mfa", "HIGH"},
{"incident_response_plan", "HIGH"},
{"documentation_management", "MEDIUM"},
{"training_awareness", "MEDIUM"},
}
for _, tt := range tests {
got := inferMCSeverity(tt.name)
if got != tt.want {
t.Errorf("inferMCSeverity(%q) = %q, want %q", tt.name, got, tt.want)
}
}
}
func TestGenerateFromMC_SmallMC(t *testing.T) {
mc := MCInfo{
MasterControlID: "MC-123",
CanonicalName: "access_control_basic",
TotalControls: 3,
RegSource: "DSGVO",
}
questions := GenerateFromMC(mc)
if len(questions) != 1 {
t.Errorf("Expected 1 question for small MC, got %d", len(questions))
}
if questions[0].Severity != "HIGH" {
t.Errorf("Expected HIGH severity for access_control, got %s", questions[0].Severity)
}
}
func TestGenerateFromMC_MediumMC(t *testing.T) {
mc := MCInfo{
MasterControlID: "MC-456",
CanonicalName: "documentation_management",
TotalControls: 8,
RegSource: "NIS2",
}
questions := GenerateFromMC(mc)
if len(questions) != 2 {
t.Errorf("Expected 2 questions for medium MC, got %d", len(questions))
}
if questions[1].DependsOn == "" {
t.Error("Second question should depend on first")
}
}
func TestGenerateFromMC_LargeMC(t *testing.T) {
mc := MCInfo{
MasterControlID: "MC-789",
CanonicalName: "risk_management_framework",
TotalControls: 25,
RegSource: "NIS2",
}
questions := GenerateFromMC(mc)
if len(questions) != 3 {
t.Errorf("Expected 3 questions for large MC, got %d", len(questions))
}
}