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
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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user