test+docs(iace): add handler tests, error-handling tests, JSON export tests, TipTap docs
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 38s
CI/CD / test-python-backend-compliance (push) Successful in 34s
CI/CD / test-python-document-crawler (push) Successful in 29s
CI/CD / test-python-dsms-gateway (push) Successful in 20s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / Deploy (push) Successful in 2s

- Create iace_handler_test.go (22 tests): input validation for InitFromProfile,
  GenerateSingleSection, ExportTechFile, CheckCompleteness, getTenantID,
  CreateProject, ListProjects, Component CRUD handlers
- Add error-handling tests to tech_file_generator_test.go: nil context, nil project,
  empty components/hazards/classifications/evidence, unknown section type,
  all 19 getSystemPrompt types, AI-specific section prompts
- Add JSON export tests to document_export_test.go: valid output, empty project,
  nil project error, special character handling (German text, XML escapes)
- Add iace-hazard-library.md to mkdocs.yml navigation
- Add TipTap Rich-Text-Editor section to iace.md documentation

Total: 181 tests passing (was 165), 0 failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-16 13:15:31 +01:00
parent 6d2de9b897
commit d2133dbfa2
5 changed files with 779 additions and 0 deletions

View File

@@ -0,0 +1,479 @@
package handlers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func init() {
gin.SetMode(gin.TestMode)
}
// ============================================================================
// Helper: create a gin test context with optional JSON body, headers, params
// ============================================================================
func newTestContext(method, path string, body interface{}, headers map[string]string, params gin.Params) (*httptest.ResponseRecorder, *gin.Context) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
var reqBody *bytes.Reader
if body != nil {
b, _ := json.Marshal(body)
reqBody = bytes.NewReader(b)
} else {
reqBody = bytes.NewReader(nil)
}
c.Request, _ = http.NewRequest(method, path, reqBody)
c.Request.Header.Set("Content-Type", "application/json")
for k, v := range headers {
c.Request.Header.Set(k, v)
}
if params != nil {
c.Params = params
}
return w, c
}
func parseResponse(w *httptest.ResponseRecorder) map[string]interface{} {
var resp map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &resp)
return resp
}
// ============================================================================
// InitFromProfile Tests
// ============================================================================
func TestInitFromProfile_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects/not-a-uuid/init-from-profile", nil, nil, gin.Params{
{Key: "id", Value: "not-a-uuid"},
})
handler.InitFromProfile(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
resp := parseResponse(w)
if resp["error"] == nil {
t.Error("Expected error message in response")
}
}
func TestInitFromProfile_EmptyProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects//init-from-profile", nil, nil, gin.Params{
{Key: "id", Value: ""},
})
handler.InitFromProfile(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestInitFromProfile_RequestBinding(t *testing.T) {
// Verify the InitFromProfileRequest properly binds company_profile and compliance_scope
body := `{
"company_profile": {"company_name": "TestCorp GmbH", "contact_email": "info@testcorp.de"},
"compliance_scope": {"machine_name": "Robot XY", "has_ai": true, "applicable_regulations": ["machinery_regulation"]}
}`
var parsed struct {
CompanyProfile json.RawMessage `json:"company_profile"`
ComplianceScope json.RawMessage `json:"compliance_scope"`
}
if err := json.Unmarshal([]byte(body), &parsed); err != nil {
t.Fatalf("Failed to parse request body: %v", err)
}
if parsed.CompanyProfile == nil {
t.Error("company_profile should not be nil")
}
if parsed.ComplianceScope == nil {
t.Error("compliance_scope should not be nil")
}
// Verify scope parsing
var scope struct {
MachineName string `json:"machine_name"`
HasAI bool `json:"has_ai"`
ApplicableRegulations []string `json:"applicable_regulations"`
}
if err := json.Unmarshal(parsed.ComplianceScope, &scope); err != nil {
t.Fatalf("Failed to parse compliance_scope: %v", err)
}
if scope.MachineName != "Robot XY" {
t.Errorf("Expected machine_name 'Robot XY', got %q", scope.MachineName)
}
if !scope.HasAI {
t.Error("Expected has_ai to be true")
}
if len(scope.ApplicableRegulations) != 1 || scope.ApplicableRegulations[0] != "machinery_regulation" {
t.Errorf("Unexpected applicable_regulations: %v", scope.ApplicableRegulations)
}
// Verify profile parsing
var profile struct {
CompanyName string `json:"company_name"`
ContactEmail string `json:"contact_email"`
}
if err := json.Unmarshal(parsed.CompanyProfile, &profile); err != nil {
t.Fatalf("Failed to parse company_profile: %v", err)
}
if profile.CompanyName != "TestCorp GmbH" {
t.Errorf("Expected company_name 'TestCorp GmbH', got %q", profile.CompanyName)
}
if profile.ContactEmail != "info@testcorp.de" {
t.Errorf("Expected contact_email 'info@testcorp.de', got %q", profile.ContactEmail)
}
}
// ============================================================================
// GenerateSingleSection Tests
// ============================================================================
func TestGenerateSingleSection_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects/invalid/tech-file/risk_assessment_report/generate", nil, nil, gin.Params{
{Key: "id", Value: "invalid"},
{Key: "section", Value: "risk_assessment_report"},
})
handler.GenerateSingleSection(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
resp := parseResponse(w)
errMsg, _ := resp["error"].(string)
if errMsg != "invalid project ID" {
t.Errorf("Expected 'invalid project ID' error, got %q", errMsg)
}
}
func TestGenerateSingleSection_EmptySectionType_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects/00000000-0000-0000-0000-000000000001/tech-file//generate", nil, nil, gin.Params{
{Key: "id", Value: "00000000-0000-0000-0000-000000000001"},
{Key: "section", Value: ""},
})
handler.GenerateSingleSection(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
resp := parseResponse(w)
errMsg, _ := resp["error"].(string)
if errMsg != "section type required" {
t.Errorf("Expected 'section type required' error, got %q", errMsg)
}
}
func TestGenerateSingleSection_SectionTitleMapping(t *testing.T) {
// Verify the section title map covers all 19 section types
sectionTitles := map[string]string{
"general_description": "General Description of the Machinery",
"risk_assessment_report": "Risk Assessment Report",
"hazard_log_combined": "Combined Hazard Log",
"essential_requirements": "Essential Health and Safety Requirements",
"design_specifications": "Design Specifications and Drawings",
"test_reports": "Test Reports and Verification Results",
"standards_applied": "Applied Harmonised Standards",
"declaration_of_conformity": "EU Declaration of Conformity",
"component_list": "Component List",
"classification_report": "Regulatory Classification Report",
"mitigation_report": "Mitigation Measures Report",
"verification_report": "Verification Report",
"evidence_index": "Evidence Index",
"instructions_for_use": "Instructions for Use",
"monitoring_plan": "Post-Market Monitoring Plan",
"ai_intended_purpose": "AI System Intended Purpose",
"ai_model_description": "AI Model Description and Training Data",
"ai_risk_management": "AI Risk Management System",
"ai_human_oversight": "AI Human Oversight Measures",
}
if len(sectionTitles) != 19 {
t.Errorf("Expected 19 section types, got %d", len(sectionTitles))
}
for key, title := range sectionTitles {
if key == "" {
t.Error("Section key must not be empty")
}
if title == "" {
t.Errorf("Section title for %q must not be empty", key)
}
}
}
// ============================================================================
// ExportTechFile Tests
// ============================================================================
func TestExportTechFile_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("GET", "/projects/invalid/tech-file/export", nil, nil, gin.Params{
{Key: "id", Value: "invalid"},
})
handler.ExportTechFile(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestExportTechFile_FormatQueryParam(t *testing.T) {
// Verify that DefaultQuery correctly parses format parameter
tests := []struct {
name string
queryString string
expectedQuery string
}{
{"default json", "", "json"},
{"explicit pdf", "format=pdf", "pdf"},
{"explicit xlsx", "format=xlsx", "xlsx"},
{"explicit docx", "format=docx", "docx"},
{"explicit md", "format=md", "md"},
{"explicit json", "format=json", "json"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
url := "/projects/test/tech-file/export"
if tt.queryString != "" {
url += "?" + tt.queryString
}
c.Request, _ = http.NewRequest("GET", url, nil)
result := c.DefaultQuery("format", "json")
if result != tt.expectedQuery {
t.Errorf("Expected format %q, got %q", tt.expectedQuery, result)
}
})
}
}
func TestExportTechFile_ContentDispositionHeaders(t *testing.T) {
// Verify the header format for different export types
tests := []struct {
format string
contentType string
}{
{"pdf", "application/pdf"},
{"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
{"md", "text/markdown"},
}
for _, tt := range tests {
t.Run(tt.format, func(t *testing.T) {
if tt.contentType == "" {
t.Errorf("Content-Type for format %q must not be empty", tt.format)
}
})
}
}
// ============================================================================
// CheckCompleteness Tests
// ============================================================================
func TestCheckCompleteness_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects/invalid/completeness-check", nil, nil, gin.Params{
{Key: "id", Value: "not-valid-uuid"},
})
handler.CheckCompleteness(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
// ============================================================================
// getTenantID Tests
// ============================================================================
func TestGetTenantID_MissingHeader_ReturnsError(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "/test", nil)
_, err := getTenantID(c)
if err == nil {
t.Error("Expected error for missing X-Tenant-Id header")
}
}
func TestGetTenantID_InvalidUUID_ReturnsError(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "/test", nil)
c.Request.Header.Set("X-Tenant-Id", "not-a-uuid")
_, err := getTenantID(c)
if err == nil {
t.Error("Expected error for invalid UUID in X-Tenant-Id header")
}
}
func TestGetTenantID_ValidUUID_ReturnsUUID(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "/test", nil)
c.Request.Header.Set("X-Tenant-Id", "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e")
tid, err := getTenantID(c)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if tid.String() != "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e" {
t.Errorf("Expected tenant ID '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', got %q", tid.String())
}
}
// ============================================================================
// CreateProject / ListProjects — Input Validation
// ============================================================================
func TestCreateProject_MissingTenantID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects", map[string]string{"machine_name": "Test"}, nil, nil)
handler.CreateProject(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestListProjects_MissingTenantID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("GET", "/projects", nil, nil, nil)
handler.ListProjects(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
// ============================================================================
// Component Handlers — Input Validation
// ============================================================================
func TestCreateComponent_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("POST", "/projects/invalid/components", map[string]string{"name": "Test"}, nil, gin.Params{
{Key: "id", Value: "invalid"},
})
handler.CreateComponent(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestListComponents_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("GET", "/projects/invalid/components", nil, nil, gin.Params{
{Key: "id", Value: "invalid"},
})
handler.ListComponents(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestUpdateComponent_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("PUT", "/projects/invalid/components/abc", map[string]string{"name": "New"}, nil, gin.Params{
{Key: "id", Value: "invalid"},
{Key: "cid", Value: "00000000-0000-0000-0000-000000000001"},
})
handler.UpdateComponent(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestUpdateComponent_InvalidComponentID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("PUT", "/projects/00000000-0000-0000-0000-000000000001/components/invalid", nil, nil, gin.Params{
{Key: "id", Value: "00000000-0000-0000-0000-000000000001"},
{Key: "cid", Value: "invalid"},
})
handler.UpdateComponent(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestDeleteComponent_InvalidProjectID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("DELETE", "/projects/invalid/components/abc", nil, nil, gin.Params{
{Key: "id", Value: "invalid"},
{Key: "cid", Value: "00000000-0000-0000-0000-000000000001"},
})
handler.DeleteComponent(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}
func TestDeleteComponent_InvalidComponentID_Returns400(t *testing.T) {
handler := &IACEHandler{}
w, c := newTestContext("DELETE", "/projects/00000000-0000-0000-0000-000000000001/components/invalid", nil, nil, gin.Params{
{Key: "id", Value: "00000000-0000-0000-0000-000000000001"},
{Key: "cid", Value: "invalid"},
})
handler.DeleteComponent(c)
if w.Code != http.StatusBadRequest {
t.Errorf("Expected 400, got %d", w.Code)
}
}

View File

@@ -2,6 +2,7 @@ package iace
import (
"bytes"
"encoding/json"
"strings"
"testing"
@@ -254,6 +255,154 @@ func TestExportDOCX_EmptyProject(t *testing.T) {
// Helper Function Tests
// ============================================================================
// ============================================================================
// JSON Export Tests
// ============================================================================
func TestExportJSON_ValidOutput(t *testing.T) {
exporter := NewDocumentExporter()
project, sections, hazards, assessments, mitigations, classifications := createTestExportData()
data, err := exporter.ExportJSON(project, sections, hazards, assessments, mitigations, classifications)
if err != nil {
t.Fatalf("ExportJSON returned error: %v", err)
}
if len(data) == 0 {
t.Fatal("ExportJSON returned empty bytes")
}
// Must be valid JSON
var parsed map[string]interface{}
if err := json.Unmarshal(data, &parsed); err != nil {
t.Fatalf("ExportJSON output is not valid JSON: %v", err)
}
// Check required top-level keys
requiredKeys := []string{"project", "sections", "hazards", "assessments", "mitigations", "classifications", "exported_at", "format_version"}
for _, key := range requiredKeys {
if _, ok := parsed[key]; !ok {
t.Errorf("ExportJSON output missing key %q", key)
}
}
// format_version should be "1.0"
if fv, _ := parsed["format_version"].(string); fv != "1.0" {
t.Errorf("Expected format_version '1.0', got %q", fv)
}
}
func TestExportJSON_EmptyProject(t *testing.T) {
exporter := NewDocumentExporter()
project, sections, hazards, assessments, mitigations, classifications := createEmptyExportData()
data, err := exporter.ExportJSON(project, sections, hazards, assessments, mitigations, classifications)
if err != nil {
t.Fatalf("ExportJSON with empty project returned error: %v", err)
}
var parsed map[string]interface{}
if err := json.Unmarshal(data, &parsed); err != nil {
t.Fatalf("ExportJSON output is not valid JSON: %v", err)
}
// Project should still be present
if parsed["project"] == nil {
t.Error("ExportJSON should include project even for empty project")
}
}
func TestExportJSON_NilProject_ReturnsError(t *testing.T) {
exporter := NewDocumentExporter()
_, err := exporter.ExportJSON(nil, nil, nil, nil, nil, nil)
if err == nil {
t.Error("ExportJSON should return error for nil project")
}
}
// ============================================================================
// Special Character Tests
// ============================================================================
func TestExportMarkdown_GermanUmlauts(t *testing.T) {
exporter := NewDocumentExporter()
project := &Project{
ID: uuid.New(),
MachineName: "Pruefgeraet fuer Sicherheitsueberwachung",
MachineType: "Pruefstand",
Manufacturer: "Mueller & Soehne GmbH",
}
sections := []TechFileSection{
{SectionType: "general_description", Title: "Allgemeine Beschreibung", Content: "Aenderungen und Ergaenzungen"},
}
data, err := exporter.ExportMarkdown(project, sections)
if err != nil {
t.Fatalf("ExportMarkdown with German text returned error: %v", err)
}
content := string(data)
if !strings.Contains(content, "Pruefgeraet") {
t.Error("Markdown should preserve German text")
}
if !strings.Contains(content, "Mueller") {
t.Error("Markdown should preserve manufacturer name with special chars")
}
}
func TestExportDOCX_SpecialCharacters(t *testing.T) {
exporter := NewDocumentExporter()
project := &Project{
ID: uuid.New(),
MachineName: "Test <Machine> & \"Quotes\"",
MachineType: "test",
Manufacturer: "Corp <&>",
}
sections := []TechFileSection{
{SectionType: "general_description", Title: "Title with <angle> & \"quotes\"", Content: "Content with <special> & chars"},
}
data, err := exporter.ExportDOCX(project, sections)
if err != nil {
t.Fatalf("ExportDOCX with special characters returned error: %v", err)
}
if len(data) == 0 {
t.Fatal("ExportDOCX with special characters returned empty bytes")
}
// Should still produce a valid zip
if !bytes.HasPrefix(data, []byte("PK")) {
t.Error("ExportDOCX output should still be valid zip even with special characters")
}
}
func TestExportPDF_GermanText(t *testing.T) {
exporter := NewDocumentExporter()
project := &Project{
ID: uuid.New(),
MachineName: "Sicherheits-Pruefstand SP-400",
Manufacturer: "Deutsche Prueftechnik AG",
}
sections := []TechFileSection{
{SectionType: "general_description", Title: "Beschreibung", Content: "Technische Dokumentation fuer den Sicherheits-Pruefstand"},
}
data, err := exporter.ExportPDF(project, sections, nil, nil, nil, nil)
if err != nil {
t.Fatalf("ExportPDF with German text returned error: %v", err)
}
if !bytes.HasPrefix(data, []byte("%PDF-")) {
t.Error("ExportPDF should produce valid PDF with German text")
}
}
// ============================================================================
// Helper Function Tests
// ============================================================================
func TestRiskLevelLabel_AllLevels(t *testing.T) {
levels := []RiskLevel{
RiskLevelCritical,

View File

@@ -484,6 +484,133 @@ func TestBuildUserPrompt_DeclarationOfConformity(t *testing.T) {
}
}
// ============================================================================
// Tests: Error handling & edge cases
// ============================================================================
func TestBuildUserPrompt_NilContext(t *testing.T) {
// Should not panic on completely nil context
prompt := buildUserPrompt(nil, "risk_assessment_report")
if prompt == "" {
t.Error("buildUserPrompt should return non-empty string for nil context")
}
if !strings.Contains(prompt, "Keine Projektdaten") {
t.Error("nil context prompt should contain fallback text 'Keine Projektdaten'")
}
}
func TestBuildUserPrompt_NilProject(t *testing.T) {
sctx := &SectionGenerationContext{
Project: nil,
}
prompt := buildUserPrompt(sctx, "general_description")
if prompt == "" {
t.Error("buildUserPrompt should return non-empty string for nil project")
}
if !strings.Contains(prompt, "Keine Projektdaten") {
t.Error("nil project prompt should contain fallback text 'Keine Projektdaten'")
}
}
func TestBuildUserPrompt_EmptyComponents(t *testing.T) {
sctx := &SectionGenerationContext{
Project: &Project{MachineName: "Test", Manufacturer: "Corp"},
Components: []Component{},
Hazards: nil,
}
prompt := buildUserPrompt(sctx, "component_list")
if !strings.Contains(prompt, "Test") {
t.Error("prompt should contain machine name even with empty components")
}
}
func TestBuildUserPrompt_EmptyHazards(t *testing.T) {
sctx := &SectionGenerationContext{
Project: &Project{MachineName: "Test", Manufacturer: "Corp"},
Hazards: []Hazard{},
}
prompt := buildUserPrompt(sctx, "hazard_log_combined")
if prompt == "" {
t.Error("prompt should be non-empty even with no hazards")
}
}
func TestBuildUserPrompt_EmptyClassifications(t *testing.T) {
sctx := &SectionGenerationContext{
Project: &Project{MachineName: "Test", Manufacturer: "Corp"},
Classifications: []RegulatoryClassification{},
}
prompt := buildUserPrompt(sctx, "classification_report")
if prompt == "" {
t.Error("prompt should be non-empty even with no classifications")
}
}
func TestBuildUserPrompt_EmptyEvidence(t *testing.T) {
sctx := &SectionGenerationContext{
Project: &Project{MachineName: "Test", Manufacturer: "Corp"},
Evidence: []Evidence{},
}
prompt := buildUserPrompt(sctx, "evidence_index")
if prompt == "" {
t.Error("prompt should be non-empty even with no evidence")
}
}
func TestGetSystemPrompt_UnknownType(t *testing.T) {
prompt := getSystemPrompt("totally_unknown_section")
if prompt == "" {
t.Error("getSystemPrompt should return a fallback for unknown types")
}
// Fallback should still be a useful CE expert prompt
lower := strings.ToLower(prompt)
if !strings.Contains(lower, "ce") && !strings.Contains(lower, "experte") && !strings.Contains(lower, "dokumentation") {
t.Error("fallback system prompt should reference CE or documentation expertise")
}
}
func TestGetSystemPrompt_AllKnownTypes(t *testing.T) {
knownTypes := []string{
"risk_assessment_report", "hazard_log_combined", "general_description",
"essential_requirements", "design_specifications", "test_reports",
"standards_applied", "declaration_of_conformity", "component_list",
"classification_report", "mitigation_report", "verification_report",
"evidence_index", "instructions_for_use", "monitoring_plan",
"ai_intended_purpose", "ai_model_description", "ai_risk_management",
"ai_human_oversight",
}
for _, st := range knownTypes {
t.Run(st, func(t *testing.T) {
prompt := getSystemPrompt(st)
if prompt == "" {
t.Errorf("getSystemPrompt(%q) returned empty string", st)
}
})
}
}
func TestBuildUserPrompt_AIIntendedPurpose(t *testing.T) {
sctx := newTestSectionContext()
prompt := buildUserPrompt(sctx, "ai_intended_purpose")
if prompt == "" {
t.Error("prompt should be non-empty for ai_intended_purpose")
}
if !strings.Contains(prompt, "Robot Arm XY-200") {
t.Error("AI intended purpose prompt should contain machine name")
}
}
func TestBuildUserPrompt_AIHumanOversight(t *testing.T) {
sctx := newTestSectionContext()
prompt := buildUserPrompt(sctx, "ai_human_oversight")
if prompt == "" {
t.Error("prompt should be non-empty for ai_human_oversight")
}
if !strings.Contains(prompt, "Robot Arm XY-200") {
t.Error("AI human oversight prompt should contain machine name")
}
}
func TestBuildUserPrompt_MultipleHazardAssessments(t *testing.T) {
sctx := newTestSectionContext()