mirror of
https://github.com/yattee/yattee.git
synced 2026-02-20 09:49:46 +00:00
Yattee v2 rewrite
This commit is contained in:
823
YatteeTests/DataTests.swift
Normal file
823
YatteeTests/DataTests.swift
Normal file
@@ -0,0 +1,823 @@
|
||||
//
|
||||
// DataTests.swift
|
||||
// YatteeTests
|
||||
//
|
||||
// Tests for the local data persistence layer.
|
||||
//
|
||||
|
||||
import Testing
|
||||
import Foundation
|
||||
@testable import Yattee
|
||||
|
||||
@MainActor
|
||||
@Suite("Data Layer Tests")
|
||||
struct DataTests {
|
||||
|
||||
// MARK: - Watch Entry Tests
|
||||
|
||||
@Suite("Watch Entry")
|
||||
struct WatchEntryTests {
|
||||
@Test("Progress calculation")
|
||||
@MainActor
|
||||
func progressCalculation() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test123",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test Video",
|
||||
authorName: "Test Channel",
|
||||
authorID: "channel123",
|
||||
duration: 600, // 10 minutes
|
||||
watchedSeconds: 300 // 5 minutes
|
||||
)
|
||||
|
||||
#expect(entry.progress == 0.5)
|
||||
}
|
||||
|
||||
@Test("Auto-finish at 90%")
|
||||
@MainActor
|
||||
func autoFinishAt90Percent() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test123",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test Video",
|
||||
authorName: "Test Channel",
|
||||
authorID: "channel123",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
#expect(!entry.isFinished)
|
||||
|
||||
entry.updateProgress(seconds: 90)
|
||||
|
||||
#expect(entry.isFinished)
|
||||
#expect(entry.progress >= 0.9)
|
||||
}
|
||||
|
||||
@Test("Reset progress")
|
||||
@MainActor
|
||||
func resetProgress() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test123",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test Video",
|
||||
authorName: "Test Channel",
|
||||
authorID: "channel123",
|
||||
duration: 100,
|
||||
watchedSeconds: 90,
|
||||
isFinished: true
|
||||
)
|
||||
|
||||
entry.resetProgress()
|
||||
|
||||
#expect(entry.watchedSeconds == 0)
|
||||
#expect(!entry.isFinished)
|
||||
}
|
||||
|
||||
@Test("Content source YouTube")
|
||||
@MainActor
|
||||
func contentSourceYouTube() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "abc123",
|
||||
sourceRawValue: "global",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .global = entry.contentSource {
|
||||
// Expected
|
||||
} else {
|
||||
Issue.record("Expected global source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Content source Federated")
|
||||
@MainActor
|
||||
func contentSourceFederated() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "uuid123",
|
||||
sourceRawValue: "federated",
|
||||
instanceURLString: "https://peertube.example.com",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .federated(_, let instance) = entry.contentSource {
|
||||
#expect(instance.host == "peertube.example.com")
|
||||
} else {
|
||||
Issue.record("Expected federated source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Remaining time formatting")
|
||||
@MainActor
|
||||
func remainingTimeFormatting() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 600, // 10 minutes
|
||||
watchedSeconds: 300 // 5 minutes watched
|
||||
)
|
||||
|
||||
// 5 minutes remaining = "5:00"
|
||||
#expect(entry.remainingTime == "5:00")
|
||||
}
|
||||
|
||||
@Test("Remaining time with seconds")
|
||||
@MainActor
|
||||
func remainingTimeWithSeconds() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 125, // 2:05
|
||||
watchedSeconds: 60 // 1 minute watched
|
||||
)
|
||||
|
||||
// 65 seconds remaining = "1:05"
|
||||
#expect(entry.remainingTime == "1:05")
|
||||
}
|
||||
|
||||
@Test("Thumbnail URL conversion")
|
||||
@MainActor
|
||||
func thumbnailURL() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: "https://example.com/thumb.jpg"
|
||||
)
|
||||
|
||||
#expect(entry.thumbnailURL?.absoluteString == "https://example.com/thumb.jpg")
|
||||
}
|
||||
|
||||
@Test("Thumbnail URL nil for invalid string")
|
||||
@MainActor
|
||||
func thumbnailURLNil() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: nil
|
||||
)
|
||||
|
||||
#expect(entry.thumbnailURL == nil)
|
||||
}
|
||||
|
||||
@Test("Mark as finished")
|
||||
@MainActor
|
||||
func markAsFinished() {
|
||||
let entry = WatchEntry(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
#expect(!entry.isFinished)
|
||||
entry.markAsFinished()
|
||||
#expect(entry.isFinished)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Bookmark Tests
|
||||
|
||||
@Suite("Bookmark")
|
||||
struct BookmarkTests {
|
||||
@Test("Formatted duration hours")
|
||||
@MainActor
|
||||
func formattedDurationHours() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Long Video",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 3661 // 1:01:01
|
||||
)
|
||||
|
||||
#expect(bookmark.formattedDuration == "1:01:01")
|
||||
}
|
||||
|
||||
@Test("Formatted duration minutes")
|
||||
@MainActor
|
||||
func formattedDurationMinutes() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Short Video",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 125 // 2:05
|
||||
)
|
||||
|
||||
#expect(bookmark.formattedDuration == "2:05")
|
||||
}
|
||||
|
||||
@Test("Live shows LIVE")
|
||||
@MainActor
|
||||
func liveShowsLive() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Live Stream",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 0,
|
||||
isLive: true
|
||||
)
|
||||
|
||||
#expect(bookmark.formattedDuration == "LIVE")
|
||||
}
|
||||
|
||||
@Test("Content source Global")
|
||||
@MainActor
|
||||
func contentSourceGlobal() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "abc123",
|
||||
sourceRawValue: "global",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .global = bookmark.contentSource {
|
||||
// Expected
|
||||
} else {
|
||||
Issue.record("Expected global source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Content source Federated")
|
||||
@MainActor
|
||||
func contentSourceFederated() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "uuid123",
|
||||
sourceRawValue: "federated",
|
||||
instanceURLString: "https://peertube.example.com",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .federated(_, let instance) = bookmark.contentSource {
|
||||
#expect(instance.host == "peertube.example.com")
|
||||
} else {
|
||||
Issue.record("Expected federated source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Thumbnail URL conversion")
|
||||
@MainActor
|
||||
func thumbnailURL() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: "https://example.com/thumb.jpg"
|
||||
)
|
||||
|
||||
#expect(bookmark.thumbnailURL?.absoluteString == "https://example.com/thumb.jpg")
|
||||
}
|
||||
|
||||
@Test("Zero duration shows empty string")
|
||||
@MainActor
|
||||
func zeroDuration() {
|
||||
let bookmark = Bookmark(
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 0,
|
||||
isLive: false
|
||||
)
|
||||
|
||||
#expect(bookmark.formattedDuration == "")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Local Playlist Tests
|
||||
|
||||
@Suite("Local Playlist")
|
||||
struct LocalPlaylistTests {
|
||||
@Test("Video count")
|
||||
@MainActor
|
||||
func videoCount() {
|
||||
let playlist = LocalPlaylist(title: "My Playlist")
|
||||
|
||||
#expect(playlist.videoCount == 0)
|
||||
}
|
||||
|
||||
@Test("Total duration formatting")
|
||||
@MainActor
|
||||
func totalDurationFormatting() {
|
||||
let playlist = LocalPlaylist(title: "My Playlist")
|
||||
|
||||
// Empty playlist
|
||||
#expect(playlist.formattedTotalDuration == "0 min")
|
||||
}
|
||||
|
||||
@Test("Total duration with hours")
|
||||
@MainActor
|
||||
func totalDurationWithHours() {
|
||||
let playlist = LocalPlaylist(title: "Long Playlist")
|
||||
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "video1",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Long Video",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 7200 // 2 hours
|
||||
)
|
||||
item.playlist = playlist
|
||||
playlist.items?.append(item)
|
||||
|
||||
#expect(playlist.formattedTotalDuration == "2h 0m")
|
||||
}
|
||||
|
||||
@Test("Contains video check")
|
||||
@MainActor
|
||||
func containsVideoCheck() {
|
||||
let playlist = LocalPlaylist(title: "Test")
|
||||
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "abc123",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Video",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
item.playlist = playlist
|
||||
playlist.items?.append(item)
|
||||
|
||||
#expect(playlist.contains(videoID: "abc123"))
|
||||
#expect(!playlist.contains(videoID: "xyz789"))
|
||||
}
|
||||
|
||||
@Test("Sorted items by order")
|
||||
@MainActor
|
||||
func sortedItems() {
|
||||
let playlist = LocalPlaylist(title: "Test")
|
||||
|
||||
// Add items out of order
|
||||
let item2 = LocalPlaylistItem(
|
||||
sortOrder: 2,
|
||||
videoID: "video2",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Second",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
let item1 = LocalPlaylistItem(
|
||||
sortOrder: 1,
|
||||
videoID: "video1",
|
||||
sourceRawValue: "youtube",
|
||||
title: "First",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
let item3 = LocalPlaylistItem(
|
||||
sortOrder: 3,
|
||||
videoID: "video3",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Third",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
item1.playlist = playlist
|
||||
item2.playlist = playlist
|
||||
item3.playlist = playlist
|
||||
playlist.items?.append(item2)
|
||||
playlist.items?.append(item1)
|
||||
playlist.items?.append(item3)
|
||||
|
||||
let sorted = playlist.sortedItems
|
||||
#expect(sorted.count == 3)
|
||||
#expect(sorted[0].videoID == "video1")
|
||||
#expect(sorted[1].videoID == "video2")
|
||||
#expect(sorted[2].videoID == "video3")
|
||||
}
|
||||
|
||||
@Test("Thumbnail URL from first sorted item")
|
||||
@MainActor
|
||||
func thumbnailURL() {
|
||||
let playlist = LocalPlaylist(title: "Test")
|
||||
|
||||
let item1 = LocalPlaylistItem(
|
||||
sortOrder: 1,
|
||||
videoID: "video1",
|
||||
sourceRawValue: "youtube",
|
||||
title: "First",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: "https://example.com/thumb1.jpg"
|
||||
)
|
||||
let item2 = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "video2",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Actually First",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: "https://example.com/thumb2.jpg"
|
||||
)
|
||||
|
||||
item1.playlist = playlist
|
||||
item2.playlist = playlist
|
||||
playlist.items?.append(item1)
|
||||
playlist.items?.append(item2)
|
||||
|
||||
// Should get thumbnail from item with lowest sortOrder
|
||||
#expect(playlist.thumbnailURL?.absoluteString == "https://example.com/thumb2.jpg")
|
||||
}
|
||||
|
||||
@Test("Items is optional for CloudKit compatibility")
|
||||
@MainActor
|
||||
func itemsOptional() {
|
||||
let playlist = LocalPlaylist(title: "Empty")
|
||||
// items should be initialized as empty array, not nil
|
||||
#expect(playlist.items != nil)
|
||||
#expect(playlist.items?.isEmpty == true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Subscription Tests
|
||||
|
||||
@Suite("Subscription")
|
||||
struct SubscriptionTests {
|
||||
@Test("Formatted subscriber count")
|
||||
@MainActor
|
||||
func formattedSubscriberCount() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "youtube",
|
||||
name: "Popular Channel",
|
||||
subscriberCount: 1_500_000
|
||||
)
|
||||
|
||||
#expect(sub.formattedSubscriberCount == "1.5M")
|
||||
}
|
||||
|
||||
@Test("No subscriber count")
|
||||
@MainActor
|
||||
func noSubscriberCount() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "youtube",
|
||||
name: "New Channel"
|
||||
)
|
||||
|
||||
#expect(sub.formattedSubscriberCount == nil)
|
||||
}
|
||||
|
||||
@Test("Content source Global")
|
||||
@MainActor
|
||||
func contentSourceGlobal() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "global",
|
||||
name: "Channel"
|
||||
)
|
||||
|
||||
if case .global = sub.contentSource {
|
||||
// Expected
|
||||
} else {
|
||||
Issue.record("Expected global source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Content source Federated")
|
||||
@MainActor
|
||||
func contentSourceFederated() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "federated",
|
||||
instanceURLString: "https://peertube.example.com",
|
||||
name: "Channel"
|
||||
)
|
||||
|
||||
if case .federated(_, let instance) = sub.contentSource {
|
||||
#expect(instance.host == "peertube.example.com")
|
||||
} else {
|
||||
Issue.record("Expected federated source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Avatar URL conversion")
|
||||
@MainActor
|
||||
func avatarURL() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "youtube",
|
||||
name: "Channel",
|
||||
avatarURLString: "https://example.com/avatar.jpg"
|
||||
)
|
||||
|
||||
#expect(sub.avatarURL?.absoluteString == "https://example.com/avatar.jpg")
|
||||
}
|
||||
|
||||
@Test("Banner URL conversion")
|
||||
@MainActor
|
||||
func bannerURL() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "youtube",
|
||||
name: "Channel",
|
||||
bannerURLString: "https://example.com/banner.jpg"
|
||||
)
|
||||
|
||||
#expect(sub.bannerURL?.absoluteString == "https://example.com/banner.jpg")
|
||||
}
|
||||
|
||||
@Test("Update from channel")
|
||||
@MainActor
|
||||
func updateFromChannel() {
|
||||
let sub = Subscription(
|
||||
channelID: "ch123",
|
||||
sourceRawValue: "youtube",
|
||||
name: "Old Name",
|
||||
subscriberCount: 1000
|
||||
)
|
||||
|
||||
let channel = Channel(
|
||||
id: .global("ch123"),
|
||||
name: "New Name",
|
||||
description: "Updated description",
|
||||
subscriberCount: 2000,
|
||||
thumbnailURL: URL(string: "https://example.com/new-avatar.jpg"),
|
||||
bannerURL: URL(string: "https://example.com/new-banner.jpg"),
|
||||
isVerified: true
|
||||
)
|
||||
|
||||
sub.update(from: channel)
|
||||
|
||||
#expect(sub.name == "New Name")
|
||||
#expect(sub.channelDescription == "Updated description")
|
||||
#expect(sub.subscriberCount == 2000)
|
||||
#expect(sub.avatarURLString == "https://example.com/new-avatar.jpg")
|
||||
#expect(sub.bannerURLString == "https://example.com/new-banner.jpg")
|
||||
#expect(sub.isVerified == true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - LocalPlaylistItem Tests
|
||||
|
||||
@Suite("LocalPlaylistItem")
|
||||
struct LocalPlaylistItemTests {
|
||||
@Test("Content source Global")
|
||||
@MainActor
|
||||
func contentSourceGlobal() {
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "abc123",
|
||||
sourceRawValue: "global",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .global = item.contentSource {
|
||||
// Expected
|
||||
} else {
|
||||
Issue.record("Expected global source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Content source Federated")
|
||||
@MainActor
|
||||
func contentSourceFederated() {
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "uuid123",
|
||||
sourceRawValue: "federated",
|
||||
instanceURLString: "https://peertube.example.com",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
if case .federated(_, let instance) = item.contentSource {
|
||||
#expect(instance.host == "peertube.example.com")
|
||||
} else {
|
||||
Issue.record("Expected federated source")
|
||||
}
|
||||
}
|
||||
|
||||
@Test("Thumbnail URL conversion")
|
||||
@MainActor
|
||||
func thumbnailURL() {
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "abc123",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100,
|
||||
thumbnailURLString: "https://example.com/thumb.jpg"
|
||||
)
|
||||
|
||||
#expect(item.thumbnailURL?.absoluteString == "https://example.com/thumb.jpg")
|
||||
}
|
||||
|
||||
@Test("Default values for CloudKit compatibility")
|
||||
@MainActor
|
||||
func defaultValues() {
|
||||
let item = LocalPlaylistItem(
|
||||
sortOrder: 0,
|
||||
videoID: "test",
|
||||
sourceRawValue: "youtube",
|
||||
title: "Test",
|
||||
authorName: "Channel",
|
||||
authorID: "ch1",
|
||||
duration: 100
|
||||
)
|
||||
|
||||
// Check default values are set
|
||||
#expect(item.isLive == false)
|
||||
#expect(item.thumbnailURLString == nil)
|
||||
#expect(item.instanceURLString == nil)
|
||||
#expect(item.peertubeUUID == nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DataManager Tests
|
||||
|
||||
@Suite("DataManager")
|
||||
struct DataManagerTests {
|
||||
@Test("Watch progress round trip")
|
||||
@MainActor
|
||||
func watchProgressRoundTrip() async throws {
|
||||
let manager = try DataManager(inMemory: true)
|
||||
|
||||
// Create a test video
|
||||
let video = Video(
|
||||
id: .global("testVideo123"),
|
||||
title: "Test Video",
|
||||
description: nil,
|
||||
author: Author(id: "ch1", name: "Channel"),
|
||||
duration: 600,
|
||||
publishedAt: nil,
|
||||
publishedText: nil,
|
||||
viewCount: nil,
|
||||
likeCount: nil,
|
||||
thumbnails: [],
|
||||
isLive: false,
|
||||
isUpcoming: false,
|
||||
scheduledStartTime: nil
|
||||
)
|
||||
|
||||
// Record progress
|
||||
manager.updateWatchProgress(for: video, seconds: 300)
|
||||
|
||||
// Retrieve progress
|
||||
let progress = manager.watchProgress(for: "testVideo123")
|
||||
|
||||
#expect(progress == 300)
|
||||
}
|
||||
|
||||
@Test("Bookmark toggle")
|
||||
@MainActor
|
||||
func bookmarkToggle() async throws {
|
||||
let manager = try DataManager(inMemory: true)
|
||||
|
||||
let video = Video(
|
||||
id: .global("bookmarkTest"),
|
||||
title: "Bookmark Me",
|
||||
description: nil,
|
||||
author: Author(id: "ch1", name: "Channel"),
|
||||
duration: 300,
|
||||
publishedAt: nil,
|
||||
publishedText: nil,
|
||||
viewCount: nil,
|
||||
likeCount: nil,
|
||||
thumbnails: [],
|
||||
isLive: false,
|
||||
isUpcoming: false,
|
||||
scheduledStartTime: nil
|
||||
)
|
||||
|
||||
#expect(!manager.isBookmarked(videoID: "bookmarkTest"))
|
||||
|
||||
manager.addBookmark(for: video)
|
||||
#expect(manager.isBookmarked(videoID: "bookmarkTest"))
|
||||
|
||||
manager.removeBookmark(for: "bookmarkTest")
|
||||
#expect(!manager.isBookmarked(videoID: "bookmarkTest"))
|
||||
}
|
||||
|
||||
@Test("Create and delete playlist")
|
||||
@MainActor
|
||||
func createAndDeletePlaylist() async throws {
|
||||
let manager = try DataManager(inMemory: true)
|
||||
|
||||
let playlist = manager.createPlaylist(title: "Test Playlist", description: "A test")
|
||||
|
||||
#expect(playlist.title == "Test Playlist")
|
||||
#expect(playlist.playlistDescription == "A test")
|
||||
|
||||
var playlists = manager.playlists()
|
||||
#expect(playlists.count == 1)
|
||||
|
||||
manager.deletePlaylist(playlist)
|
||||
|
||||
playlists = manager.playlists()
|
||||
#expect(playlists.count == 0)
|
||||
}
|
||||
|
||||
@Test("Subscription management")
|
||||
@MainActor
|
||||
func subscriptionManagement() async throws {
|
||||
let manager = try DataManager(inMemory: true)
|
||||
|
||||
let channel = Channel(
|
||||
id: .global("testChannel"),
|
||||
name: "Test Channel",
|
||||
description: "A test channel",
|
||||
subscriberCount: 10000,
|
||||
thumbnailURL: nil
|
||||
)
|
||||
|
||||
#expect(!manager.isSubscribed(to: "testChannel"))
|
||||
|
||||
manager.subscribe(to: channel)
|
||||
#expect(manager.isSubscribed(to: "testChannel"))
|
||||
|
||||
let subs = manager.subscriptions()
|
||||
#expect(subs.count == 1)
|
||||
#expect(subs.first?.name == "Test Channel")
|
||||
|
||||
manager.unsubscribe(from: "testChannel")
|
||||
#expect(!manager.isSubscribed(to: "testChannel"))
|
||||
}
|
||||
|
||||
@Test("Watch history ordering")
|
||||
@MainActor
|
||||
func watchHistoryOrdering() async throws {
|
||||
let manager = try DataManager(inMemory: true)
|
||||
|
||||
// Create multiple videos
|
||||
for i in 1...3 {
|
||||
let video = Video(
|
||||
id: .global("video\(i)"),
|
||||
title: "Video \(i)",
|
||||
description: nil,
|
||||
author: Author(id: "ch1", name: "Channel"),
|
||||
duration: 100,
|
||||
publishedAt: nil,
|
||||
publishedText: nil,
|
||||
viewCount: nil,
|
||||
likeCount: nil,
|
||||
thumbnails: [],
|
||||
isLive: false,
|
||||
isUpcoming: false,
|
||||
scheduledStartTime: nil
|
||||
)
|
||||
manager.updateWatchProgress(for: video, seconds: Double(i * 10))
|
||||
|
||||
// Small delay to ensure different timestamps
|
||||
try await Task.sleep(for: .milliseconds(10))
|
||||
}
|
||||
|
||||
let history = manager.watchHistory()
|
||||
|
||||
#expect(history.count == 3)
|
||||
// Most recent should be first
|
||||
#expect(history.first?.videoID == "video3")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user