# v0.49.0 — Test Infrastructure Hardening

> **Theme:** Replace false-confidence tests with provably-correct synchronization;
> fill the unit-test and fuzz coverage gaps identified in the v10 overall
> assessment; decompose the scheduler monolith.

## Why This Release

The v10 overall assessment found that pg_trickle's core algorithms are
mathematically sound — all P0 correctness issues are closed. The remaining
risk is concentrated in **coverage confidence**: tests that look correct but
cannot actually detect the failures they were written to catch, and source
modules that can only be validated through expensive E2E runs because they
have zero unit test coverage.

This release fixes the test foundation so that every subsequent hardening
investment lands on solid ground.

## Deliverables

### TEST-10-01 — Concurrency Test Synchronization Overhaul

All tests in `tests/e2e_concurrent_tests.rs` currently use
`tokio::time::sleep(Duration::from_millis(50))` to simulate overlap between
concurrent operations. This is unreliable: on a loaded CI runner the 50ms
window may not be enough, and there is no guarantee the spawned tasks are
actually executing concurrently.

Replace with `pg_locks`-polling loops that wait until the target transaction
is actually visible in `pg_locks` before proceeding. Add a timeout with a
clear failure message so flakiness becomes a named error rather than a silent
pass.

Affected tests:
- `test_pb1_concurrent_refresh_skip_locked_no_corruption`
- `test_concurrent_inserts_during_refresh`
- `test_concurrent_refresh_and_drop`
- `test_concurrent_refresh_multiple_sts_same_source`

### TEST-10-02 — 10-Module Unit Test Sweep

Add `#[cfg(test)]` modules with focused unit tests for pure-logic functions
in every module that currently has zero test coverage:

| Module | Key logic to test |
|--------|-------------------|
| `src/catalog.rs` | ST lookup, OID resolution, metadata serialization |
| `src/template_cache.rs` | Cache hit/miss/eviction logic, hash key derivation |
| `src/ivm.rs` | Lock mode selection for given operator trees |
| `src/cdc/polling.rs` | LSN range splitting, synthetic slot naming |
| `src/cdc/rebuild.rs` | Trigger body SQL generation, reserved column mapping |
| `src/diagnostics.rs` | Status field population, staleness calculation |
| `src/logging.rs` | Log-level routing, message formatting |
| `src/metrics_server.rs` | Metric serialization format |
| `src/otel.rs` | Span creation, attribute propagation |

### TEST-10-03 — Fuzz Targets for Merge Codegen and Row Identity

Add two new `libFuzzer` fuzz targets:

1. **`fuzz/merge_sql_fuzz.rs`** — generates random change streams (random
   insert/update/delete counts, random column types, random key cardinalities)
   and feeds them into the full merge SQL construction pipeline
   (`src/refresh/merge/`). Validates that the generated SQL is valid UTF-8,
   does not contain unresolved placeholders, and round-trips through a
   PostgreSQL parse-only check.

2. **`fuzz/row_id_fuzz.rs`** — generates random operator trees with random
   source schemas and feeds them into the row identity tracking logic
   (`src/dvm/row_id.rs`). Validates that the keyed/keyless classification is
   deterministic and that generated row-id expressions are syntactically valid.

Both targets are added to the nightly fuzz workflow.

### TEST-10-04 — DDL During Concurrent Refresh E2E Test

Add `test_ddl_during_concurrent_refresh` to `tests/e2e_concurrent_tests.rs`.
This test:
1. Creates a stream table and populates it.
2. Starts a long-running refresh in a background task.
3. Concurrently issues `ALTER STREAM TABLE ... SET query = '...'`.
4. Verifies that either:
   - The DDL completes and the next refresh uses the new query, or
   - The DDL is blocked until the refresh completes (no torn state).
5. Runs three refresh cycles to confirm the stream table remains correct.

### CI-10-02 — Expand e2e-Smoke Filter

The current PR smoke test filter matches only 4 test name patterns
(`smoke`, `basic`, `create_stream_table`, `health_check`). An operator-level
regression in JOIN or AGG handling would pass this gate undetected.

Expand the filter to also match:
- `test_.*join.*` — inner/left/full join correctness
- `test_.*aggregate.*` — aggregate differential correctness
- `test_.*window.*` — window function correctness
- `test_.*subquery.*` — correlated subquery correctness

### CI-10-03 — Consolidated Fuzz Recipe

Add `just fuzz-all` to the `justfile` that runs all fuzz targets in sequence
with a configurable corpus duration (default 60s each). Document the available
fuzz targets and their corpus locations in `CONTRIBUTING.md`.

### CQ-10-01 — Scheduler Module Decomposition

`src/scheduler/mod.rs` is 6,700+ lines — the largest single file in the
codebase and a source of high merge-conflict risk. Extract into focused
submodules:

| New file | Content |
|----------|---------|
| `src/scheduler/loop.rs` | Main scheduling loop, tick logic, SIGTERM handling |
| `src/scheduler/dispatch.rs` | Parallel dispatch state, worker claiming, orphan reaping |
| `src/scheduler/cost.rs` | (already exists — ensure complete) |
| `src/scheduler/watermark.rs` | Tick watermark computation, xmin holdback, frontier advance |

`mod.rs` becomes a thin re-export facade. All existing public API is
preserved with no behaviour change.

## Testing

- `just test-unit` — all new unit tests pass
- `just test-integration` — concurrency tests pass with `pg_locks`-polling sync
- `just test-light-e2e` — smoke filter expansion catches more regressions
- `cargo fuzz run merge_sql_fuzz -- -max_total_time=60` — no crashes
- `cargo fuzz run row_id_fuzz -- -max_total_time=60` — no crashes
