Files
andros 2bd6c18f99 Add ProfileFetcher to lib, Profile tab, and tappable authors and mentions
- ProfileFetcher: fetch + parse social.org → OrgSocialProfile with feedURL set
- 9 live-relay integration tests (ross baker, shom, sacha chua) all passing
- ProfileView: avatar, nick, title, bio, info, follows, posts; navigationDestination chains for nested profiles
- OwnProfileView: Profile tab showing own feed from Settings vfile token URL
- PostRowView: author header navigates to profile; mentions extracted with feedURL and shown as tappable chips
- NotificationsView: tapping a notification navigates to the author's profile
2026-04-19 08:37:50 +02:00

86 lines
2.8 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
// Real relay feeds used across tests
private let rossURL = URL(string: "https://rossabaker.com/social.org")!
private let shomURL = URL(string: "https://shom.dev/social.org")!
private let sachaURL = URL(string: "https://sachachua.com/social.org")!
@Suite("ProfileFetcher live relay feeds", .serialized)
struct ProfileFetcherTests {
let fetcher = ProfileFetcher()
@Test("fetches ross baker profile nick and posts present")
func fetchRoss() async throws {
let profile = try await fetcher.fetch(from: rossURL)
#expect(profile.feedURL == rossURL)
#expect(profile.nick != nil)
#expect(!profile.posts.isEmpty)
}
@Test("fetches shom profile has description or title")
func fetchShom() async throws {
let profile = try await fetcher.fetch(from: shomURL)
#expect(profile.feedURL == shomURL)
let hasIdentity = profile.nick != nil || profile.title != nil
#expect(hasIdentity)
}
@Test("fetches sacha chua profile feedURL set correctly")
func fetchSacha() async throws {
let profile = try await fetcher.fetch(from: sachaURL)
#expect(profile.feedURL == sachaURL)
}
@Test("all posts have non-empty timestamps")
func postsHaveTimestamps() async throws {
let profile = try await fetcher.fetch(from: rossURL)
for post in profile.posts {
#expect(!post.timestamp.isEmpty)
}
}
@Test("posts are not in the future (scheduled posts filtered)")
func noFuturePosts() async throws {
let profile = try await fetcher.fetch(from: shomURL)
let now = Date()
for post in profile.posts {
#expect(post.date <= now)
}
}
@Test("follows are valid URLs")
func followsAreURLs() async throws {
let profile = try await fetcher.fetch(from: rossURL)
for follow in profile.follows {
#expect(follow.url.scheme == "https" || follow.url.scheme == "http")
}
}
@Test("avatar is a valid URL when present")
func avatarIsURL() async throws {
let profile = try await fetcher.fetch(from: rossURL)
if let avatar = profile.avatar {
#expect(avatar.scheme == "https" || avatar.scheme == "http")
}
}
@Test("throws on invalid URL scheme")
func throwsOnInvalidScheme() async throws {
let bad = URL(string: "ftp://bad.example.com/social.org")!
await #expect(throws: FeedFetcherError.invalidURL) {
try await fetcher.fetch(from: bad)
}
}
@Test("throws on non-existent feed")
func throwsOn404() async throws {
let missing = URL(string: "https://rossabaker.com/does-not-exist.org")!
await #expect(throws: (any Error).self) {
try await fetcher.fetch(from: missing)
}
}
}