feat: Vorbereitung-Module auf 100% — Persistenz, Backend-Services, UCCA Frontend
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s
Phase A: PostgreSQL State Store (sdk_states Tabelle, InMemory-Fallback) Phase B: Modules dynamisch vom Backend, Scope DB-Persistenz, Source Policy State Phase C: UCCA Frontend (3 Seiten, Wizard, RiskScoreGauge), Obligations Live-Daten Phase D: Document Import (PDF/LLM/Gap-Analyse), System Screening (SBOM/OSV.dev) Phase E: Company Profile CRUD mit Audit-Logging Phase F: Tests (Python + TypeScript), flow-data.ts DB-Tabellen aktualisiert Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
110
admin-compliance/lib/sdk/__tests__/api-client.test.ts
Normal file
110
admin-compliance/lib/sdk/__tests__/api-client.test.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Tests for SDK API Client extensions (modules, UCCA, import, screening).
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
|
||||
// Mock fetch globally
|
||||
const mockFetch = vi.fn()
|
||||
global.fetch = mockFetch
|
||||
|
||||
// Import after mocking
|
||||
import { sdkApiClient } from '../api-client'
|
||||
|
||||
describe('SDK API Client', () => {
|
||||
beforeEach(() => {
|
||||
mockFetch.mockReset()
|
||||
})
|
||||
|
||||
describe('getModules', () => {
|
||||
it('fetches modules from backend', async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve([{ id: 'mod-1', name: 'DSGVO' }]),
|
||||
})
|
||||
|
||||
const result = await sdkApiClient.getModules()
|
||||
expect(result).toHaveLength(1)
|
||||
expect(result[0].name).toBe('DSGVO')
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining('/api/sdk/v1/modules'),
|
||||
expect.any(Object)
|
||||
)
|
||||
})
|
||||
|
||||
it('returns empty array on error', async () => {
|
||||
mockFetch.mockRejectedValueOnce(new Error('Network error'))
|
||||
const result = await sdkApiClient.getModules()
|
||||
expect(result).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('analyzeDocument', () => {
|
||||
it('sends FormData to import analyze endpoint', async () => {
|
||||
const mockResponse = {
|
||||
document_id: 'doc-1',
|
||||
detected_type: 'DSFA',
|
||||
confidence: 0.85,
|
||||
}
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockResponse),
|
||||
})
|
||||
|
||||
const formData = new FormData()
|
||||
const result = await sdkApiClient.analyzeDocument(formData)
|
||||
expect(result.document_id).toBe('doc-1')
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining('/api/sdk/v1/import/analyze'),
|
||||
expect.objectContaining({ method: 'POST' })
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('scanDependencies', () => {
|
||||
it('sends FormData to screening scan endpoint', async () => {
|
||||
const mockResponse = {
|
||||
id: 'scan-1',
|
||||
status: 'completed',
|
||||
total_components: 10,
|
||||
total_issues: 2,
|
||||
}
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockResponse),
|
||||
})
|
||||
|
||||
const formData = new FormData()
|
||||
const result = await sdkApiClient.scanDependencies(formData)
|
||||
expect(result.id).toBe('scan-1')
|
||||
expect(result.total_components).toBe(10)
|
||||
})
|
||||
})
|
||||
|
||||
describe('assessUseCase', () => {
|
||||
it('sends intake data to UCCA assess endpoint', async () => {
|
||||
const mockResult = { id: 'assessment-1', feasibility: 'GREEN' }
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(mockResult),
|
||||
})
|
||||
|
||||
const result = await sdkApiClient.assessUseCase({
|
||||
name: 'Test Use Case',
|
||||
domain: 'education',
|
||||
})
|
||||
expect(result.feasibility).toBe('GREEN')
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAssessments', () => {
|
||||
it('fetches assessment list', async () => {
|
||||
mockFetch.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: () => Promise.resolve([{ id: 'a1' }, { id: 'a2' }]),
|
||||
})
|
||||
|
||||
const result = await sdkApiClient.getAssessments()
|
||||
expect(result).toHaveLength(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -372,6 +372,153 @@ export class SDKApiClient {
|
||||
return response.data
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Methods - Modules
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get available compliance modules from backend
|
||||
*/
|
||||
async getModules(filters?: {
|
||||
serviceType?: string
|
||||
criticality?: string
|
||||
processesPii?: boolean
|
||||
aiComponents?: boolean
|
||||
}): Promise<{ modules: unknown[]; total: number }> {
|
||||
const params = new URLSearchParams()
|
||||
if (filters?.serviceType) params.set('service_type', filters.serviceType)
|
||||
if (filters?.criticality) params.set('criticality', filters.criticality)
|
||||
if (filters?.processesPii !== undefined) params.set('processes_pii', String(filters.processesPii))
|
||||
if (filters?.aiComponents !== undefined) params.set('ai_components', String(filters.aiComponents))
|
||||
|
||||
const queryString = params.toString()
|
||||
const url = `${this.baseUrl}/modules${queryString ? `?${queryString}` : ''}`
|
||||
|
||||
const response = await this.fetchWithRetry<{ modules: unknown[]; total: number }>(
|
||||
url,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
)
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Methods - UCCA (Use Case Compliance Assessment)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Assess a use case
|
||||
*/
|
||||
async assessUseCase(intake: unknown): Promise<unknown> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${this.baseUrl}/ucca/assess`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
body: JSON.stringify(intake),
|
||||
}
|
||||
)
|
||||
return response
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all assessments
|
||||
*/
|
||||
async getAssessments(): Promise<unknown[]> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown[]>>(
|
||||
`${this.baseUrl}/ucca/assessments?tenantId=${encodeURIComponent(this.tenantId)}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
return response.data || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single assessment
|
||||
*/
|
||||
async getAssessment(id: string): Promise<unknown> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${this.baseUrl}/ucca/assessments/${id}`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an assessment
|
||||
*/
|
||||
async deleteAssessment(id: string): Promise<void> {
|
||||
await this.fetchWithRetry<APIResponse<void>>(
|
||||
`${this.baseUrl}/ucca/assessments/${id}`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Methods - Document Import
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Analyze an uploaded document
|
||||
*/
|
||||
async analyzeDocument(formData: FormData): Promise<unknown> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${this.baseUrl}/import/analyze`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
body: formData,
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Methods - System Screening
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Scan a dependency file (package-lock.json, requirements.txt, etc.)
|
||||
*/
|
||||
async scanDependencies(formData: FormData): Promise<unknown> {
|
||||
const response = await this.fetchWithRetry<APIResponse<unknown>>(
|
||||
`${this.baseUrl}/screening/scan`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Tenant-ID': this.tenantId,
|
||||
},
|
||||
body: formData,
|
||||
}
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public Methods - Export
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -65,6 +65,9 @@ const initialState: SDKState = {
|
||||
// Compliance Scope
|
||||
complianceScope: null,
|
||||
|
||||
// Source Policy
|
||||
sourcePolicy: null,
|
||||
|
||||
// Progress
|
||||
currentPhase: 1,
|
||||
currentStep: 'company-profile',
|
||||
|
||||
@@ -15,6 +15,7 @@ describe('StateProjector', () => {
|
||||
customerType: null,
|
||||
companyProfile: null,
|
||||
complianceScope: null,
|
||||
sourcePolicy: null,
|
||||
currentPhase: 1,
|
||||
currentStep: 'company-profile',
|
||||
completedSteps: [],
|
||||
|
||||
@@ -1483,6 +1483,14 @@ export interface SDKState {
|
||||
// Compliance Scope (determines depth level L1-L4)
|
||||
complianceScope: import('./compliance-scope-types').ComplianceScopeState | null
|
||||
|
||||
// Source Policy (checkpoint tracking — actual data in backend)
|
||||
sourcePolicy: {
|
||||
configured: boolean
|
||||
sourcesCount: number
|
||||
piiRulesCount: number
|
||||
lastAuditAt: string | null
|
||||
} | null
|
||||
|
||||
// Progress
|
||||
currentPhase: SDKPhase
|
||||
currentStep: string
|
||||
|
||||
Reference in New Issue
Block a user