78 lines
4.7 KiB
Markdown
78 lines
4.7 KiB
Markdown
# Quality gates — jamendo provider
|
|
|
|
Gates for the `/jamendo` provider (architecture/jamendo-provider.md). Automatic
|
|
tests live inline in `jamendody/src/tests.rs` (unit, over a `FakeJam`) and are
|
|
named in each gate. LLM gates are read-and-reason checks an implementing agent
|
|
verifies.
|
|
|
|
## Automatic tests (jamendody/src/tests.rs)
|
|
|
|
- **T1 `root_has_only_search`** — `get_lib_root` exposes exactly one child,
|
|
`/jamendo/search` (creatable), and the root is not queueable. (D3)
|
|
- **T2 path parsing** (`*_parse`, `foreign_and_malformed_paths_reject`) —
|
|
`/jamendo`, `/jamendo/search`, `/jamendo/search/<term>`, `/jamendo/track/<id>`,
|
|
`/jamendo/album/<id>` parse to their variants; foreign roots, empty segments,
|
|
wrong arity, and unknown tags are `MalformedPath`. (D3)
|
|
- **T3 `is_track_path_only_for_canonical_track`** — true only for
|
|
`/jamendo/track/<id>`; false for albums, search nodes, root. (D3/D4)
|
|
- **T4 `search_term_lists_tracks_and_albums`** — a search node carries matching
|
|
tracks as rows at canonical `/jamendo/track/<id>` paths and matching albums as
|
|
**queueable** container children at `/jamendo/album/<id>`; the node is
|
|
queueable and `is_downloadable`. (D3/D4)
|
|
- **T5 `album_node_lists_tracks`** — `/jamendo/album/<id>` lists the album's
|
|
tracks as canonical rows; node is queueable + downloadable. (D3)
|
|
- **T6 `get_urls_for_track_returns_audio`** — `get_urls_for_track` on a track
|
|
path returns exactly the backend `audio` URL; a non-track path is
|
|
`MalformedPath`, not a stream. (D4)
|
|
- **T7 `track_metadata_maps_fields`** — `get_metadata_for_track` maps
|
|
`name→title`, `artist_name→artist`, `album_name→Album.title`,
|
|
`duration(secs)→Track.duration` unchanged, `provider_item_id == "track:<id>"`.
|
|
(D4)
|
|
- **T8 `create_rename_delete_search_term`** — creating a term adds an editable +
|
|
deletable child under `/jamendo/search`; empty/whitespace title →
|
|
`InvalidInput`; rename moves the term; delete removes it. (D3)
|
|
- **T9 `backend_failures_are_typed`** — a backend whose every call errors makes
|
|
`get_lib_node` return `ProviderError::FetchError`, never panics. (D2)
|
|
- **T10 `settings_debug_redacts_client_id`** — `format!("{:?}", settings)` with
|
|
a `client_id` set does not contain the id and shows `<redacted>`. (D5)
|
|
- **T11 `duration_is_seconds_not_ms`** — a track whose backend `duration_secs`
|
|
is 210 yields `Track.duration == Some(210)` (guards against a ms/seconds
|
|
regression like the web bug). (D1/D4)
|
|
|
|
## LLM quality gates (read-and-reason)
|
|
|
|
- **G1 — no panics on external conditions.** No `unwrap`/`expect`/`panic!` on
|
|
network, decode, config, or path input in `jamendody`. Lock poisoning
|
|
degrades to empty (`unwrap_or_default`), never panics. (Hard rule)
|
|
- **G2 — typed error boundary.** Every `JamApi` failure is a `FetchError`
|
|
variant; the provider maps it to `ProviderError` (`FetchError` /
|
|
`MalformedPath` / `NotSupported` / `InvalidInput`) and logs the typed cause.
|
|
No `color-eyre`/`anyhow` leaks across the trait. (Hard rule)
|
|
- **G3 — client_id is redacted everywhere.** `Settings` and `JamApi` have
|
|
hand-written `Debug` that redact `client_id`; it never appears in a log line,
|
|
and a resolved `audio` URL (may carry a signed token) is never logged. (Hard
|
|
rule: redact secrets)
|
|
- **G4 — timeouts on every external call.** The `reqwest` client is built with a
|
|
timeout from `call_timeout_secs`; no unbounded HTTP call exists. (Hard rule)
|
|
- **G5 — bounded results, no silent caps.** `search_results` (≤ API max 200) and
|
|
`album_tracks` bound listings; when a cap truncates, it is `log`-ged
|
|
(`debug`/`warn`), not silent. (Hard rule: no silent caps)
|
|
- **G6 — non-fatal init.** A missing/empty `client_id` (or unparseable
|
|
`jamendo.toml`) makes `init` return a typed `Config` error; the orchestrator
|
|
maps it to `None` so only `/jamendo` is lost, never the server. No blocking
|
|
network in `build()`. (D1/D5)
|
|
- **G7 — canonical leaf independence.** Track rows and album children always use
|
|
`/jamendo/track/<id>` and `/jamendo/album/<id>` regardless of the branch they
|
|
were reached through, so playback/expansion never depends on browse context.
|
|
(D3)
|
|
- **G8 — no proto/wire/TUI/player change.** The feature is a pure path-prefix
|
|
subtree: no edit to `crabidy.proto`, `ProviderCommand`, the TUI, or
|
|
`audio-player`. Search-term flows reuse the existing create/rename/delete RPCs.
|
|
(Assumptions)
|
|
- **G9 — defensive DTOs.** Wire structs use `#[serde(default)]`; a missing field
|
|
degrades (empty/`None`), a renamed field is a local `JamApi` fix, never a
|
|
crash. Ids decode whether numeric or string. (R3)
|
|
- **G10 — docs.** Public items (`Client`, `Settings`, `PROVIDER_ROOT`, the `Jam`
|
|
trait and its methods, domain types) carry doc comments stating intent and
|
|
error behavior. (Preference)
|