mirror of
https://github.com/Aminark/legado-ios.git
synced 2026-10-20 19:21:22 +08:00
138 lines
6.2 KiB
Swift
138 lines
6.2 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import LegadoCore
|
|
|
|
struct ReadingActivityRepositoryTests {
|
|
@Test func decodesAndEncodesBookmarkPosition() throws {
|
|
let data = Data(#"{"time":7,"bookName":"书","bookAuthor":"作者","chapterIndex":2,"chapterPos":88,"chapterName":"第二章","bookText":"片段","content":"备注"}"#.utf8)
|
|
let bookmark = try JSONDecoder().decode(Bookmark.self, from: data)
|
|
let encoded = try JSONEncoder().encode(bookmark)
|
|
let object = try #require(JSONSerialization.jsonObject(with: encoded) as? [String: Any])
|
|
|
|
#expect(bookmark.chapterPosition == 88)
|
|
#expect(object["chapterPos"] as? Int == 88)
|
|
#expect(object["chapterPosition"] == nil)
|
|
}
|
|
|
|
@Test func accumulatesPerDeviceAndSummarizesAcrossDevices() {
|
|
let service = ReadingActivityService()
|
|
let now: Int64 = 2_000
|
|
var records = [ReadRecord(deviceId: "a", bookName: "书", readTime: 1_000, lastRead: 1_000)]
|
|
records = service.addingReadTime(500, deviceID: "a", bookName: "书", now: now, to: records)
|
|
records = service.addingReadTime(700, deviceID: "b", bookName: "书", now: now + 1, to: records)
|
|
|
|
let summary = service.summaries(records)
|
|
#expect(records.count == 2)
|
|
#expect(summary == [.init(bookName: "书", readTime: 2_200, lastRead: 2_001)])
|
|
}
|
|
|
|
@Test func decodesLegacyReadRecordWithoutDailyStatistics() throws {
|
|
let data = Data(#"{"deviceId":"ios","bookName":"旧书","readTime":120000,"lastRead":7}"#.utf8)
|
|
let record = try JSONDecoder().decode(ReadRecord.self, from: data)
|
|
|
|
#expect(record.dailyReadTimes.isEmpty)
|
|
#expect(record.readTime == 120_000)
|
|
}
|
|
|
|
@Test func calculatesTodayStreakTotalsAndUniqueBooks() throws {
|
|
var calendar = Calendar(identifier: .gregorian)
|
|
calendar.timeZone = try #require(TimeZone(identifier: "Asia/Shanghai"))
|
|
let now = try #require(calendar.date(from: DateComponents(year: 2026, month: 8, day: 20, hour: 12)))
|
|
let service = ReadingActivityService()
|
|
let records = [
|
|
ReadRecord(
|
|
deviceId: "a",
|
|
bookName: "第一本",
|
|
readTime: 9_000_000,
|
|
lastRead: Int64(now.timeIntervalSince1970 * 1_000),
|
|
dailyReadTimes: ["2026-08-18": 600_000, "2026-08-19": 1_200_000, "2026-08-20": 1_800_000]
|
|
),
|
|
ReadRecord(
|
|
deviceId: "b",
|
|
bookName: "第一本",
|
|
readTime: 3_000_000,
|
|
dailyReadTimes: ["2026-08-20": 300_000]
|
|
),
|
|
ReadRecord(
|
|
deviceId: "a",
|
|
bookName: "第二本",
|
|
readTime: 4_000_000,
|
|
dailyReadTimes: ["2026-08-17": 60_000]
|
|
)
|
|
]
|
|
|
|
let statistics = service.statistics(records, now: now, calendar: calendar)
|
|
|
|
#expect(statistics.todayReadTime == 2_100_000)
|
|
#expect(statistics.currentStreak == 4)
|
|
#expect(statistics.totalReadTime == 16_000_000)
|
|
#expect(statistics.bookCount == 2)
|
|
#expect(statistics.recentDays.count == 7)
|
|
}
|
|
|
|
@Test func keepsCurrentStreakUntilTheDayAfterLastReading() throws {
|
|
var calendar = Calendar(identifier: .gregorian)
|
|
calendar.timeZone = try #require(TimeZone(identifier: "Asia/Shanghai"))
|
|
let now = try #require(calendar.date(from: DateComponents(year: 2026, month: 8, day: 20, hour: 8)))
|
|
let records = [ReadRecord(
|
|
deviceId: "a",
|
|
bookName: "书",
|
|
readTime: 120_000,
|
|
dailyReadTimes: ["2026-08-18": 60_000, "2026-08-19": 60_000]
|
|
)]
|
|
|
|
let statistics = ReadingActivityService().statistics(records, now: now, calendar: calendar)
|
|
|
|
#expect(statistics.todayReadTime == 0)
|
|
#expect(statistics.currentStreak == 2)
|
|
}
|
|
|
|
@Test func filtersAndSortsReadRecords() {
|
|
let records = [
|
|
ReadRecord(deviceId: "a", bookName: "Beta", readTime: 5_000, lastRead: 20),
|
|
ReadRecord(deviceId: "a", bookName: "Alpha", readTime: 1_000, lastRead: 30),
|
|
ReadRecord(deviceId: "b", bookName: "Beta", readTime: 2_000, lastRead: 40)
|
|
]
|
|
let service = ReadingActivityService()
|
|
|
|
#expect(service.displayedSummaries(records, searchKey: "", sortMode: 0).map(\.bookName) == ["Alpha", "Beta"])
|
|
#expect(service.displayedSummaries(records, searchKey: "", sortMode: 1).map(\.bookName) == ["Beta", "Alpha"])
|
|
#expect(service.displayedSummaries(records, searchKey: "", sortMode: 2).map(\.bookName) == ["Beta", "Alpha"])
|
|
#expect(service.displayedSummaries(records, searchKey: "alp", sortMode: 0).map(\.bookName) == ["Alpha"])
|
|
}
|
|
|
|
@Test func sortsBookmarksByBookAndPosition() {
|
|
var later = Bookmark(time: 1, bookName: "B", bookAuthor: "作者")
|
|
later.chapterIndex = 1
|
|
later.chapterPosition = 20
|
|
var earlierPosition = Bookmark(time: 2, bookName: "A", bookAuthor: "作者")
|
|
earlierPosition.chapterIndex = 2
|
|
earlierPosition.chapterPosition = 30
|
|
var first = Bookmark(time: 3, bookName: "A", bookAuthor: "作者")
|
|
first.chapterIndex = 1
|
|
first.chapterPosition = 40
|
|
|
|
let sorted = ReadingActivityService().sortedBookmarks([later, earlierPosition, first])
|
|
|
|
#expect(sorted.map(\.time) == [first.time, earlierPosition.time, later.time])
|
|
}
|
|
|
|
@Test func persistsBookmarksAndRecordsAtomically() async throws {
|
|
let directory = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString, directoryHint: .isDirectory)
|
|
defer { try? FileManager.default.removeItem(at: directory) }
|
|
let repository = FileReadingActivityRepository(
|
|
bookmarksURL: directory.appending(path: "bookmarks.json"),
|
|
recordsURL: directory.appending(path: "records.json")
|
|
)
|
|
var bookmark = Bookmark(time: 4, bookName: "书")
|
|
bookmark.chapterPosition = 19
|
|
let record = ReadRecord(deviceId: "ios", bookName: "书", readTime: 3_000, lastRead: 5)
|
|
|
|
try await repository.saveBookmarks([bookmark])
|
|
try await repository.saveReadRecords([record])
|
|
|
|
#expect(try await repository.loadBookmarks() == [bookmark])
|
|
#expect(try await repository.loadReadRecords() == [record])
|
|
}
|
|
}
|