8aa24f069d
Reads more naturally on timelines as "via iOS". Fixture-based tests that pin the prior value stay as-is to verify the parser preserves whatever CLIENT string is already in the feed.
99 lines
3.9 KiB
Swift
99 lines
3.9 KiB
Swift
import Foundation
|
||
import Testing
|
||
@testable import OrgSocialKit
|
||
|
||
// Known post with a live reaction on the relay (shom Sept 2025 post, 1 reaction: 🫠)
|
||
private let postWithReaction = "https://shom.dev/social.org#2025-09-06T22:36:20-0500"
|
||
private let relay = URL(string: "https://relay.org-social.org")!
|
||
|
||
// MARK: - Unit tests (no network)
|
||
|
||
@Suite("NewPostOptions – reaction factory")
|
||
struct ReactionOptionsTests {
|
||
|
||
@Test("reaction factory sets replyTo and mood, empty text")
|
||
func reactionFactory() {
|
||
let opts = NewPostOptions.reaction(to: "https://example.com/social.org#2025-01-01T00:00:00Z", mood: "❤️")
|
||
#expect(opts.replyTo == "https://example.com/social.org#2025-01-01T00:00:00Z")
|
||
#expect(opts.mood == "❤️")
|
||
#expect(opts.text.isEmpty)
|
||
}
|
||
}
|
||
|
||
@Suite("PostWriter – reaction block")
|
||
struct ReactionBlockTests {
|
||
|
||
let writer = PostWriter()
|
||
|
||
@Test("reaction block has REPLY_TO and MOOD, no body text")
|
||
func reactionBlockStructure() {
|
||
let opts = NewPostOptions.reaction(to: "https://shom.dev/social.org#2025-09-06T22:36:20-0500", mood: "🫠")
|
||
let block = writer.buildPostBlock(timestamp: "2025-01-01T12:00:00+01:00", options: opts)
|
||
|
||
#expect(block.contains(":REPLY_TO: https://shom.dev/social.org#2025-09-06T22:36:20-0500"))
|
||
#expect(block.contains(":MOOD: 🫠"))
|
||
// Body text must be absent
|
||
let lines = block.components(separatedBy: "\n")
|
||
let bodyLines = lines.drop(while: { !$0.hasPrefix(":END:") }).dropFirst()
|
||
let nonEmpty = bodyLines.filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty }
|
||
#expect(nonEmpty.isEmpty, "Reaction block must have no body text, found: \(nonEmpty)")
|
||
}
|
||
|
||
@Test("reaction block has CLIENT property")
|
||
func reactionBlockHasClient() {
|
||
let opts = NewPostOptions.reaction(to: "https://example.com/social.org#2025-01-01T00:00:00Z", mood: "👍")
|
||
let block = writer.buildPostBlock(timestamp: "2025-01-01T12:00:00+01:00", options: opts)
|
||
#expect(block.contains(":CLIENT: iOS"))
|
||
}
|
||
|
||
@Test("appendPost with reaction options produces valid org content")
|
||
func appendReactionPost() throws {
|
||
let feed = """
|
||
#+TITLE: Test
|
||
#+NICK: test
|
||
|
||
* Posts
|
||
"""
|
||
let opts = NewPostOptions.reaction(to: "https://shom.dev/social.org#2025-09-06T22:36:20-0500", mood: "❤️")
|
||
let (updated, postURL) = try writer.appendPost(
|
||
to: feed,
|
||
feedURL: URL(string: "https://example.com/social.org")!,
|
||
options: opts
|
||
)
|
||
#expect(updated.contains(":MOOD: ❤️"))
|
||
#expect(updated.contains(":REPLY_TO: https://shom.dev/social.org#2025-09-06T22:36:20-0500"))
|
||
#expect(postURL.hasPrefix("https://example.com/social.org#"))
|
||
}
|
||
}
|
||
|
||
// MARK: - Integration tests (live relay)
|
||
|
||
@Suite("ThreadClient – reactions", .serialized)
|
||
struct ReactionIntegrationTests {
|
||
|
||
let client = ThreadClient()
|
||
|
||
@Test("fetchInteractions returns at least one reaction for known post")
|
||
func reactionsNonEmpty() async throws {
|
||
let interactions = try await client.fetchInteractions(for: postWithReaction, from: relay)
|
||
#expect(interactions.reactionCount >= 1)
|
||
#expect(!interactions.reactions.isEmpty)
|
||
}
|
||
|
||
@Test("each reaction mood has a non-empty emoji")
|
||
func reactionEmojisValid() async throws {
|
||
let interactions = try await client.fetchInteractions(for: postWithReaction, from: relay)
|
||
for mood in interactions.reactions {
|
||
#expect(!mood.emoji.isEmpty)
|
||
#expect(!mood.posts.isEmpty)
|
||
}
|
||
}
|
||
|
||
@Test("reactions are sorted by count descending")
|
||
func reactionsSortedByCount() async throws {
|
||
let interactions = try await client.fetchInteractions(for: postWithReaction, from: relay)
|
||
let counts = interactions.reactions.map(\.posts.count)
|
||
#expect(counts == counts.sorted(by: >))
|
||
}
|
||
}
|