summaryrefslogtreecommitdiff
path: root/SoraTests
diff options
context:
space:
mode:
Diffstat (limited to 'SoraTests')
-rw-r--r--SoraTests/ViewDerivedDataTests.swift232
1 files changed, 230 insertions, 2 deletions
diff --git a/SoraTests/ViewDerivedDataTests.swift b/SoraTests/ViewDerivedDataTests.swift
index 6572b70..9152591 100644
--- a/SoraTests/ViewDerivedDataTests.swift
+++ b/SoraTests/ViewDerivedDataTests.swift
@@ -323,6 +323,143 @@ final class ViewDerivedDataTests: XCTestCase { // swiftlint:disable:this type_b
)
}
+ func testBooruManagerMoebooruPostsDefaultToHoldsFalseFilter() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let urlBuilderSection = try extractFunction(
+ named: "func url(forPosts page: Int, limit: Int, tags: [String]) -> URL?",
+ from: source
+ )
+ let moebooruCaseStart = try XCTUnwrap(urlBuilderSection.range(of: "case .moebooru:")?.lowerBound)
+ let moebooruCaseEnd = try XCTUnwrap(
+ urlBuilderSection.range(of: "case .gelbooru:", range: moebooruCaseStart..<urlBuilderSection.endIndex)?
+ .lowerBound
+ )
+ let moebooruSection = String(urlBuilderSection[moebooruCaseStart..<moebooruCaseEnd])
+ let moebooruTagHelperSection = try extractFunction(
+ named: "private func moebooruTagString(for tags: [String]) -> String",
+ from: source
+ )
+ let moebooruTagHelperUsageCount = tokenCount(
+ matching: #"let\s+moebooruTags\s*=\s*moebooruTagString\(for:\s*tags\)"#,
+ in: moebooruSection
+ )
+ let holdsFalseDefaultCount = tokenCount(
+ matching: #"\(tags\s*\+\s*\["holds:false"\]\)\.joined\(separator:\s*"\+"\)"#,
+ in: moebooruTagHelperSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ moebooruTagHelperUsageCount,
+ 0,
+ "Moebooru requests should derive tags through a helper that can enforce feed-visibility filters."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ holdsFalseDefaultCount,
+ 0,
+ "Moebooru requests should default to `holds:false` to match website feed visibility."
+ )
+ }
+
+ func testBooruManagerMoebooruRespectsExplicitHoldsTag() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let moebooruTagHelperSection = try extractFunction(
+ named: "private func moebooruTagString(for tags: [String]) -> String",
+ from: source
+ )
+ let explicitHoldsDetectionCount = tokenCount(
+ matching: #"hasPrefix\(\"holds:\"\)"#,
+ in: moebooruTagHelperSection
+ )
+ let explicitHoldsBypassCount = tokenCount(
+ matching: #"if\s+hasExplicitHoldsFilter\s*\{\s*return\s+tags\.joined\(separator:\s*"\+"\)\s*\}"#,
+ in: moebooruTagHelperSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ explicitHoldsDetectionCount,
+ 0,
+ "Moebooru tag helper should detect explicit holds filters in search tags."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ explicitHoldsBypassCount,
+ 0,
+ "Moebooru tag helper should not override explicit user-supplied `holds:*` filters."
+ )
+ }
+
+ func testSettingsManagerPersistsShowHeldMoebooruPostsFlag() throws {
+ let source = try loadSource(at: "Sora/Data/Settings/SettingsManager.swift")
+ let showHeldSettingCount = tokenCount(
+ matching: #"\@AppStorage\("showHeldMoebooruPosts"\)\s*var\s+showHeldMoebooruPosts\s*=\s*false"#,
+ in: source
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ showHeldSettingCount,
+ 0,
+ "SettingsManager should persist a `showHeldMoebooruPosts` toggle with a default of false."
+ )
+ }
+
+ func testMainViewPassesShowHeldMoebooruPostsToBooruManager() throws {
+ let source = try loadSource(at: "Sora/Views/MainView.swift")
+ let managerShowHeldWiringCount = tokenCount(
+ matching: #"showHeldMoebooruPosts:\s*settings\.showHeldMoebooruPosts"#,
+ in: source
+ )
+ let showHeldChangeObserverCount = tokenCount(
+ matching: #"\.onChange\s*\(\s*of:\s*settings\.showHeldMoebooruPosts\s*\)"#,
+ in: source
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ managerShowHeldWiringCount,
+ 1,
+ "MainView should wire held-post toggle into all BooruManager reconstructions."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ showHeldChangeObserverCount,
+ 0,
+ "MainView should refresh BooruManager when held-post visibility setting changes."
+ )
+ }
+
+ func testBooruManagerSupportsShowHeldMoebooruPostsMode() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let initSignatureCount = tokenCount(
+ matching: #"showHeldMoebooruPosts:\s*Bool\s*=\s*false"#,
+ in: source
+ )
+ let moebooruTagHelperSection = try extractFunction(
+ named: "private func moebooruTagString(for tags: [String]) -> String",
+ from: source
+ )
+ let showHeldBypassCount = tokenCount(
+ matching: #"guard\s*!\s*showHeldMoebooruPosts\s*else\s*\{\s*return\s+tags\.joined\(separator:\s*"\+"\)\s*\}"#,
+ in: moebooruTagHelperSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ initSignatureCount,
+ 0,
+ "BooruManager should expose a `showHeldMoebooruPosts` initialization option."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ showHeldBypassCount,
+ 0,
+ "Moebooru tag helper should bypass forced `holds:false` when showing held posts is enabled."
+ )
+ }
+
func testBooruManagerDanbooruPostsUseBeforeCursorPagination() throws {
let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
let urlBuilderSection = try extractFunction(
@@ -336,11 +473,11 @@ final class ViewDerivedDataTests: XCTestCase { // swiftlint:disable:this type_b
)
let danbooruSection = String(urlBuilderSection[danbooruCaseStart..<danbooruCaseEnd])
let pageTokenFunctionSection = try extractFunction(
- named: "private func danbooruPageToken(for page: Int) -> String",
+ named: "private func danbooruPageToken(for page: Int, tags: [String]) -> String",
from: source
)
let danbooruCursorPageQueryCount = tokenCount(
- matching: #"URLQueryItem\(name:\s*"page",\s*value:\s*danbooruPageToken\(for:\s*page\)\)"#,
+ matching: #"URLQueryItem\(name:\s*"page",\s*value:\s*danbooruPageToken\(for:\s*page,\s*tags:\s*tags\)\)"#,
in: danbooruSection
)
let beforeCursorCount = tokenCount(
@@ -362,6 +499,97 @@ final class ViewDerivedDataTests: XCTestCase { // swiftlint:disable:this type_b
)
}
+ func testBooruManagerDanbooruPaginationFallsBackToNumericPageForExplicitSort() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let pageTokenFunctionSection = try extractFunction(
+ named: "private func danbooruPageToken(for page: Int, tags: [String]) -> String",
+ from: source
+ )
+ let explicitSortGuardCount = tokenCount(
+ matching: #"guard\s*!\s*hasExplicitSortTag\(in:\s*tags\)\s*else\s*\{\s*return\s*String\(page\)\s*\}"#,
+ in: pageTokenFunctionSection
+ )
+ let sortTagHelperSection = try extractFunction(
+ named: "private func hasExplicitSortTag(in tags: [String]) -> Bool",
+ from: source
+ )
+ let sortTagDetectionCount = tokenCount(
+ matching: #"hasPrefix\("order:"\)"#,
+ in: sortTagHelperSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ explicitSortGuardCount,
+ 0,
+ "Danbooru pagination should fallback to numeric pages for explicit `order:*` searches."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ sortTagDetectionCount,
+ 0,
+ "Danbooru pagination should detect explicit sort tags from search input."
+ )
+ }
+
+ func testBooruManagerDoesNotForceClientSidePostResort() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let retrySection = try extractFunction(
+ named: "private func fetchPostsWithRetry(url: URL) async -> [BooruPost]",
+ from: source
+ )
+ let forcedResortCount = tokenCount(
+ matching: #"\.sorted\s*\{\s*\$0\.createdAt\s*>\s*\$1\.createdAt\s*\}"#,
+ in: retrySection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertEqual(
+ forcedResortCount,
+ 0,
+ "Fetched posts should preserve provider API ordering and not be force-sorted by created_at."
+ )
+ }
+
+ func testBooruManagerParsePostsPreservesAPIOrderWhenDeduplicating() throws {
+ let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
+ let parsePostsSection = try extractFunction(
+ named: "nonisolated static func parsePosts(",
+ from: source
+ )
+ let stableDedupeInsertCount = tokenCount(
+ matching: #"seenPostIDs\.insert\(post\.id\)\.inserted"#,
+ in: parsePostsSection
+ )
+ let orderedAppendCount = tokenCount(
+ matching: #"orderedUniquePosts\.append\(post\)"#,
+ in: parsePostsSection
+ )
+ let orderedReturnCount = tokenCount(
+ matching: #"return\s+orderedUniquePosts"#,
+ in: parsePostsSection
+ )
+
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ stableDedupeInsertCount,
+ 0,
+ "Post dedup should track seen IDs without scrambling API order."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ orderedAppendCount,
+ 0,
+ "Post dedup should append in parser order to preserve provider ranking."
+ )
+ // swiftlint:disable:next prefer_nimble
+ XCTAssertGreaterThan(
+ orderedReturnCount,
+ 0,
+ "Post parser output should return the stable ordered array after deduplication."
+ )
+ }
+
func testBooruManagerValidatesHTTPStatusCodesForRequests() throws {
let source = try loadSource(at: "Sora/Data/Booru/BooruManager.swift")
let requestURLSection = try extractFunction(