2bd6c18f99
- 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
86 lines
2.8 KiB
Swift
86 lines
2.8 KiB
Swift
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)
|
||
}
|
||
}
|
||
}
|