# Testing concept: fully testing [`adapters/`](adapters/:1)

This is a Clean Architecture-aligned testing concept for completely testing the code under [`adapters/`](adapters/:1), using:

- [`docs/TESTING_LAYERS.md`](docs/TESTING_LAYERS.md:1) (where test types belong)
- [`docs/architecture/shared/ADAPTERS.md`](docs/architecture/shared/ADAPTERS.md:1) (what adapters are)
- [`docs/architecture/shared/REPOSITORY_STRUCTURE.md`](docs/architecture/shared/REPOSITORY_STRUCTURE.md:1) (where things live)
- [`docs/architecture/shared/DATA_FLOW.md`](docs/architecture/shared/DATA_FLOW.md:1) (dependency rule)
- [`docs/TESTS.md`](docs/TESTS.md:1) (current repo testing practices)

---

## 1) Goal + constraints

### 1.1 Goal
Make [`adapters/`](adapters/:1) **safe to change** by covering:

1. Correct port behavior (adapters implement Core ports correctly)
2. Correct mapping across boundaries (domain ⇄ persistence, domain ⇄ external system)
3. Correct error shaping at boundaries (adapter-scoped schema errors)
4. Correct composition (small clusters like composite resolvers)
5. Correct wiring assumptions (DI boundaries: repositories don’t construct their own mappers)

### 1.2 Constraints / non-negotiables

- Dependencies point inward: delivery apps → adapters → core per [`docs/architecture/shared/DATA_FLOW.md`](docs/architecture/shared/DATA_FLOW.md:13)
- Adapters are reusable infrastructure implementations (no delivery concerns) per [`docs/architecture/shared/REPOSITORY_STRUCTURE.md`](docs/architecture/shared/REPOSITORY_STRUCTURE.md:25)
- Tests live as close as possible to the code they verify per [`docs/TESTING_LAYERS.md`](docs/TESTING_LAYERS.md:6)

---

## 2) Test taxonomy for adapters (mapped to repo locations)

This section translates [`docs/TESTING_LAYERS.md`](docs/TESTING_LAYERS.md:1) into concrete rules for adapter code.

### 2.1 Local tests (live inside [`adapters/`](adapters/:1))

These are the default for adapter correctness.

#### A) Unit tests (file-adjacent)
**Use for:**

- schema guards (validate persisted/remote shapes)
- error types (message formatting, details)
- pure mappers (domain ⇄ orm/DTO)
- in-memory repositories and deterministic services

**Location:** next to implementation, e.g. [`adapters/logging/ConsoleLogger.test.ts`](adapters/logging/ConsoleLogger.test.ts:1)

**Style:** behavior-focused with BDD structure from [`docs/TESTS.md`](docs/TESTS.md:23). Use simple `Given/When/Then` comments; do not assert internal calls unless that’s the observable contract.

Reference anchor: [`typescript.describe()`](adapters/logging/ConsoleLogger.test.ts:4)

#### B) Sociable unit tests (small collaborating cluster)
**Use for:**

- a repository using an injected mapper (repository + mapper + schema guard)
- composite adapters (delegation and resolution order)

**Location:** still adjacent to the “root” of the cluster, not necessarily to each file.

Reference anchor: [`adapters/media/MediaResolverAdapter.test.ts`](adapters/media/MediaResolverAdapter.test.ts:1)

#### C) Component / module tests (module invariants without infrastructure)
**Use for:**

- “module-level” adapter compositions that should behave consistently as a unit (e.g. a group of in-memory repos that are expected to work together)

**Location:** adjacent to the module root.

Reference anchor: [`adapters/racing/persistence/inmemory/InMemoryScoringRepositories.test.ts`](adapters/racing/persistence/inmemory/InMemoryScoringRepositories.test.ts:1)

### 2.2 Global tests (live outside adapters)

#### D) Contract tests (boundary tests)
Contract tests belong at system boundaries per [`docs/TESTING_LAYERS.md`](docs/TESTING_LAYERS.md:88).

For this repo there are two contract categories:

1. **External system contracts** (API ↔ website) already documented in [`docs/CONTRACT_TESTING.md`](docs/CONTRACT_TESTING.md:1)
2. **Internal port contracts** (core port interface ↔ adapter implementation)

Internal port contracts are still valuable, but they are not “between systems”. Treat them as **shared executable specifications** for a port.

**Proposed location:** [`tests/contracts/`](tests/:1)

Principle: the contract suite imports the port interface from core and runs the same assertions against multiple adapter implementations (in-memory and TypeORM-DB-free where possible).

#### E) Integration / E2E (system-level)
Per [`docs/TESTS.md`](docs/TESTS.md:106):

- Integration tests live in [`tests/integration/`](tests/:1) and use in-memory adapters.
- E2E tests live in [`tests/e2e/`](tests/:1) and can use TypeORM/Postgres.

Adapter code should *enable* these tests, but adapter *unit correctness* should not depend on these tests.

---

## 3) Canonical adapter test recipes (what to test, not how)

These are reusable patterns to standardize how we test adapters.

### 3.1 In-memory repositories (pure adapter behavior)

**Minimum spec for an in-memory repository implementation:**

- persists and retrieves the aggregate/value (happy path)
- supports negative paths (not found returns null / empty)
- enforces invariants that the real implementation must also enforce (uniqueness, idempotency)
- does not leak references if immutability is expected (optional; depends on domain semantics)

Examples:

- [`adapters/identity/persistence/inmemory/InMemoryUserRepository.test.ts`](adapters/identity/persistence/inmemory/InMemoryUserRepository.test.ts:1)
- [`adapters/racing/persistence/inmemory/InMemorySessionRepository.test.ts`](adapters/racing/persistence/inmemory/InMemorySessionRepository.test.ts:1)

### 3.2 TypeORM mappers (mapping + validation)

**Minimum spec for a mapper:**

- domain → orm mapping produces a persistable shape
- orm → domain mapping reconstitutes without calling “create” semantics (i.e., preserves persisted identity)
- invalid persisted shape throws adapter-scoped schema error type

Examples:

- [`adapters/media/persistence/typeorm/mappers/MediaOrmMapper.test.ts`](adapters/media/persistence/typeorm/mappers/MediaOrmMapper.test.ts:1)
- [`adapters/racing/persistence/typeorm/mappers/DriverOrmMapper.test.ts`](adapters/racing/persistence/typeorm/mappers/DriverOrmMapper.test.ts:1)

### 3.3 TypeORM repositories (DB-free correctness + DI boundaries)

**We split repository tests into 2 categories:**

1. **DB-free repository behavior tests**: verify mapping is applied and correct ORM repository methods are called with expected shapes (using a stubbed TypeORM repository).
2. **DI boundary tests**: verify no internal instantiation of mappers and that constructor requires injected dependencies.

Examples:

- [`adapters/media/persistence/typeorm/repositories/TypeOrmMediaRepository.test.ts`](adapters/media/persistence/typeorm/repositories/TypeOrmMediaRepository.test.ts:1)
- [`adapters/payments/persistence/typeorm/repositories/TypeOrmPaymentRepository.test.ts`](adapters/payments/persistence/typeorm/repositories/TypeOrmPaymentRepository.test.ts:1)

### 3.4 Schema guards + schema errors (adapter boundary hardening)

**Minimum spec:**

- guard accepts valid shapes
- guard rejects invalid shapes with deterministic error messages
- schema error contains enough details to debug (entity, field, reason)

Examples:

- [`adapters/admin/persistence/typeorm/schema/TypeOrmAdminSchemaGuards.test.ts`](adapters/admin/persistence/typeorm/schema/TypeOrmAdminSchemaGuards.test.ts:1)
- [`adapters/admin/persistence/typeorm/errors/TypeOrmAdminSchemaError.test.ts`](adapters/admin/persistence/typeorm/errors/TypeOrmAdminSchemaError.test.ts:1)

### 3.5 Gateways (external side effects)

**Minimum spec:**

- correct request construction (mapping domain intent → external API payload)
- error handling and retries (if present)
- logging behavior (only observable outputs)

These tests should stub the external client; no real network.

---

## 4) Gap matrix (folder-level)

Legend:

- ✅ = present (at least one meaningful test exists)
- ⚠️ = partially covered
- ❌ = missing

> Important: this matrix is based on the current directory contents under [`adapters/`](adapters/:1). It’s folder-level, not per-class.

| Adapter folder | What exists | Local tests status | Missing tests (minimum) |
|---|---|---:|---|
| [`adapters/achievement/`](adapters/achievement/:1) | TypeORM entities/mappers/repository/schema guard | ❌ | Mapper tests, schema guard tests, repo DI boundary tests, schema error tests |
| [`adapters/activity/`](adapters/activity/:1) | In-memory repository | ❌ | In-memory repo behavior test suite |
| [`adapters/admin/`](adapters/admin/:1) | In-memory repo + TypeORM layer | ✅ | Consider adding DB-free repo tests consistency patterns for TypeORM (if not already), ensure schema guard coverage is complete |
| [`adapters/analytics/`](adapters/analytics/:1) | In-memory repos + TypeORM layer | ⚠️ | Tests for TypeORM repos without tests, tests for non-tested mappers (`AnalyticsSnapshotOrmMapper`, `EngagementEventOrmMapper`), schema guard tests, schema error tests |
| [`adapters/automation/`](adapters/automation/:1) | Config objects | ❌ | Unit tests for config parsing/merging defaults (if behavior exists); otherwise explicitly accept no tests |
| [`adapters/bootstrap/`](adapters/bootstrap/:1) | Seeders + many config modules + factories | ⚠️ | Add unit tests for critical deterministic configs/factories not yet covered; establish module tests for seeding workflows (DB-free) |
| [`adapters/drivers/`](adapters/drivers/:1) | In-memory repository | ❌ | In-memory repo behavior tests |
| [`adapters/events/`](adapters/events/:1) | In-memory event publishers | ❌ | Behavior tests: publishes expected events to subscribers/collectors; ensure “no-op” safety |
| [`adapters/health/`](adapters/health/:1) | In-memory health check adapter | ❌ | Behavior tests: healthy/unhealthy reporting, edge cases |
| [`adapters/http/`](adapters/http/:1) | Request context module | ❌ | Unit tests for any parsing/propagation logic; otherwise explicitly accept no tests |
| [`adapters/identity/`](adapters/identity/:1) | In-memory repos + TypeORM repos/mappers + services + session adapter | ⚠️ | Add tests for in-memory files without tests (company/external game rating), tests for TypeORM repos without tests, schema guards tests, cookie session adapter tests |
| [`adapters/leaderboards/`](adapters/leaderboards/:1) | In-memory repo + event publisher | ❌ | Repo tests + publisher tests |
| [`adapters/leagues/`](adapters/leagues/:1) | In-memory repo + event publisher | ❌ | Repo tests + publisher tests |
| [`adapters/logging/`](adapters/logging/:1) | Console logger + error reporter | ⚠️ | Add tests for error reporter behavior; keep logger tests |
| [`adapters/media/`](adapters/media/:1) | Resolvers + in-memory repos + TypeORM layer + ports | ⚠️ | Add tests for in-memory repos without tests, file-system storage adapter tests, gateway/event publisher tests if behavior exists |
| [`adapters/notifications/`](adapters/notifications/:1) | Gateways + persistence + ports | ⚠️ | Add gateway tests, registry tests, port adapter tests; schema guard tests for TypeORM |
| [`adapters/payments/`](adapters/payments/:1) | In-memory repos + TypeORM layer | ⚠️ | Add tests for non-tested mappers, non-tested repos, schema guard tests |
| [`adapters/persistence/`](adapters/persistence/:1) | In-memory achievement repo + migration script | ⚠️ | Decide whether migrations are tested (usually via E2E/integration). If treated as code, add smoke test for migration shape |
| [`adapters/races/`](adapters/races/:1) | In-memory repository | ❌ | In-memory repo behavior tests |
| [`adapters/racing/`](adapters/racing/:1) | Large in-memory + TypeORM layer; many tests | ✅ | Add tests for remaining untested files (notably some in-memory repos and TypeORM repos/mappers without tests) |
| [`adapters/rating/`](adapters/rating/:1) | In-memory repository | ❌ | In-memory repo behavior tests |
| [`adapters/social/`](adapters/social/:1) | In-memory + TypeORM; some tests | ⚠️ | Add tests for TypeORM social graph repository, schema guards, and any missing in-memory invariants |
| [`adapters/eslint-rules/`](adapters/eslint-rules/:1) | ESLint rules | ⚠️ | Optional: rule tests (if the project values rule stability); otherwise accept manual verification |

---

## 5) Priority order (risk-first)

If “completely tested” is the goal, this is the order I’d implement missing tests.

1. Persistence adapters that can corrupt or misread data (TypeORM mappers + schema guards) under [`adapters/racing/persistence/typeorm/`](adapters/racing/persistence/typeorm/:1), [`adapters/identity/persistence/typeorm/`](adapters/identity/persistence/typeorm/:1), [`adapters/payments/persistence/typeorm/`](adapters/payments/persistence/typeorm/:1)
2. Un-tested persistence folders with real production impact: [`adapters/achievement/`](adapters/achievement/:1), [`adapters/analytics/`](adapters/analytics/:1)
3. External side-effect gateways: [`adapters/notifications/gateways/`](adapters/notifications/gateways/:1)
4. Small but foundational shared utilities (request context, health, event publishers): [`adapters/http/`](adapters/http/:1), [`adapters/health/`](adapters/health/:1), [`adapters/events/`](adapters/events/:1)
5. Remaining in-memory repos to keep integration tests trustworthy: [`adapters/activity/`](adapters/activity/:1), [`adapters/drivers/`](adapters/drivers/:1), [`adapters/races/`](adapters/races/:1), [`adapters/rating/`](adapters/rating/:1), [`adapters/leaderboards/`](adapters/leaderboards/:1), [`adapters/leagues/`](adapters/leagues/:1)

---

## 6) Definition of done (what “completely tested adapters” means)

For each adapter module under [`adapters/`](adapters/:1):

1. Every in-memory repository has a behavior test (happy path + at least one negative path).
2. Every TypeORM mapper has a mapping test and an invalid-shape test.
3. Every TypeORM repository has at least a DB-free test proving:
   - dependencies are injected (no internal `new Mapper()` patterns)
   - mapping is applied on save/load
4. Every schema guard and schema error class is tested.
5. Every external gateway has a stubbed-client unit test verifying payload mapping and error shaping.
6. At least one module-level test exists for any composite adapter (delegation order + null-handling).
7. Anything that is intentionally “not worth unit-testing” is explicitly declared and justified in the gap matrix (to avoid silent omissions).

---

## 7) Optional: internal port-contract test harness (shared executable specs)

If we want the same behavioral contract applied across multiple adapter implementations, add a tiny harness under [`tests/contracts/`](tests/:1):

- `tests/contracts/<feature>/<PortName>.contract.ts`
  - exports a function that takes a factory creating an implementation
- Each adapter test imports that contract and runs it

This keeps contracts central **without** moving tests away from the code (the adapter still owns the “run this contract for my implementation” test file).

---

## 8) Mode switch intent

After you approve this concept, the implementation phase is to add the missing tests adjacent to the adapter files and (optionally) introduce `tests/contracts/` without breaking dependency rules.

