import Testing import Foundation @testable import EffemKit @Suite("PodcastRef") struct PodcastRefTests { @Test func decodesFromJSON() throws { let json = """ {"feedId": 75075, "feedUrl": "https://example.com/feed.xml", "podcastGuid": "917393e3-1b1e-5cef-ace4-edaa54e1f810"} """.data(using: .utf8)! let ref = try JSONDecoder().decode(PodcastRef.self, from: json) #expect(ref.feedId == 75075) #expect(ref.feedUrl == "https://example.com/feed.xml") #expect(ref.podcastGuid == "917393e3-1b1e-5cef-ace4-edaa54e1f810") } @Test func decodesWithMinimalFields() throws { let json = """ {"feedId": 42} """.data(using: .utf8)! let ref = try JSONDecoder().decode(PodcastRef.self, from: json) #expect(ref.feedId == 42) #expect(ref.feedUrl == nil) #expect(ref.podcastGuid == nil) } @Test func roundTrips() throws { let ref = PodcastRef(feedId: 100, feedUrl: "https://example.com/rss", podcastGuid: "abc-123") let data = try JSONEncoder().encode(ref) let decoded = try JSONDecoder().decode(PodcastRef.self, from: data) #expect(ref == decoded) } @Test func toRecordIncludesOnlyNonNilFields() { let minimal = PodcastRef(feedId: 1) let record = minimal.toRecord() #expect(record["feedId"] as? Int == 1) #expect(record["feedUrl"] == nil) #expect(record["podcastGuid"] == nil) let full = PodcastRef(feedId: 2, feedUrl: "https://x.com/rss", podcastGuid: "guid") let fullRecord = full.toRecord() #expect(fullRecord["feedId"] as? Int == 2) #expect(fullRecord["feedUrl"] as? String == "https://x.com/rss") #expect(fullRecord["podcastGuid"] as? String == "guid") } } @Suite("EpisodeRef") struct EpisodeRefTests { @Test func decodesFromJSON() throws { let json = """ {"feedId": 75075, "episodeId": 1721351091, "episodeGuid": "ac34129c", "podcastGuid": "917393e3"} """.data(using: .utf8)! let ref = try JSONDecoder().decode(EpisodeRef.self, from: json) #expect(ref.feedId == 75075) #expect(ref.episodeId == 1721351091) #expect(ref.episodeGuid == "ac34129c") #expect(ref.podcastGuid == "917393e3") } @Test func decodesWithMinimalFields() throws { let json = """ {"feedId": 10, "episodeId": 20} """.data(using: .utf8)! let ref = try JSONDecoder().decode(EpisodeRef.self, from: json) #expect(ref.feedId == 10) #expect(ref.episodeId == 20) #expect(ref.episodeGuid == nil) } @Test func roundTrips() throws { let ref = EpisodeRef(feedId: 5, episodeId: 10, episodeGuid: "ep-guid") let data = try JSONEncoder().encode(ref) let decoded = try JSONDecoder().decode(EpisodeRef.self, from: data) #expect(ref == decoded) } @Test func toRecordIncludesOnlyNonNilFields() { let minimal = EpisodeRef(feedId: 1, episodeId: 2) let record = minimal.toRecord() #expect(record["feedId"] as? Int == 1) #expect(record["episodeId"] as? Int == 2) #expect(record["episodeGuid"] == nil) } } @Suite("Comment") struct CommentTests { @Test func decodesFromJSON() throws { let json = """ { "did": "did:plc:abc123", "rkey": "3jm2szx5c47mo", "episode": {"feedId": 75075, "episodeId": 100}, "text": "Great episode!", "timestamp": 120, "createdAt": "2026-01-15T10:30:00Z", "author": { "did": "did:plc:abc123", "handle": "alice.bsky.social", "displayName": "Alice" } } """.data(using: .utf8)! let comment = try JSONDecoder().decode(EffemKit.Comment.self, from: json) #expect(comment.did == "did:plc:abc123") #expect(comment.text == "Great episode!") #expect(comment.timestamp == 120) #expect(comment.episode.feedId == 75075) #expect(comment.author?.displayName == "Alice") #expect(comment.uri == "at://did:plc:abc123/xyz.effem.feed.comment/3jm2szx5c47mo") } } @Suite("Subscription") struct SubscriptionTests { @Test func uriIsCorrect() throws { let json = """ { "did": "did:plc:xyz", "rkey": "abc123", "podcast": {"feedId": 42}, "createdAt": "2026-01-01T00:00:00Z" } """.data(using: .utf8)! let sub = try JSONDecoder().decode(Subscription.self, from: json) #expect(sub.uri == "at://did:plc:xyz/xyz.effem.feed.subscription/abc123") #expect(sub.id == "did:plc:xyz/abc123") } } @Suite("SocialOverlay") struct SocialOverlayTests { @Test func decodesPartialFields() throws { let json = """ {"subscriberCount": 42, "commentCount": 10} """.data(using: .utf8)! let overlay = try JSONDecoder().decode(SocialOverlay.self, from: json) #expect(overlay.subscriberCount == 42) #expect(overlay.commentCount == 10) #expect(overlay.recommendationCount == nil) #expect(overlay.bookmarkCount == nil) #expect(overlay.subscribedByFollowing == nil) } @Test func decodesFullFields() throws { let json = """ { "subscriberCount": 100, "commentCount": 50, "recommendationCount": 25, "bookmarkCount": 10, "subscribedByFollowing": ["did:plc:a", "did:plc:b"] } """.data(using: .utf8)! let overlay = try JSONDecoder().decode(SocialOverlay.self, from: json) #expect(overlay.subscriberCount == 100) #expect(overlay.subscribedByFollowing?.count == 2) } } @Suite("Bookmark") struct BookmarkTests { @Test func decodesFromJSON() throws { let json = """ { "did": "did:plc:xyz", "rkey": "tid123", "episode": {"feedId": 1, "episodeId": 2}, "timestamp": 300, "createdAt": "2026-02-01T00:00:00Z" } """.data(using: .utf8)! let bookmark = try JSONDecoder().decode(Bookmark.self, from: json) #expect(bookmark.timestamp == 300) #expect(bookmark.uri == "at://did:plc:xyz/xyz.effem.feed.bookmark/tid123") } } @Suite("PodcastList") struct PodcastListTests { @Test func decodesFromJSON() throws { let json = """ { "did": "did:plc:xyz", "rkey": "list1", "name": "My Favorite Pods", "description": "The best ones", "podcasts": [{"feedId": 1}, {"feedId": 2, "feedUrl": "https://example.com/rss"}], "createdAt": "2026-01-01T00:00:00Z" } """.data(using: .utf8)! let list = try JSONDecoder().decode(PodcastList.self, from: json) #expect(list.name == "My Favorite Pods") #expect(list.podcasts.count == 2) #expect(list.podcasts[1].feedUrl == "https://example.com/rss") } } @Suite("CommentReplyRef") struct CommentReplyRefTests { @Test func initializesCorrectly() { let ref = CommentReplyRef( rootURI: "at://did:plc:a/xyz.effem.feed.comment/root", rootCID: "bafyreia", parentURI: "at://did:plc:b/xyz.effem.feed.comment/parent", parentCID: "bafyreib" ) #expect(ref.rootURI.contains("root")) #expect(ref.parentCID == "bafyreib") } }