> **Plain-language companion:** [v0.49.0.md](v0.49.0.md)

## v0.49.0 — Test Infrastructure Hardening & Scheduler Decomposition

**Status: ✅ Released.**

> **Release Theme**
> Replace false-confidence tests with provably-correct synchronization primitives;
> fill the unit-test and fuzz coverage gaps identified in the v10 overall
> assessment; and decompose the scheduler monolith to reduce merge-conflict risk
> and improve reviewability of future scheduler changes.

---

### Deliverables

| ID | Title | Effort | Priority | Status |
|----|-------|--------|----------|--------|
| TEST-10-01 | Concurrency test synchronization overhaul | M | P1 | ✅ Done |
| TEST-10-02 | 10-module unit test sweep | M | P1 | ✅ Done |
| TEST-10-03 | Fuzz targets: merge SQL codegen & row identity | M | P2 | ✅ Done |
| TEST-10-04 | DDL-during-concurrent-refresh E2E test | S | P2 | ✅ Done |
| CI-10-02 | Expand e2e-smoke filter | S | P2 | ✅ Done |
| CI-10-03 | Consolidated fuzz recipe (`just fuzz-all`) | S | P3 | ✅ Done |
| CQ-10-01 | Scheduler module decomposition | L | P2 | ✅ Done |

---

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

**Problem:** All concurrent E2E tests used `tokio::time::sleep` to simulate
operation overlap. On a loaded CI runner the fixed delay is insufficient to
guarantee concurrency, producing tests that may pass even when the underlying
behaviour is broken.

**Fix:** Added `wait_for_active_query` helper in `tests/e2e_concurrent_tests.rs`
that polls `pg_stat_activity` until the target query fragment appears (or a
configurable timeout elapses). All four affected tests (`test_pb1_*`,
`test_concurrent_refresh_and_drop`, `test_conc1_alter_while_refresh`,
`test_conc2_drop_while_refresh`) now use this polling helper.

---

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

Added focused `#[cfg(test)]` modules to three modules that had zero unit test
coverage:

- **`src/template_cache.rs`** — hash i64 round-trip, OID round-trip, purge
  guard condition logic.
- **`src/cdc/polling.rs`** — `build_pk_hash_expr` for single/multi/escaped
  column names.
- **`src/cdc/rebuild.rs`** — trigger name format, CDC mode classification,
  replica identity sufficiency.

Other modules in the sweep (`catalog.rs`, `ivm.rs`, `diagnostics.rs`,
`logging.rs`, `metrics_server.rs`, `otel.rs`) already had adequate coverage
from existing test suites.

---

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

Two new libFuzzer targets:

**`fuzz/fuzz_targets/merge_sql_fuzz.rs`** (FUZZ-7):
- Fuzzes `pg_quote_literal`, `parse_hash_bound_spec`, `extract_keyword_int`,
  amplification ratio computation, and `build_content_hash_expr`.
- Validates: no panics, UTF-8 output, deterministic results.

**`fuzz/fuzz_targets/row_id_fuzz.rs`** (FUZZ-8):
- Fuzzes `RowIdSchema` construction and pipeline verification.
- Validates: reflexivity (Any/Derived/PassThrough always compatible with
  themselves), no panics on arbitrary byte input.

Public wrapper module `src/fuzz_pub.rs` added to expose the otherwise-crate-
private pipeline functions to the fuzz harness.

---

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

Added `test_ddl_during_concurrent_refresh` to `tests/e2e_concurrent_tests.rs`:
1. Creates a stream table with 100 rows.
2. Inserts 200 more rows, then fires a refresh and an `ALTER STREAM TABLE`
   concurrently using the `wait_for_active_query` synchronization helper.
3. Asserts catalog status is `ACTIVE` or `ERROR` after the race (no torn state).
4. Runs three convergence refreshes and asserts final row count is 300.

---

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

The PR smoke test now matches five additional pattern families:
- `test_.*join.*`
- `test_.*aggregate.*`
- `test_.*window.*`
- `test_.*subquery.*`

This ensures operator-level regressions in the DVM engine are caught by the
per-PR smoke gate.

---

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

- Added `just fuzz-all` to `justfile` — runs each registered fuzz target for
  a configurable time (default `FUZZ_TIME=60` seconds).
- Added fuzz target table and corpus path documentation to `CONTRIBUTING.md`.

---

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

**Before:** `src/scheduler/mod.rs` was ≈6,700 lines — the largest single file
in the codebase and a high merge-conflict surface.

**After:** Three new focused submodules extracted:

| New file | Content |
|----------|---------|
| `src/scheduler/dispatch.rs` | Parallel dispatch state (`ParallelDispatchState`, `UnitDispatchState`), dynamic worker spawn, worker claiming, orphan reaping, adaptive poll-interval logic |
| `src/scheduler/scheduler_loop.rs` | BGW registration functions, launcher main loop, per-database scheduler main loop |
| `src/scheduler/watermark.rs` | Tick watermark computation, xmin holdback warning, frontier advance helpers |

`mod.rs` is now a re-export façade that re-exports all public items previously
defined inline. All existing public API is preserved with no behaviour change.

The test module in `mod.rs` imports the moved items via
`use super::dispatch::{...}` so all unit tests continue to pass.
