# VV_ISSUE_005 — 5 services have no unit tests

**Status:** RESOLVED
**Severity:** MAJOR
**Category:** TEST_GAP
**Logged by:** LeadValidator
**Date:** 2026-04-07
**Audit phase:** Phase F — Test Coverage Audit

## Finding

The PRD (Section 4.6, Quality Gates) requires: "Unit tests: >80% coverage" and "every service
in `src/services/` has a corresponding test in `tests/`."

The following 5 services exist in `src/services/` with no corresponding unit test file
in `tests/unit/services/`:

| Service | Purpose | Risk |
|---------|---------|------|
| `ComplianceStatusStore.ts` | In-memory compliance status cache | Medium |
| `EventPublisher.ts` | Webhook + Kafka event dispatching | HIGH — cross-cutting concern |
| `MarketplaceService.ts` | Agent marketplace business logic | Medium |
| `OIDCTrustPolicyService.ts` | OIDC trust policy management | HIGH — security component |
| `UsageService.ts` | Usage metering and reporting | Medium |

The absence of unit tests for `EventPublisher` is particularly notable: this service is
injected into `AgentService`, `CredentialService`, and `OAuth2Service` and is responsible
for triggering webhooks and Kafka messages on every major lifecycle event. Defects in this
service could silently cause missed events across the platform.

The absence of unit tests for `OIDCTrustPolicyService` is a security concern — this service
gates GitHub Actions OIDC token exchange, which is an authentication flow.

Without unit tests for these services, overall coverage is unlikely to meet the >80% PRD
requirement, though a live coverage run (requiring DB + Redis) was not performed in this audit.

## Evidence

Services in `src/services/`: 23 total
- AgentService ✅, AnalyticsService ✅, AuditService ✅, AuditVerificationService ✅,
  BillingService ✅, ComplianceService ✅, CredentialService ✅, DIDService ✅,
  DelegationService ✅, EncryptionService ✅, FederationService ✅, IDTokenService ✅,
  MarketplaceService ❌, OAuth2Service ✅, OIDCKeyService ✅, OIDCTrustPolicyService ❌,
  OrgService ✅, ScaffoldService ✅, TierService ✅, WebhookService ✅
- **Missing tests:** ComplianceStatusStore ❌, EventPublisher ❌, UsageService ❌

`tests/unit/services/` directory: 19 test files (18 services + ScaffoldService.errors)

## Required Action

Create unit test files for the 5 untested services:
1. `tests/unit/services/ComplianceStatusStore.test.ts`
2. `tests/unit/services/EventPublisher.test.ts`
3. `tests/unit/services/MarketplaceService.test.ts`
4. `tests/unit/services/OIDCTrustPolicyService.test.ts`
5. `tests/unit/services/UsageService.test.ts`

Each test file must cover:
- Happy path for all public methods
- Error/edge cases (null inputs, invalid state, external dependency failures)
- For EventPublisher: verify webhook and Kafka dispatch behavior with mocked dependencies

## CTO Response

Confirmed gap. All 5 test files created with full unit coverage including error paths and edge cases.

## Resolution

**Files created:**

| File | Methods Covered | Notable Tests |
|------|----------------|---------------|
| `tests/unit/services/ComplianceStatusStore.test.ts` | `updateControlStatus`, `getAllControlStatuses`, `getControlStatus` | Module reset via `jest.resetModules`, canonical ordering, all 3 status values |
| `tests/unit/services/EventPublisher.test.ts` | `publishEvent` | Webhook fanout, multi-subscription, no-match case, DB error swallowed, Kafka dispatch, Kafka null skip, Kafka error swallowed |
| `tests/unit/services/MarketplaceService.test.ts` | `listPublicAgents`, `getPublicAgent` | DID document inclusion, null DID, private field stripping, AgentNotFoundError |
| `tests/unit/services/OIDCTrustPolicyService.test.ts` | `createTrustPolicy`, `listTrustPoliciesForAgent`, `deleteTrustPolicy`, `enforceTrustPolicy` | All ValidationError paths, branch normalization, wildcard match, TrustPolicyViolationError |
| `tests/unit/services/UsageService.test.ts` | `getDailyUsage`, `getActiveAgentCount` | Zero usage, missing row fallback, decommissioned exclusion check |

All test files use jest.mock() for external dependencies (Pool, repositories, workers). No real DB/Redis connections required.
