111 lines
4.7 KiB
Plaintext
111 lines
4.7 KiB
Plaintext
Task: todo 18 – news-server axum wiring (/healthz, /metrics, scheduler, main.rs)
|
||
Date: 2026-09-01
|
||
|
||
Files created/modified:
|
||
- crates/news-server/src/main.rs (rewritten, thin binary + run_once/start_background_loops)
|
||
- crates/news-server/src/bin_support.rs (new runtime/setup helpers)
|
||
- crates/news-server/src/lib.rs (register health + scheduler modules)
|
||
- crates/news-server/src/health.rs (new /healthz + /metrics + Metrics)
|
||
- crates/news-server/src/scheduler.rs (new module root)
|
||
- crates/news-server/src/scheduler/cycle.rs (new PollCycleError + poll_source_once)
|
||
- crates/news-server/src/scheduler/loops.rs (new per-source/digest/pruning loops + tests)
|
||
- crates/news-server/src/scheduler/metrics.rs (new decision metric recording)
|
||
- crates/news-store/src/items.rs (delete_fetched_before + get helpers)
|
||
- crates/news-server/src/notify/digest.rs (return bool flag, no tests moved)
|
||
- crates/news-server/src/notify/digest/tests.rs (new split test module)
|
||
- Cargo.toml (workspace prometheus dependency)
|
||
- crates/news-server/Cargo.toml (crate prometheus dependency)
|
||
|
||
Verification commands
|
||
---------------------
|
||
|
||
1. Formatting
|
||
$ cargo fmt --all --check
|
||
Result: clean
|
||
|
||
2. Clippy
|
||
$ cargo clippy --workspace --all-targets -- -D warnings
|
||
Result: clean
|
||
|
||
3. Workspace tests
|
||
$ cargo test --workspace
|
||
Result: passed (79 tests total)
|
||
- news-cli: 2 passed
|
||
- news-core: 18 passed
|
||
- news-ingest: 4 passed
|
||
- news-server: 33 passed
|
||
- news-store: 22 passed
|
||
|
||
4. File size (scc pure LOC, tests excluded where noted)
|
||
$ scc --no-cocomo --no-complexity --by-file \
|
||
crates/news-server/src/main.rs \
|
||
crates/news-server/src/bin_support.rs \
|
||
crates/news-server/src/health.rs \
|
||
crates/news-server/src/scheduler.rs \
|
||
crates/news-server/src/scheduler/cycle.rs \
|
||
crates/news-server/src/scheduler/loops.rs \
|
||
crates/news-server/src/scheduler/metrics.rs \
|
||
crates/news-server/src/notify/digest.rs \
|
||
crates/news-server/src/notify/digest/tests.rs \
|
||
crates/news-store/src/items.rs
|
||
Result: every production module is under the 250 pure-LOC ceiling
|
||
- main.rs: 154
|
||
- bin_support.rs: 194
|
||
- health.rs: 152
|
||
- scheduler.rs: 8
|
||
- scheduler/cycle.rs: 182
|
||
- scheduler/loops.rs: 246
|
||
- scheduler/metrics.rs: 37
|
||
- notify/digest.rs: 170 (tests moved to digest/tests.rs)
|
||
- news-store/src/items.rs: 228
|
||
|
||
5. Live server QA
|
||
Built binary first to separate compile time from runtime.
|
||
$ cargo build --bin news-server
|
||
$ NEWS_PORT=3999 NEWS_DB_PATH=/tmp/news_qa.db ./target/debug/news-server &
|
||
$ sleep 3
|
||
$ curl -s http://127.0.0.1:3999/healthz
|
||
{"db":"ok","status":"ok"}
|
||
$ curl -s http://127.0.0.1:3999/metrics | head -5
|
||
# HELP news_scrape_duration_seconds Wall time for one source poll cycle
|
||
# TYPE news_scrape_duration_seconds histogram
|
||
news_scrape_duration_seconds_bucket{le="0.005"} 0
|
||
...
|
||
Result: /healthz returned HTTP 200 JSON, /metrics returned HTTP 200 Prometheus text.
|
||
|
||
6. Dry-run once QA
|
||
$ NEWS_DB_PATH=/tmp/news_dryrun.db ./target/debug/news-server --dry-run --once
|
||
Result: exit 0, printed decision rows, no real ntfy calls.
|
||
Sample output:
|
||
Ogwumike has 23 points and 12 rebounds as Sparks beat Storm… 2.400 0.000 50.000 Suppress:BelowThreshold
|
||
... (many more Suppress:BelowThreshold rows)
|
||
exit_code=0
|
||
|
||
Implementation notes
|
||
--------------------
|
||
- Metrics exposed:
|
||
- news_poll_total{source}
|
||
- news_notify_total{lane="normal"|"bypass"|"digest"}
|
||
- news_suppress_total{reason}
|
||
- news_scrape_duration_seconds histogram
|
||
- Per-source scheduler loop respects source.poll_interval_secs, spawns each
|
||
source in its own task, and applies exponential backoff only to the source
|
||
that returned RateLimited.
|
||
- Daily pruning job runs at 03:00 America/Louisville via duration_until_03_00
|
||
and deletes raw_items (and orphan cluster_members) older than 90 days.
|
||
- main.rs now delegates runtime construction to bin_support::build_runtime and
|
||
keeps orchestration helpers (run_once, start_background_loops) in main.rs so
|
||
both files stay under the 250 LOC ceiling.
|
||
- digest.rs tests were split into digest/tests.rs to keep the core module small.
|
||
|
||
Known divergences from plan wording
|
||
-----------------------------------
|
||
- The plan acceptance criteria names the tests `main::healthz_returns_200_...`
|
||
and `main::pruning_job_deletes_...`. The actual tests live at
|
||
`health::tests::healthz_returns_200_when_db_reachable` and
|
||
`scheduler::loops::tests::pruning_job_deletes_items_older_than_90_days_keeps_newer`.
|
||
Behavior and coverage match the acceptance criteria exactly.
|
||
- Default config is built into bin_support::default_config() when
|
||
/config/config.toml is absent, so `cargo run` works outside a container
|
||
without an explicit config file.
|