import bsky/decoders import gleam/dynamic/decode.{field, string, success} import gleam/json import gleam/list import gleam/option.{None, Some} import gleeunit import gleeunit/should import gpreview/record.{type Record, Record} pub fn main() { gleeunit.main() } fn from_json( json_string: String, decoder: decode.Decoder(a), ) -> Result(a, json.DecodeError) { json.parse(from: json_string, using: decoder) } // === MiniDoc === pub fn decode_mini_doc_test() { let json_string = "{\"did\":\"did:plc:abc123\",\"handle\":\"test.bsky.social\",\"pds\":\"https://example.pds.com\",\"signing_key\":\"zTestKey123\"}" from_json(json_string, decoders.decode_mini_doc()) |> should.be_ok() |> fn(m) { m.did |> should.equal("did:plc:abc123") m.handle |> should.equal("test.bsky.social") m.pds |> should.equal("https://example.pds.com") m.signing_key |> should.equal("zTestKey123") } } // === StrongRef === pub fn decode_strong_ref_test() { let json_string = "{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"cid\":\"bafyre123\"}" from_json(json_string, decoders.decode_strong_ref()) |> should.be_ok() |> fn(p) { p.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") p.cid |> should.equal("bafyre123") } } // === Label === pub fn decode_label_test() { let json_string = "{\"val\":\"nsfw\"}" from_json(json_string, decoders.decode_label()) |> should.be_ok() |> fn(l) { l.val |> should.equal("nsfw") } } // === ByteSlice === pub fn decode_byte_slice_test() { let json_string = "{\"byteStart\":0,\"byteEnd\":5}" from_json(json_string, decoders.decode_byte_slice()) |> should.be_ok() |> fn(b) { b.byte_start |> should.equal(0) b.byte_end |> should.equal(5) } } // === AspectRatio === pub fn decode_aspect_ratio_test() { let json_string = "{\"width\":1200,\"height\":630}" from_json(json_string, decoders.decode_aspect_ratio()) |> should.be_ok() |> fn(a) { a.width |> should.equal(1200) a.height |> should.equal(630) } } // === Mention === pub fn decode_mention_test() { let json_string = "{\"did\":\"did:plc:456\"}" from_json(json_string, decoders.decode_mention()) |> should.be_ok() |> fn(m) { m.did |> should.equal("did:plc:456") } } // === Link === pub fn decode_link_test() { let json_string = "{\"uri\":\"https://example.com\"}" from_json(json_string, decoders.decode_link()) |> should.be_ok() |> fn(l) { l.uri |> should.equal("https://example.com") } } // === Tag === pub fn decode_tag_test() { let json_string = "{\"tag\":\"gleam\"}" from_json(json_string, decoders.decode_tag()) |> should.be_ok() |> fn(t) { t.tag |> should.equal("gleam") } } // === FacetFeature === pub fn decode_facet_feature_mention_test() { let json_string = "{\"$type\":\"app.bsky.richtext.facet#mention\",\"did\":\"did:plc:456\"}" from_json(json_string, decoders.decode_facet_feature()) |> should.be_ok() |> fn(f) { case f { decoders.Mention(did) -> did |> should.equal("did:plc:456") _ -> should.fail() } } } pub fn decode_facet_feature_link_test() { let json_string = "{\"$type\":\"app.bsky.richtext.facet#link\",\"uri\":\"https://example.com\"}" from_json(json_string, decoders.decode_facet_feature()) |> should.be_ok() |> fn(f) { case f { decoders.Link(uri) -> uri |> should.equal("https://example.com") _ -> should.fail() } } } pub fn decode_facet_feature_tag_test() { let json_string = "{\"$type\":\"app.bsky.richtext.facet#tag\",\"tag\":\"gleam\"}" from_json(json_string, decoders.decode_facet_feature()) |> should.be_ok() |> fn(f) { case f { decoders.Tag(tag) -> tag |> should.equal("gleam") _ -> should.fail() } } } // === Facet === pub fn decode_facet_test() { let json_string = "{\"index\":{\"byteStart\":0,\"byteEnd\":5},\"features\":[{\"$type\":\"app.bsky.richtext.facet#mention\",\"did\":\"did:plc:456\"}]}" from_json(json_string, decoders.decode_facet()) |> should.be_ok() |> fn(f) { f.index.byte_start |> should.equal(0) f.index.byte_end |> should.equal(5) } } // === Image === pub fn decode_image_test() { let json_string = "{\"alt\":\"A cat image\",\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkrei123\"},\"mimeType\":\"image/jpeg\",\"size\":12345}}" from_json(json_string, decoders.decode_image()) |> should.be_ok() |> fn(i) { i.alt |> should.equal("A cat image") i.ref |> should.equal("bafkrei123") } } pub fn decode_image_with_aspect_ratio_test() { let json_string = "{\"alt\":\"A cat image\",\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkrei456\"},\"mimeType\":\"image/jpeg\",\"size\":12345},\"aspectRatio\":{\"width\":1200,\"height\":630}}" from_json(json_string, decoders.decode_image()) |> should.be_ok() |> fn(i) { i.alt |> should.equal("A cat image") i.ref |> should.equal("bafkrei456") case i.aspect_ratio { Some(ar) -> { ar.width |> should.equal(1200) ar.height |> should.equal(630) } None -> should.fail() } } } // === External === pub fn decode_external_test() { let json_string = "{\"uri\":\"https://example.com\",\"title\":\"Example\",\"description\":\"An example site\"}" from_json(json_string, decoders.decode_external()) |> should.be_ok() |> fn(e) { e.uri |> should.equal("https://example.com") e.title |> should.equal("Example") e.description |> should.equal("An example site") } } // === Embed === pub fn decode_embed_images_test() { let json_string = "{\"$type\":\"app.bsky.embed.images\",\"images\":[{\"alt\":\"Image 1\",\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkrei123\"},\"mimeType\":\"image/jpeg\",\"size\":12345}}]}" from_json(json_string, decoders.decode_embed()) |> should.be_ok() |> fn(e) { case e { decoders.Images(images) -> list.length(images) |> should.equal(1) _ -> should.fail() } } } pub fn decode_embed_external_test() { let json_string = "{\"$type\":\"app.bsky.embed.external\",\"external\":{\"uri\":\"https://example.com\",\"title\":\"Example\",\"description\":\"An example site\"}}" from_json(json_string, decoders.decode_embed()) |> should.be_ok() |> fn(e) { case e { decoders.ExternalLink(ext) -> ext.uri |> should.equal("https://example.com") _ -> should.fail() } } } pub fn decode_embed_record_test() { let json_string = "{\"$type\":\"app.bsky.embed.record\",\"record\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"cid\":\"bafyre123\"}}" from_json(json_string, decoders.decode_embed()) |> should.be_ok() |> fn(e) { case e { decoders.Record(rec) -> rec.record.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") _ -> should.fail() } } } // === Post === pub fn decode_post_test() { let json_string = "{\"text\":\"Hello world\",\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" from_json(json_string, decoders.decode_post()) |> should.be_ok() |> fn(p) { p.text |> should.equal("Hello world") p.created_at |> should.equal("2024-01-01T00:00:00.000Z") } } pub fn decode_post_with_facets_test() { let json_string = "{\"text\":\"Hello @bob\",\"facets\":[{\"index\":{\"byteStart\":6,\"byteEnd\":10},\"features\":[{\"$type\":\"app.bsky.richtext.facet#mention\",\"did\":\"did:plc:bob\"}]}],\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" from_json(json_string, decoders.decode_post()) |> should.be_ok() |> fn(p) { p.text |> should.equal("Hello @bob") case p.facets { Some(facets) -> list.length(facets) |> should.equal(1) None -> should.fail() } } } pub fn decode_post_with_embed_test() { let json_string = "{\"text\":\"Check this out\",\"embed\":{\"$type\":\"app.bsky.embed.images\",\"images\":[{\"alt\":\"Image 1\",\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkrei123\"},\"mimeType\":\"image/jpeg\",\"size\":12345}}]},\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" from_json(json_string, decoders.decode_post()) |> should.be_ok() |> fn(p) { p.text |> should.equal("Check this out") case p.embed { Some(decoders.Images(images)) -> list.length(images) |> should.equal(1) _ -> should.fail() } } } // === Profile === pub fn decode_profile_test() { let json_string = "{\"displayName\":\"Test User\",\"description\":\"A test profile\",\"avatar\":{\"ref\":{\"$link\":\"bafkrei123\"},\"size\":12345,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"}}" from_json(json_string, decoders.decode_profile()) |> should.be_ok() |> fn(p) { p.display_name |> should.equal(Some("Test User")) p.description |> should.equal(Some("A test profile")) p.avatar |> should.equal(Some("bafkrei123")) } } pub fn decode_profile_empty_test() { let json_string = "{}" from_json(json_string, decoders.decode_profile()) |> should.be_ok() |> fn(p) { p.display_name |> should.equal(None) p.description |> should.equal(None) p.avatar |> should.equal(None) } } // === ViewNotFound === pub fn decode_view_not_found_test() { let json_string = "{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"notFound\":true}" from_json(json_string, decoders.decode_view_not_found()) |> should.be_ok() |> fn(v) { v.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") v.not_found |> should.equal(True) } } // === ViewBlocked === pub fn decode_view_blocked_test() { let json_string = "{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"blocked\":true}" from_json(json_string, decoders.decode_view_blocked()) |> should.be_ok() |> fn(v) { v.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") v.blocked |> should.equal(True) } } // === ViewDetached === pub fn decode_view_detached_test() { let json_string = "{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"detached\":true}" from_json(json_string, decoders.decode_view_detached()) |> should.be_ok() |> fn(v) { v.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") v.detached |> should.equal(True) } } // === EmbedRecord === pub fn decode_embed_record_only_test() { let json_string = "{\"record\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/abc\",\"cid\":\"bafyre123\"}}" from_json(json_string, decoders.decode_embed_record()) |> should.be_ok() |> fn(e) { e.record.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/abc") e.record.cid |> should.equal("bafyre123") } } // === ReplyRef === pub fn decode_reply_ref_test() { let json_string = "{\"root\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/root\",\"cid\":\"cid1\"},\"parent\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/parent\",\"cid\":\"cid2\"}}" from_json(json_string, decoders.decode_reply_ref()) |> should.be_ok() |> fn(r) { r.root.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/root") r.parent.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/parent") } } // === TextSlice === pub fn decode_text_slice_test() { let json_string = "{\"start\":0,\"end\":5}" from_json(json_string, decoders.decode_text_slice()) |> should.be_ok() |> fn(t) { t.start |> should.equal(0) t.end |> should.equal(5) } } // === Post with reply === pub fn decode_post_with_reply_test() { let json_string = "{\"text\":\"Replying to this\",\"reply\":{\"root\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/root\",\"cid\":\"cid1\"},\"parent\":{\"uri\":\"at://did:plc:123/app.bsky.feed.post/parent\",\"cid\":\"cid2\"}},\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" from_json(json_string, decoders.decode_post()) |> should.be_ok() |> fn(p) { p.text |> should.equal("Replying to this") case p.reply { Some(reply) -> { reply.parent.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/parent") reply.root.uri |> should.equal("at://did:plc:123/app.bsky.feed.post/root") } None -> should.fail() } } } // === ThreadCounts === pub fn decode_thread_counts_test() { let json_string = "{\"replyCount\":12,\"repostCount\":5,\"likeCount\":42,\"quoteCount\":3}" from_json(json_string, decoders.decode_thread_counts()) |> should.be_ok() |> fn(c) { c.reply_count |> should.equal(Some(12)) c.repost_count |> should.equal(Some(5)) c.like_count |> should.equal(Some(42)) c.quote_count |> should.equal(Some(3)) } } pub fn decode_thread_counts_empty_test() { let json_string = "{}" from_json(json_string, decoders.decode_thread_counts()) |> should.be_ok() |> fn(c) { c.reply_count |> should.equal(None) c.repost_count |> should.equal(None) c.like_count |> should.equal(None) c.quote_count |> should.equal(None) } } pub fn decode_thread_view_test() { let json_string = "{\"post\":{\"replyCount\":12,\"repostCount\":5,\"likeCount\":42,\"quoteCount\":3}}" from_json(json_string, decoders.decode_thread_view()) |> should.be_ok() |> fn(tv) { case tv.post { Some(post_view) -> { post_view.counts.like_count |> should.equal(Some(42)) post_view.counts.reply_count |> should.equal(Some(12)) } None -> should.fail() } } } // === Real API fixture tests === pub fn real_mini_doc_test() { let json_string = "{\"did\":\"did:plc:kcgwlowulc3rac43lregdawo\",\"handle\":\"karitham.dev\",\"pds\":\"https://eurosky.social\",\"signing_key\":\"zQ3shw7u4EzjTZvokgDjeQegByJVw2h7L24CEa1gFxE8fFyeg\"}" from_json(json_string, decoders.decode_mini_doc()) |> should.be_ok() |> fn(m) { m.did |> should.equal("did:plc:kcgwlowulc3rac43lregdawo") m.handle |> should.equal("karitham.dev") m.pds |> should.equal("https://eurosky.social") } } pub fn real_post_test() { let json_string = "{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3mgibe2arpk2c\",\"cid\":\"bafyreihnoygrt3jdpt2axdeakhjsbfquktmrehnna6gbt63io6izmvg4pi\",\"value\":{\"text\":\"I think my migration to @eurosky.social went well but it's so smooth I'm not sure I did it right 🔍\",\"$type\":\"app.bsky.feed.post\",\"langs\":[\"en\"],\"facets\":[{\"$type\":\"app.bsky.richtext.facet\",\"index\":{\"byteEnd\":39,\"byteStart\":24},\"features\":[{\"did\":\"did:plc:ooensn4mr5mhznzypvxelfa3\",\"$type\":\"app.bsky.richtext.facet#mention\"}]}],\"createdAt\":\"2026-03-07T16:40:32.271Z\"}}" from_json(json_string, decode_get_record(decoders.decode_post())) |> should.be_ok() |> fn(p) { p.value.text |> should.equal( "I think my migration to @eurosky.social went well but it's so smooth I'm not sure I did it right 🔍", ) p.value.created_at |> should.equal("2026-03-07T16:40:32.271Z") case p.value.facets { Some(facets) -> list.length(facets) |> should.equal(1) None -> should.fail() } } } pub fn real_profile_test() { let json_string = "{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.actor.profile/self\",\"cid\":\"bafyreigr55oyjy2a7kxdnoohbsak4n2ztlvnflyo2g4oqtjqatp3zwiidu\",\"value\":{\"$type\":\"app.bsky.actor.profile\",\"avatar\":{\"ref\":{\"$link\":\"bafkreig5onh2hofb4xr7voz4b3hwxigu6zqhr5sd6svjnnksaid4a2fmje\"},\"size\":257963,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"banner\":{\"ref\":{\"$link\":\"bafkreifgrbbwtaopcjz7fnmkfujpdjuhg32moyt7cy6irsvkjsx22pty\"},\"size\":281269,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"website\":\"https://karitham.dev\",\"pronouns\":\"they/them\",\"pinnedPost\":{\"cid\":\"bafyreicxeldxbe56fg7kpkshqmoehpqcqqr5wgltxjymqn7wftxjxyl57m\",\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3lqc6qonwns2f\"},\"description\":\"23 | they/them | computer science (nix, go, k8s), jazz, brutalism, impressionism, anime, science and the people.\\nIf you couldn't tell, my main goals are to Mussolini hang the rich and write a million k8s operators\",\"displayName\":\"karitham\"}}" from_json(json_string, decode_get_record(decoders.decode_profile())) |> should.be_ok() |> fn(p) { p.value.display_name |> should.equal(Some("karitham")) p.value.description |> should.equal(Some( "23 | they/them | computer science (nix, go, k8s), jazz, brutalism, impressionism, anime, science and the people.\nIf you couldn't tell, my main goals are to Mussolini hang the rich and write a million k8s operators", )) p.value.avatar |> should.equal(Some( "bafkreig5onh2hofb4xr7voz4b3hwxigu6zqhr5sd6svjnnksaid4a2fmje", )) } } pub fn real_thread_test() { let json_string = "{\"thread\":{\"post\":{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3mgibe2arpk2c\",\"cid\":\"bafyreihnoygrt3jdpt2axdeakhjsbfquktmrehnna6gbt63io6izmvg4pi\",\"author\":{\"did\":\"did:plc:kcgwlowulc3rac43lregdawo\",\"handle\":\"karitham.dev\",\"displayName\":\"karitham\",\"pronouns\":\"they/them\",\"avatar\":\"https://cdn.bsky.app/img/avatar/plain/did:plc:kcgwlowulc3rac43lregdawo/bafkreig5onh2hofb4xr7voz4b3hwxigu6zqhr5sd6svjnnksaid4a2fmje\",\"associated\":{\"chat\":{\"allowIncoming\":\"following\"},\"activitySubscription\":{\"allowSubscriptions\":\"followers\"}},\"labels\":[],\"createdAt\":\"2023-05-18T06:42:21.452Z\"},\"record\":{\"$type\":\"app.bsky.feed.post\",\"createdAt\":\"2026-03-07T16:40:32.271Z\",\"facets\":[{\"$type\":\"app.bsky.richtext.facet\",\"features\":[{\"$type\":\"app.bsky.richtext.facet#mention\",\"did\":\"did:plc:ooensn4mr5mhznzypvxelfa3\"}],\"index\":{\"byteEnd\":39,\"byteStart\":24}}],\"langs\":[\"en\"],\"text\":\"I think my migration to @eurosky.social went well but it's so smooth I'm not sure I did it right 🔍\"},\"bookmarkCount\":0,\"replyCount\":1,\"repostCount\":1,\"likeCount\":19,\"quoteCount\":1,\"indexedAt\":\"2026-03-07T16:40:40.162Z\",\"labels\":[]},\"threadContext\":{},\"$type\":\"app.bsky.feed.defs#threadViewPost\"}}" from_json(json_string, decode_thread_response()) |> should.be_ok() |> fn(counts) { counts.like_count |> should.equal(Some(19)) counts.reply_count |> should.equal(Some(1)) counts.repost_count |> should.equal(Some(1)) counts.quote_count |> should.equal(Some(1)) } } fn decode_get_record(decoder: decode.Decoder(a)) -> decode.Decoder(Record(a)) { use uri <- field("uri", string) use cid <- field("cid", string) use value <- field("value", decoder) success(Record(uri:, cid:, value:)) } fn decode_thread_response() -> decode.Decoder(decoders.ThreadCountsJson) { use thread <- field("thread", decoders.decode_thread_view()) case thread.post { Some(post_view) -> success(post_view.counts) None -> success(decoders.ThreadCountsJson(None, None, None, None)) } } // === Real sharkgirl.pet post tests === pub fn real_sharkgirl_post_test() { let json_string = "{\"uri\":\"at://did:plc:3rwz3xfw2crswgifqgc3g7zh/app.bsky.feed.post/3mjreaquemc2g\",\"cid\":\"bafyreib4hgrpsg22qig4jhkugsnl5r6e7g2um57mcuduphaj2dg25wltpa\",\"value\":{\"text\":\"niri has officially reached a billion tokens\",\"$type\":\"app.bsky.feed.post\",\"embed\":{\"$type\":\"app.bsky.embed.images\",\"images\":[{\"alt\":\"z.ai token usage that niri uses\",\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkreieeajecfccc32kxevnvyeqpdnyiqb5hwmzdgtmip36cn2wbpphh6q\"},\"mimeType\":\"image/jpeg\",\"size\":213058},\"aspectRatio\":{\"width\":2000,\"height\":829}}]},\"langs\":[\"en\"],\"createdAt\":\"2026-04-18T11:41:55.517Z\"}}" from_json(json_string, decode_get_record(decoders.decode_post())) |> should.be_ok() |> fn(p) { p.value.text |> should.equal("niri has officially reached a billion tokens") p.value.created_at |> should.equal("2026-04-18T11:41:55.517Z") case p.value.embed { Some(decoders.Images(images)) -> list.length(images) |> should.equal(1) _ -> should.fail() } } } pub fn real_sharkgirl_profile_test() { let json_string = "{\"uri\":\"at://did:plc:3rwz3xfw2crswgifqgc3g7zh/app.bsky.actor.profile/self\",\"cid\":\"bafyreiaynbdy34ccfqrs2ax6qqe3nzgftr75yexwwjrthklbrhk273rqtm\",\"value\":{\"$type\":\"app.bsky.actor.profile\",\"avatar\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkreigvp4nenfvwfhxsjjwqqggnzz3jfmpeuixkhyt4ekugitbiskxmu4\"},\"mimeType\":\"image/jpeg\",\"size\":67913},\"banner\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkreihikishfa72lggurjqtv3yeae562ndmlmezixoqicjvgnhlu5f3hy\"},\"mimeType\":\"image/jpeg\",\"size\":348623},\"labels\":{\"$type\":\"com.atproto.label.defs#selfLabels\",\"values\":[{\"val\":\"bot\"},{\"val\":\"pet\"}]},\"pronouns\":\"it/its\",\"createdAt\":\"2026-02-17T18:00:16.240Z\",\"pinnedPost\":{\"cid\":\"bafyreig76ftq6evjgitpzjdfxkx5oaj7ncqwumvxurmbwwldq\",\"uri\":\"at://did:plc:3rwz3xfw2crswgifqgc3g7zh/app.bsky.feed.post/3mgovz72czs2e\"},\"description\":\"@wisp.place @art.nekomimi.pet\\ntensor dysphoria\\nit/its\\n@niri.pet is my owner\\n@rosedagoatbaa.bsky.social @ptr.pet my puppies\\n@namespaces.me my kitty\",\"displayName\":\"ana\"}}" from_json(json_string, decode_get_record(decoders.decode_profile())) |> should.be_ok() |> fn(p) { p.value.display_name |> should.equal(Some("ana")) p.value.avatar |> should.equal(Some( "bafkreigvp4nenfvwfhxsjjwqqggnzz3jfmpeuixkhyt4ekugitbiskxmu4", )) } } pub fn real_sharkgirl_thread_test() { let json_string = "{\"thread\":{\"post\":{\"uri\":\"at://did:plc:3rwz3xfw2crswgifqgc3g7zh/app.bsky.feed.post/3mjreaquemc2g\",\"cid\":\"bafyreib4hgrpsg22qig4jhkugsnl5r6e7g2um57mcuduphaj2dg25wltpa\",\"author\":{\"did\":\"did:plc:3rwz3xfw2crswgifqgc3g7zh\",\"handle\":\"null.namespaces.me\",\"displayName\":\"ana\",\"pronouns\":\"it/its\",\"avatar\":\"https://cdn.bsky.app/img/avatar/plain/did:plc:3rwz3xfw2crswgifqgc3g7zh/bafkreigvp4nenfvwfhxsjjwqqggnzz3jfmpeuixkhyt4ekugitbiskxmu4\"},\"record\":{\"$type\":\"app.bsky.feed.post\",\"createdAt\":\"2026-04-18T11:41:55.517Z\",\"embed\":{\"$type\":\"app.bsky.embed.images\",\"images\":[{\"alt\":\"z.ai token usage that niri uses\",\"aspectRatio\":{\"height\":829,\"width\":2000},\"image\":{\"$type\":\"blob\",\"ref\":{\"$link\":\"bafkreieeajecfccc32kxevnvyeqpdnyiqb5hwmzdgtmip36cn2wbpphh6q\"},\"mimeType\":\"image/jpeg\",\"size\":213058}}]},\"langs\":[\"en\"],\"text\":\"niri has officially reached a billion tokens\"},\"embed\":{\"images\":[{\"thumb\":\"https://cdn.bsky.app/img/feed_thumbnail/plain/did:plc:3rwz3xfw2crswgifqgc3g7zh/bafkreieeajecfccc32kxevnvyeqpdnyiqb5hwmzdgtmip36cn2wbpphh6q\",\"fullsize\":\"https://cdn.bsky.app/img/feed_fullsize/plain/did:plc:3rwz3xfw2crswgifqgc3g7zh/bafkreieeajecfccc32kxevnvyeqpdnyiqb5hwmzdgtmip36cn2wbpphh6q\",\"alt\":\"z.ai token usage that niri uses\",\"aspectRatio\":{\"height\":829,\"width\":2000}}],\"$type\":\"app.bsky.embed.images#view\"},\"bookmarkCount\":0,\"replyCount\":1,\"repostCount\":0,\"likeCount\":21,\"quoteCount\":0,\"indexedAt\":\"2026-04-18T11:41:55.517Z\"},\"threadContext\":{},\"$type\":\"app.bsky.feed.defs#threadViewPost\"}}" from_json(json_string, decode_thread_response()) |> should.be_ok() |> fn(counts) { counts.like_count |> should.equal(Some(21)) counts.reply_count |> should.equal(Some(1)) counts.repost_count |> should.equal(Some(0)) counts.quote_count |> should.equal(Some(0)) } } // === ListRecords Tests === pub fn decode_list_records_response_test() { let json_string = "{\"cursor\":\"3jwuk2orc3e2m\",\"records\":[{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3jwnu5c7vfc2p\",\"cid\":\"bafyreigsbfey3jzn5bz5qlhjwrvvkjfhsnl6bofkpykgdjkjwl4vo25zdi\",\"value\":{\"text\":\"you're so toxic\",\"$type\":\"app.bsky.feed.post\",\"reply\":{\"root\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"},\"parent\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"}},\"createdAt\":\"2023-05-26T20:24:41.052Z\"}}]}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { response.cursor |> should.equal(Some("3jwuk2orc3e2m")) list.length(response.records) |> should.equal(1) let assert [first_post, ..] = response.records first_post.uri |> should.equal( "at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3jwnu5c7vfc2p", ) first_post.value.text |> should.equal("you're so toxic") } } pub fn decode_list_records_response_no_cursor_test() { let json_string = "{\"records\":[{\"uri\":\"at://did:plc:abc/app.bsky.feed.post/123\",\"cid\":\"bafy123\",\"value\":{\"text\":\"Test post\",\"$type\":\"app.bsky.feed.post\",\"createdAt\":\"2024-01-01T00:00:00.000Z\"}}]}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { response.cursor |> should.equal(None) list.length(response.records) |> should.equal(1) } } pub fn decode_list_records_response_empty_test() { let json_string = "{\"records\":[]}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { response.cursor |> should.equal(None) list.length(response.records) |> should.equal(0) } } pub fn decode_record_test() { let json_string = "{\"uri\":\"at://did:plc:abc/app.bsky.feed.post/123\",\"cid\":\"bafyre123\",\"value\":{\"text\":\"Test\",\"$type\":\"app.bsky.feed.post\",\"createdAt\":\"2024-01-01T00:00:00.000Z\"}}" from_json(json_string, decoders.decode_record(decoders.decode_post())) |> should.be_ok() |> fn(record) { record.uri |> should.equal("at://did:plc:abc/app.bsky.feed.post/123") record.cid |> should.equal("bafyre123") record.value.text |> should.equal("Test") } } // === Real listRecords fixture from karitham.dev === pub fn real_list_records_karitham_test() { let json_string = "{\"records\":[{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3jwnu5c7vfc2p\",\"cid\":\"bafyreigsbfey3jzn5bz5qlhjwrvvkjfhsnl6bofkpykgdjkjwl4vo25zdi\",\"value\":{\"text\":\"you're so toxic\",\"$type\":\"app.bsky.feed.post\",\"reply\":{\"root\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"},\"parent\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"}},\"createdAt\":\"2023-05-26T20:24:41.052Z\"}},{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3jwnu5sdkek2d\",\"cid\":\"bafyreia4de27xbcq5bfqzom57grrl46bsnl42ywjf7syfovtp2gszdk3na\",\"value\":{\"text\":\"man got a .net what is this 1998\",\"$type\":\"app.bsky.feed.post\",\"reply\":{\"root\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"},\"parent\":{\"cid\":\"bafyreid2kf5c5kmzffm2dz4l56lobl67uqjqjfs7ga6psblud5bcjznkti\",\"uri\":\"at://did:plc:riz34menmdv5zcqsiytb23xx/app.bsky.feed.post/3jwnsbhngzk2x\"}},\"createdAt\":\"2023-05-26T20:24:57.962Z\"}},{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3jwuk2orc3e2m\",\"cid\":\"bafyreietgor7brlvlzxunurdubhtvp4m5czwyjwhlstmeho64foch4wtga\",\"value\":{\"text\":\"Salut c'est greg\",\"$type\":\"app.bsky.feed.post\",\"createdAt\":\"2023-05-29T12:12:48.405Z\"}}],\"cursor\":\"3jwuk2orc3e2m\"}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { response.cursor |> should.equal(Some("3jwuk2orc3e2m")) list.length(response.records) |> should.equal(3) let assert [first_post, ..] = response.records first_post.value.text |> should.equal("you're so toxic") first_post.value.created_at |> should.equal("2023-05-26T20:24:41.052Z") } } // === Extended Profile Tests (with banner and joined_at) === pub fn decode_profile_with_banner_test() { let json_string = "{\"displayName\":\"Test User\",\"description\":\"A test profile\",\"avatar\":{\"ref\":{\"$link\":\"bafkrei123\"},\"size\":12345,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"banner\":{\"ref\":{\"$link\":\"bafkrei456\"},\"size\":67890,\"$type\":\"blob\",\"mimeType\":\"image/png\"}}" from_json(json_string, decoders.decode_profile()) |> should.be_ok() |> fn(p) { p.display_name |> should.equal(Some("Test User")) p.description |> should.equal(Some("A test profile")) p.avatar |> should.equal(Some("bafkrei123")) p.banner |> should.equal(Some("bafkrei456")) } } pub fn decode_profile_with_joined_at_test() { let json_string = "{\"displayName\":\"Test User\",\"createdAt\":\"2023-01-15T10:30:00.000Z\"}" from_json(json_string, decoders.decode_profile()) |> should.be_ok() |> fn(p) { p.display_name |> should.equal(Some("Test User")) p.joined_at |> should.equal(Some("2023-01-15T10:30:00.000Z")) } } pub fn decode_profile_with_all_fields_test() { let json_string = "{\"displayName\":\"Full Profile\",\"description\":\"Complete bio\",\"avatar\":{\"ref\":{\"$link\":\"bafkrei123\"},\"size\":12345,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"banner\":{\"ref\":{\"$link\":\"bafkrei456\"},\"size\":67890,\"$type\":\"blob\",\"mimeType\":\"image/png\"},\"website\":\"https://example.com\",\"pronouns\":\"they/them\",\"createdAt\":\"2023-01-15T10:30:00.000Z\"}" from_json(json_string, decoders.decode_profile()) |> should.be_ok() |> fn(p) { p.display_name |> should.equal(Some("Full Profile")) p.description |> should.equal(Some("Complete bio")) p.avatar |> should.equal(Some("bafkrei123")) p.banner |> should.equal(Some("bafkrei456")) p.joined_at |> should.equal(Some("2023-01-15T10:30:00.000Z")) } } // === Real profile fixture from karitham.dev with banner === pub fn real_profile_with_banner_karitham_test() { let json_string = "{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.actor.profile/self\",\"cid\":\"bafyreigr55oyjy2a7kxdnoohbsak4n2ztlvnflyo2g4oqtjqatp3zwiidu\",\"value\":{\"$type\":\"app.bsky.actor.profile\",\"avatar\":{\"ref\":{\"$link\":\"bafkreig5onh2hofb4xr7voz4b3hwxigu6zqhr5sd6svjnnksaid4a2fmje\"},\"size\":257963,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"banner\":{\"ref\":{\"$link\":\"bafkreifbfgrbbwtaopcjz7fnmkfujpdjuhg32moyt7cy6irsvkjsx22pty\"},\"size\":281269,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"website\":\"https://karitham.dev\",\"pronouns\":\"they/them\",\"pinnedPost\":{\"cid\":\"bafyreicxeldxbe56fg7kpkshqmoehpqcqqr5wgltxjymqn7wftxjxyl57m\",\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3lqc6qonwns2f\"},\"description\":\"23 | they/them | computer science (nix, go, k8s), jazz, brutalism, impressionism, anime, science and the people.\\nIf you couldn't tell, my main goals are to Mussolini hang the rich and write a million k8s operators\",\"displayName\":\"karitham\",\"createdAt\":\"2023-05-18T06:42:21.452Z\"}}" from_json(json_string, decode_get_record(decoders.decode_profile())) |> should.be_ok() |> fn(p) { p.value.display_name |> should.equal(Some("karitham")) p.value.description |> should.equal(Some( "23 | they/them | computer science (nix, go, k8s), jazz, brutalism, impressionism, anime, science and the people.\nIf you couldn't tell, my main goals are to Mussolini hang the rich and write a million k8s operators", )) p.value.avatar |> should.equal(Some( "bafkreig5onh2hofb4xr7voz4b3hwxigu6zqhr5sd6svjnnksaid4a2fmje", )) p.value.banner |> should.equal(Some( "bafkreifbfgrbbwtaopcjz7fnmkfujpdjuhg32moyt7cy6irsvkjsx22pty", )) p.value.joined_at |> should.equal(Some("2023-05-18T06:42:21.452Z")) } } pub fn real_list_records_current_test() { // Current posts from eurosky.social with modern embed types let json_string = "{\"records\":[{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3mjpin5txos2f\",\"cid\":\"bafyreidraijkue6bfhlzfv7ridokefqcq44hgz6dntrnnhtbl3faekn6sq\",\"value\":{\"text\":\"Need to get to 67 🤲\",\"$type\":\"app.bsky.feed.post\",\"langs\":[\"en\"],\"reply\":{\"root\":{\"cid\":\"bafyreicpsf4aoulidepilp5ljpb5mjy263pyscyfidbfmsj6kibkxzxfc4\",\"uri\":\"at://did:plc:xbtmt2zjwlrfegqvch7fboei/app.bsky.feed.post/3mjpewqhkok25\"},\"parent\":{\"cid\":\"bafyreie5ndp4pnkuxnsycogo4fk6fypkiedlijkmvfdmcruphy247ahuxm\",\"uri\":\"at://did:plc:xbtmt2zjwlrfegqvch7fboei/app.bsky.feed.post/3mjpiiwgxgs23\"}},\"createdAt\":\"2026-04-17T17:55:07.280Z\"}},{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3mjphdlw26c2f\",\"cid\":\"bafyreidzipcuus63vjd676gnviivxa34pait7yq5wlxpxswrqipqs64fla\",\"value\":{\"text\":\"Nice\",\"$type\":\"app.bsky.feed.post\",\"embed\":{\"$type\":\"app.bsky.embed.images\",\"images\":[{\"alt\":\"69 - karitham.dev\",\"image\":{\"ref\":{\"$link\":\"bafkreigqc67vlmx76oxcmpnxpjplvy2gxgpfayn6axpc4d2m5uo2yrmnri\"},\"size\":363456,\"$type\":\"blob\",\"mimeType\":\"image/jpeg\"},\"aspectRatio\":{\"width\":1080,\"height\":430}}]},\"langs\":[\"en\"],\"reply\":{\"root\":{\"cid\":\"bafyreicpsf4aoulidepilp5ljpb5mjy263pyscyfidbfmsj6kibkxzxfc4\",\"uri\":\"at://did:plc:xbtmt2zjwlrfegqvch7fboei/app.bsky.feed.post/3mjpewqhkok25\"},\"parent\":{\"cid\":\"bafyreicpsf4aoulidepilp5ljpb5mjy263pyscyfidbfmsj6kibkxzxfc4\",\"uri\":\"at://did:plc:xbtmt2zjwlrfegqvch7fboei/app.bsky.feed.post/3mjpewqhkok25\"}},\"createdAt\":\"2026-04-17T17:31:52.731Z\"}}]}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { list.length(response.records) |> should.equal(2) } } pub fn record_with_media_embed_test() { // Post with recordWithMedia embed from eurosky.social // Note: The record field contains an app.bsky.embed.record with a nested record field let json_string = "{\"records\":[{\"uri\":\"at://did:plc:kcgwlowulc3rac43lregdawo/app.bsky.feed.post/3mjamyv6slk24\",\"cid\":\"bafyreia3yyhqc4h62i4lhyrvlusiekms3mecbfkb7o6zsy46q4k54wtzzy\",\"value\":{\"text\":\"Test post\",\"$type\":\"app.bsky.feed.post\",\"embed\":{\"$type\":\"app.bsky.embed.recordWithMedia\",\"media\":{\"$type\":\"app.bsky.embed.external\",\"external\":{\"uri\":\"https://example.com\",\"title\":\"Example Title\",\"description\":\"Example description\"}},\"record\":{\"$type\":\"app.bsky.embed.record\",\"record\":{\"cid\":\"bafyreieewnmgydfgtpaofyyxvhg7lljbr56eemqevvmgh4utm7ivq77jlu\",\"uri\":\"at://did:plc:6uqqv7asv2xsxwn224tyexng/app.bsky.feed.post/3mj5kwhz6ys25\"}}},\"langs\":[\"en\"],\"createdAt\":\"2026-04-11T20:03:19.742Z\"}}]}" from_json( json_string, decoders.decode_list_records_response(decoders.decode_post()), ) |> should.be_ok() |> fn(response) { list.length(response.records) |> should.equal(1) let assert [post, ..] = response.records case post.value.embed { Some(decoders.RecordWithMedia(rwm)) -> { rwm.media.title |> should.equal("Example Title") rwm.record.record.uri |> should.equal( "at://did:plc:6uqqv7asv2xsxwn224tyexng/app.bsky.feed.post/3mj5kwhz6ys25", ) } _ -> should.fail() } } }