Files
andros 8aa24f069d Shorten default CLIENT tag from org-social-ios to iOS
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.
2026-04-21 10:27:24 +02:00

99 lines
3.9 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: >))
}
}