Skip to content

feat(syndesmos): external API integration (P3-06)#86

Closed
forkwright wants to merge 1 commit intomainfrom
p3-06/syndesmos
Closed

feat(syndesmos): external API integration (P3-06)#86
forkwright wants to merge 1 commit intomainfrom
p3-06/syndesmos

Conversation

@forkwright
Copy link
Owner

Summary

  • Adds the syndesmos crate: external API integration for Plex, Last.fm, and Tidal
  • Implements ExternalIntegration trait with all four methods: notify_plex_import, scrobble, sync_tidal_want_list, get_artist_data
  • Aggelia event handler for PlexNotifyRequired → Plex library refresh and ScrobbleRequired → Last.fm scrobble
  • Per-service circuit breakers (Plex, Last.fm, Tidal) with configurable threshold (default: 5 failures) and cooldown (default: 5 min)
  • Exponential-backoff retry: 1 initial + 3 retries at 2 s, 8 s, 32 s
  • All integrations degrade gracefully when unconfigured (Ok(()) / Ok(None) / Ok(vec![]))
  • SyndesmosConfig added to horismos (PlexConfig, LastfmConfig, TidalConfig, all Option<_>)
  • Removes unused MockPlexApi::with_failures and fail_count — dead code containing a latent panic (unwrap_err() on a URL that reqwest accepts as valid)

Test count

36 tests, all green:

  • retry: 7 tests (backoff, circuit breaker trip/reset, attempt count)
  • events: 3 tests (PlexNotifyRequired, ScrobbleRequired, cancellation)
  • plex/notify: 4 tests (section mapping, no-section graceful, by-section direct)
  • lastfm/artist: 3 tests (found, not found, JSON parse)
  • lastfm/scrobble: 2 tests (mock parameters, circuit path)
  • lastfm/auth: 3 tests (auth URL, signature sort, format exclusion)
  • tidal/wantlist: 5 tests (delta, event emit, no event, empty, parse)
  • lib (SyndesmosService): 9 tests (all four methods: unconfigured + configured)

Validation results

cargo fmt --all -- --check     OK
cargo clippy -p syndesmos -- -D warnings   OK (zero warnings)
cargo test -p syndesmos        36 passed, 0 failed

Observations

  • MockPlexApi::with_failures used reqwest::Client::new().get("http://invalid.test/").build().unwrap_err() to generate a reqwest::Error. Because http://invalid.test/ is a syntactically valid URL, build() succeeds and unwrap_err() would panic if this path were ever hit. The entire failure-simulation path was removed; retry behavior is tested generically in retry.rs.
  • #[tokio::test(start_paused = true)] requires the test-util tokio feature. The prompt spec omitted it; correctly added to dev-dependencies.

Implements the syndesmos crate: Plex, Last.fm, and Tidal integrations
with per-service circuit breakers, exponential-backoff retry, and
Aggelia event handling for PlexNotifyRequired and ScrobbleRequired.

Removes unused MockPlexApi::with_failures constructor and fail_count
field — dead code containing a latent bug where unwrap_err() on a
valid URL would panic if the code path were ever reached.

Validation: cargo check, clippy -D warnings, 36 tests all green.
@forkwright
Copy link
Owner Author

Duplicate — syndesmos already merged as #83

@forkwright forkwright closed this Mar 14, 2026
@forkwright forkwright deleted the p3-06/syndesmos branch March 14, 2026 07:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant