import at_record/gen/catalog/artist as catalog_artist import at_record/gen/catalog/release as catalog_release import at_record/gen/defs.{CatalogRef} import at_record_server/catalog_deps.{ type Deps, type MbRelease, Deps, MbArtistCredit, MbRelease, } import at_record_server/catalog_entities.{type Entities, Entities, OwnArtist} import at_record_server/discogs_client.{ type ReleaseDetails, DetailArtist, Release, ReleaseDetails, } import at_record_server/oauth/sessions.{type OauthSession} import at_record_server/promotion.{OwnRelease, Promoted} import atproto/constellation.{Backlink, BacklinksPage} import atproto/xrpc import gleam/bit_array import gleam/dict import gleam/dynamic/decode import gleam/http/response import gleam/json import gleam/list import gleam/option.{None, Some} import gleam/result import support fn no_entities() -> Entities { Entities(artists: dict.new(), genres: dict.new()) } fn details() -> ReleaseDetails { ReleaseDetails( primary_image_url: None, artists: [DetailArtist(id: 1, name: "Slint")], genres: ["Rock"], styles: ["Post Rock"], country: Some("US"), released: Some("1991-03-27"), barcodes: ["12345"], ) } const own_did = "did:plc:me" const other_did = "did:plc:someone" fn session() -> OauthSession { support.stub_session() } fn release(id: Int) -> discogs_client.Release { Release( discogs_id: id, title: "Spiderland", artist: "Slint", year: Some(1991), format: Some("LP"), thumb_url: None, cover_url: None, instance_id: None, ) } fn catalog_ref(uri: String) -> defs.CatalogRef { CatalogRef(uri: uri, cid: "bafyexisting", external_ids: None) } fn own_release(uri: String) -> promotion.OwnRelease { OwnRelease(ref: catalog_ref(uri), cover_size: None) } fn discogs_artist_id() -> defs.ExternalId { defs.ExternalId( provider: "discogs", id: "1", url: Some("https://www.discogs.com/artist/1"), ) } fn slint_artist_record( ids: List(defs.ExternalId), ) -> catalog_artist.CatalogArtist { catalog_artist.CatalogArtist( name: "Slint", created_at: "2026-01-01T00:00:00Z", external_ids: Some(ids), based_on: None, disambiguation: None, sort_name: None, supersedes: None, thumb_url: None, ) } /// Entities pre-populated with a dedup-hit Slint artist carrying `ids`. fn entities_with_slint(ids: List(defs.ExternalId)) -> Entities { let uri = "at://" <> own_did <> "/" <> catalog_artist.collection <> "/3art" Entities( artists: dict.from_list([ #("1", OwnArtist(ref: catalog_ref(uri), record: slint_artist_record(ids))), ]), genres: dict.new(), ) } /// Deps that fail the test if the network is touched. fn offline_deps() -> Deps { support.unreachable_catalog_deps() } /// Deps whose backlink index is empty, so adopt_or_mint falls through to mint. fn no_backlinks_deps() -> Deps { no_backlinks_deps_with_mbid(fn(_, _) { None }) } /// Same as `no_backlinks_deps`, but with a stubbed `release_mbid`. fn no_backlinks_deps_with_mbid( release_mbid: fn(List(String), Int) -> option.Option(MbRelease), ) -> Deps { Deps( backlinks: fn(_, _) { Ok(BacklinksPage(total: 0, records: [], cursor: None)) }, fetch_release: fn(_) { panic as "fetch_release must not be called" }, fetch_edit: fn(_) { panic as "fetch_edit must not be called" }, release_mbid:, ) } /// A stub PDS that answers every createRecord with a per-collection uri, and /// runs `inspect` on each request body so a test can assert what was minted. fn inspecting_client(inspect: fn(String) -> Nil) -> xrpc.Client { xrpc.Client(send: fn(req) { let body = bit_array.to_string(req.body) |> result.unwrap("") inspect(body) let collection = json.parse(body, decode.at(["collection"], decode.string)) |> result.unwrap("record") let uri = "at://" <> own_did <> "/" <> collection <> "/1" let out = "{\"uri\": \"" <> uri <> "\", \"cid\": \"bafy\"}" Ok(response.new(200) |> response.set_body(bit_array.from_string(out))) }) } fn dead_client() -> xrpc.Client { xrpc.Client(send: fn(_) { panic as "no PDS writes expected" }) } /// A stub PDS that fails every artist createRecord with a non-429 error and /// answers other collections normally, so an artist mint failure can be forced. fn artist_failing_client() -> xrpc.Client { xrpc.Client(send: fn(req) { let body = bit_array.to_string(req.body) |> result.unwrap("") let collection = json.parse(body, decode.at(["collection"], decode.string)) |> result.unwrap("") case collection == catalog_artist.collection { True -> Ok(response.new(500) |> response.set_body(bit_array.from_string("no"))) False -> { let uri = "at://" <> own_did <> "/" <> collection <> "/1" let out = "{\"uri\": \"" <> uri <> "\", \"cid\": \"bafy\"}" Ok(response.new(200) |> response.set_body(bit_array.from_string(out))) } } }) } /// A stub PDS that answers createRecord with a fixed uri/cid. fn minting_client(uri: String) -> xrpc.Client { xrpc.Client(send: fn(_req) { let body = "{\"uri\": \"" <> uri <> "\", \"cid\": \"bafyminted\"}" Ok(response.new(200) |> response.set_body(bit_array.from_string(body))) }) } fn remote_release(discogs_id id: Int) -> catalog_release.CatalogRelease { catalog_release.CatalogRelease( ..support.blank_catalog_release(), external_ids: Some([discogs_client.external_id(release(id))]), ) } pub fn own_map_hit_reuses_without_network_test() { let existing = own_release("at://" <> own_did <> "/release/3aaa") let own = dict.from_list([#("42", existing)]) let outcome = promotion.adopt_or_mint( offline_deps(), dead_client(), session(), own, no_entities(), release(42), None, None, ) assert outcome.promoted == Some(Promoted(release: existing.ref, origin: "promotion")) assert outcome.own == own } pub fn verified_backlink_is_adopted_test() { let foreign_uri = "at://" <> other_did <> "/dev.mokkenstorm.crate.catalog.release/3bbb" let deps = Deps( backlinks: fn(_subject, _source) { Ok(BacklinksPage( total: 1, records: [ Backlink( did: other_did, collection: "dev.mokkenstorm.crate.catalog.release", rkey: "3bbb", ), ], cursor: None, )) }, fetch_release: fn(uri) { assert uri == foreign_uri Some(#("bafyforeign", remote_release(discogs_id: 42))) }, fetch_edit: fn(_) { panic as "fetch_edit must not be called" }, release_mbid: fn(_, _) { panic as "release_mbid must not be called" }, ) let outcome = promotion.adopt_or_mint( deps, dead_client(), session(), dict.new(), no_entities(), release(42), None, None, ) let assert Some(Promoted(release: adopted, origin: "adoption")) = outcome.promoted assert adopted.uri == foreign_uri assert adopted.cid == "bafyforeign" } pub fn unverified_backlink_falls_through_to_mint_test() { let deps = Deps( backlinks: fn(_, _) { Ok(BacklinksPage( total: 1, records: [Backlink(did: other_did, collection: "c", rkey: "3ccc")], cursor: None, )) }, // The candidate record carries a different discogs id: index noise. fetch_release: fn(_) { Some(#("bafyx", remote_release(discogs_id: 999))) }, fetch_edit: fn(_) { panic as "fetch_edit must not be called" }, release_mbid: fn(_, _) { None }, ) let minted_uri = "at://" <> own_did <> "/release/3new" let outcome = promotion.adopt_or_mint( deps, minting_client(minted_uri), session(), dict.new(), no_entities(), release(42), None, None, ) let assert Some(Promoted(release: minted, origin: "promotion")) = outcome.promoted assert minted.uri == minted_uri assert dict.size(outcome.own) == 1 } pub fn constellation_outage_still_mints_test() { let deps = Deps( backlinks: fn(_, _) { Error(xrpc.RequestFailed("down")) }, fetch_release: fn(_) { panic as "no candidates to verify" }, fetch_edit: fn(_) { panic as "fetch_edit must not be called" }, release_mbid: fn(_, _) { None }, ) let outcome = promotion.adopt_or_mint( deps, minting_client("at://" <> own_did <> "/release/3ddd"), session(), dict.new(), no_entities(), release(42), None, None, ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn mint_with_details_enriches_release_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_release.collection { False -> Nil True -> { assert support.field_string(body, ["record", "artistDisplay"]) == Ok("Slint") assert support.field_strings(body, ["record", "genres"]) == Ok(["Rock"]) assert support.field_strings(body, ["record", "styles"]) == Ok(["Post Rock"]) assert support.field_string(body, ["record", "country"]) == Ok("US") assert support.field_string(body, ["record", "released"]) == Ok("1991-03-27") assert support.field_nested(body, ["record", "identifiers"], ["type"]) == Ok(["Barcode"]) assert support.field_nested(body, ["record", "identifiers"], ["value"]) == Ok(["12345"]) assert support.field_nested(body, ["record", "creditedArtists"], [ "artist", "uri", ]) |> result.map(list.length) == Ok(1) } } }) let outcome = promotion.adopt_or_mint( no_backlinks_deps(), client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted assert dict.size(outcome.entities.artists) == 1 assert dict.size(outcome.entities.genres) == 2 } pub fn mint_without_details_sets_only_artist_display_test() { let client = inspecting_client(fn(body) { assert support.field_string(body, ["record", "artistDisplay"]) == Ok("Slint") assert support.field_present(body, ["record", "genres"]) == False assert support.field_present(body, ["record", "country"]) == False assert support.field_present(body, ["record", "creditedArtists"]) == False }) let outcome = promotion.adopt_or_mint( no_backlinks_deps(), client, session(), dict.new(), no_entities(), release(42), None, None, ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted assert dict.size(outcome.entities.artists) == 0 } pub fn mint_with_resolved_mbid_adds_musicbrainz_external_id_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_release.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs", "musicbrainz"]) assert support.field_nested(body, ["record", "externalIds"], ["id"]) == Ok(["42", "mbid-abc"]) assert support.field_nested(body, ["record", "externalIds"], ["url"]) == Ok([ "https://www.discogs.com/release/42", "https://musicbrainz.org/release/mbid-abc", ]) } } }) let deps = no_backlinks_deps_with_mbid(fn(barcodes, discogs_id) { assert barcodes == ["12345"] assert discogs_id == 42 Some(MbRelease(mbid: "mbid-abc", artist_credits: [])) }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn mint_with_unresolved_mbid_keeps_only_discogs_external_id_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_release.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs"]) } } }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { None }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } fn details_with_artist_name(name: String) -> ReleaseDetails { ReleaseDetails(..details(), artists: [DetailArtist(id: 1, name:)]) } pub fn mint_attaches_musicbrainz_id_to_exact_matching_artist_credit_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_artist.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs", "musicbrainz"]) assert support.field_nested(body, ["record", "externalIds"], ["id"]) == Ok(["1", "artist-mbid-1"]) } } }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "Slint", mbid: "artist-mbid-1"), ]), ) }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn mint_attaches_musicbrainz_id_via_token_match_on_punctuation_drift_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_artist.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs", "musicbrainz"]) assert support.field_nested(body, ["record", "externalIds"], ["id"]) == Ok(["1", "artist-mbid-2"]) } } }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { // MB drops the apostrophe Discogs carries; still a token-match hit. Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "Guns N Roses", mbid: "artist-mbid-2"), ]), ) }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), no_entities(), release(42), None, Some(details_with_artist_name("Guns N' Roses")), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn mint_leaves_artist_without_musicbrainz_id_on_name_mismatch_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_artist.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs"]) } } }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "A Completely Different Artist", mbid: "other"), ]), ) }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn dedup_hit_backfills_musicbrainz_id_via_put_record_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_artist.collection { False -> Nil True -> { // A putRecord at the existing rkey, not a fresh createRecord. assert support.field_string(body, ["rkey"]) == Ok("3art") assert support.field_string(body, ["record", "name"]) == Ok("Slint") assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs", "musicbrainz"]) assert support.field_nested(body, ["record", "externalIds"], ["id"]) == Ok(["1", "artist-mbid-1"]) assert support.field_nested(body, ["record", "externalIds"], ["url"]) == Ok([ "https://www.discogs.com/artist/1", "https://musicbrainz.org/artist/artist-mbid-1", ]) } } }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "Slint", mbid: "artist-mbid-1"), ]), ) }) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), entities_with_slint([discogs_artist_id()]), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted // The dict entry carries the put's new cid, so later credits reuse it. let assert Ok(updated) = dict.get(outcome.entities.artists, "1") assert updated.ref.cid == "bafy" } pub fn dedup_hit_with_existing_musicbrainz_id_is_not_touched_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") assert collection != catalog_artist.collection }) let deps = no_backlinks_deps_with_mbid(fn(_, _) { Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "Slint", mbid: "artist-mbid-1"), ]), ) }) let entities = entities_with_slint([ discogs_artist_id(), defs.ExternalId(provider: "musicbrainz", id: "already-there", url: None), ]) let outcome = promotion.adopt_or_mint( deps, client, session(), dict.new(), entities, release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted let assert Ok(kept) = dict.get(outcome.entities.artists, "1") assert kept.ref.cid == "bafyexisting" } pub fn failed_backfill_still_mints_release_test() { let deps = no_backlinks_deps_with_mbid(fn(_, _) { Some( MbRelease(mbid: "release-mbid", artist_credits: [ MbArtistCredit(name: "Slint", mbid: "artist-mbid-1"), ]), ) }) let outcome = promotion.adopt_or_mint( deps, artist_failing_client(), session(), dict.new(), entities_with_slint([discogs_artist_id()]), release(42), None, Some(details()), ) // Backfill is enrichment, not creation: its failure never withholds the item. let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted assert outcome.item_failed == False assert outcome.report.failed == 0 let assert Ok(kept) = dict.get(outcome.entities.artists, "1") assert kept.ref.cid == "bafyexisting" } pub fn mint_leaves_artist_without_musicbrainz_id_on_mb_miss_test() { let client = inspecting_client(fn(body) { let collection = support.field_string(body, ["collection"]) |> result.unwrap("") case collection == catalog_artist.collection { False -> Nil True -> { assert support.field_nested(body, ["record", "externalIds"], [ "provider", ]) == Ok(["discogs"]) } } }) let outcome = promotion.adopt_or_mint( no_backlinks_deps(), client, session(), dict.new(), no_entities(), release(42), None, Some(details()), ) let assert Some(Promoted(origin: "promotion", ..)) = outcome.promoted } pub fn failed_artist_mint_withholds_release_test() { let outcome = promotion.adopt_or_mint( no_backlinks_deps(), artist_failing_client(), session(), dict.new(), no_entities(), release(42), None, Some(details()), ) // No release minted, so the item never becomes own or gets a shelf genesis. assert outcome.promoted == None assert outcome.item_failed == True assert dict.size(outcome.own) == 0 // The caller can act on the report: a counted, non-rate-limited failure. assert outcome.report.failed >= 1 assert outcome.report.rate_limited == False } pub fn parse_at_uri_extracts_did_collection_rkey_test() { let assert Some(#(did, collection, rkey)) = promotion.parse_at_uri( "at://did:plc:abc123/dev.mokkenstorm.crate.catalog.release/3xyz", ) assert did == "did:plc:abc123" assert collection == "dev.mokkenstorm.crate.catalog.release" assert rkey == "3xyz" } pub fn parse_at_uri_rejects_malformed_uri_test() { assert promotion.parse_at_uri("https://example.com/not-an-at-uri") == None }