high priority medium complexity backend pending backend specialist Tier 5

Acceptance Criteria

testConnection(orgId, integrationType) resolves the correct adapter from the registry and calls its testConnection() method
Credentials are fetched from the credential vault (decrypted) before being passed to the adapter — plaintext credentials never exist in memory longer than the duration of the test call
The result is normalized into a ConnectionTestResult domain object with fields: success (bool), latencyMs (int), errorCode (String?), errorMessage (String?)
On completion (success or failure), the integration_configs record is updated with last_tested_at (timestamp) and last_test_status ('success' | 'failure' | 'timeout')
If the adapter's testConnection() throws or times out after 10 seconds, the result is normalized to success=false with errorCode='TIMEOUT' or 'ADAPTER_ERROR'
If the adapter is not registered for the given integrationType, a AdapterNotFoundError domain error is returned
If credentials are not found in the vault for the org+integrationType, a CredentialsNotFoundError domain error is returned
The connection test is non-destructive — it must not create, modify, or delete any data in the external system
Concurrent test calls for the same org+integrationType are allowed (no lock required) but each persists its own last_tested_at independently
The method returns within 12 seconds under all conditions (10s adapter timeout + 2s overhead)

Technical Requirements

frameworks
Dart
Supabase Dart SDK
BLoC
Riverpod
apis
Adapter Registry testConnection() interface
Credential Vault decryption API
Supabase PostgREST update API
Xledger REST API (via adapter)
Microsoft Dynamics REST API (via adapter)
Cornerstone REST API (via adapter)
Consio REST API (via adapter)
Bufdir REST API (via adapter)
data models
IntegrationConfig
ConnectionTestResult
EncryptedCredential
AdapterCapabilities
performance requirements
Total method execution must not exceed 12 seconds (10s adapter timeout + 2s for vault + DB operations)
Credential decryption must complete within 500ms
Database update of last_tested_at must complete within 500ms after adapter returns
security requirements
Decrypted credentials must be held only in a local variable scoped to the testConnection() call stack — never stored, logged, or returned to callers
The ConnectionTestResult returned to the caller must not include any credential values, even in error messages
Vault access must require org-scoped authorization — service must verify the requesting session owns the org before fetching credentials
All network calls to external systems via adapters must use HTTPS only

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

Implement the 10-second timeout using Future.timeout() on the adapter's testConnection() call: `await adapter.testConnection(creds).timeout(const Duration(seconds: 10), onTimeout: () => ConnectionTestResult.timeout())`. Credential decryption and DB persistence should be sequential (decrypt → test → persist), not parallel, to ensure credentials are only in scope during the test. The vault fetch and credential scoping should use a helper method that takes a callback to guarantee the decrypted object goes out of scope after the callback completes — this is the safest memory pattern in Dart. For the BLoC integration, the connection test should emit a sequence of states: ConnectionTestInProgress → ConnectionTestSuccess/ConnectionTestFailed.

Do not cache connection test results — each call must invoke the adapter fresh. The Supabase update for last_tested_at should use `.update({'last_tested_at': DateTime.now().toIso8601String(), 'last_test_status': result.statusString}).eq('org_id', orgId).eq('integration_type', integrationType.name)` to avoid a full record overwrite.

Testing Requirements

Unit tests with mocked adapter registry, vault, and Supabase client: (1) successful test returns normalized ConnectionTestResult with success=true and latencyMs populated, (2) adapter timeout after 10s returns success=false with errorCode='TIMEOUT', (3) adapter throws exception returns success=false with errorCode='ADAPTER_ERROR', (4) adapter not found returns AdapterNotFoundError, (5) credentials not in vault returns CredentialsNotFoundError, (6) last_tested_at and last_test_status are persisted for both success and failure outcomes, (7) decrypted credential is NOT present in the returned ConnectionTestResult. Use Dart's Future.delayed in mock adapters to simulate timeout. Verify that timed-out calls do not leave dangling futures. Target 85%+ branch coverage.

Component
Integration Configuration Service
service high
Epic Risks (3)
medium impact high prob technical

Each of the five external systems (Xledger, Dynamics, Cornerstone, Consio, Bufdir) has a different authentication flow, field schema, and error format. Forcing them into a uniform adapter interface may require compromises that result in leaky abstractions or make the adapter contract too complex to maintain.

Mitigation & Contingency

Mitigation: Design the IntegrationAdapter interface with a loose invoke() payload rather than a typed one, allowing each adapter to declare its own input/output schema. Use integration type metadata in the registry to document per-adapter quirks. Build Xledger first as the most documented API, then adapt the interface based on learnings.

Contingency: If the uniform interface cannot accommodate all five systems, split into two interface tiers: a simple polling/export adapter and a richer bidirectional adapter, with the registry declaring which tier each system implements.

medium impact high prob dependency

Development and testing of the Cornerstone and Consio adapters depends on NHF providing sandbox API access. If credentials or documentation are delayed, these adapters cannot be validated, blocking the epic's acceptance criteria.

Mitigation & Contingency

Mitigation: Implement Xledger and Dynamics adapters first (better-documented, sandbox available). Create a mock adapter for Cornerstone/Consio using recorded API responses for CI testing. Proactively request sandbox access from NHF at project kickoff.

Contingency: Ship the epic with Cornerstone/Consio adapters in a 'stub' state (connectivity test returns a simulated success, invoke() is not production-wired) and gate the NHF integration behind a feature flag until real API access is obtained.

medium impact medium prob scope

Real-world field mappings may include nested transformations, conditional logic, and data type coercions (e.g., Norwegian date formats, currency rounding rules) that the Field Mapping Resolver's initial design does not accommodate, requiring scope expansion mid-epic.

Mitigation & Contingency

Mitigation: Gather actual field mapping examples from Blindeforbundet (Xledger) and HLF (Dynamics) before designing the resolver. Identify the most complex transformation required and ensure the resolver design handles it. Limit Phase 1 to direct field renaming and format conversion only.

Contingency: If complex transformations are required, implement a simple expression evaluator (e.g., JSONata or a custom mini-DSL) as an extension point in the resolver, delivering basic mappings first and complex ones in a follow-up task.