crabidy/quality/jamendo.md

4.7 KiB

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_searchget_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_audioget_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_fieldsget_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_idformat!("{:?}", 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)