Files
Flaro/Packages/FlixyKit/Tests/FlixyDownloadsTests/DownloadStateTests.swift
T
GuidoandClaude Opus 5 1ed64bf166 Phase 0: Projektgerüst, Design-Tokens und Navigation
Legt das Fundament für den Plex-Client an:

- XcodeGen-Projekt nach dem Muster von Onyx (com.scarriffleservices,
  strict concurrency, Projektdatei nicht eingecheckt)
- FlixyKit mit vier Modulen: PlexKit, FlixyDesign, FlixyPlayback,
  FlixyDownloads. macOS steht als Plattform mit drin, damit die
  Logik-Tests ohne Simulator laufen.
- FlixyDesign: dunkle Farb-Tokens, Schriftskala, Poster- und
  Querformat-Kacheln, Aufmacher-Bild. Die Akzentfarbe fließt über die
  Umgebung und wird per WCAG-Rechnung lesbar gehalten.
- PlexKit: Kopfzeilen-Identität, Transport-Protokoll, Fehlerarten und
  die Medienmodelle samt nachsichtigem Decoder.
- FlixyPlayback: VideoPlayerEngine-Protokoll und PlaybackDecider — die
  eine Funktion, an der Phase 2 das Transcoding andockt.
- Ausfahrbare Seitenleiste auf dem iPhone, Split-View auf dem iPad.
- 45 Tests, alle grün.

VLCKit 4.0.0-a24 ist vorab geprüft: baut über SPM unter Swift 6 mit
strict concurrency. Die iOS-Slice ist mit 239 MB allerdings gewichtig,
deshalb bekommt sie in Phase 1 ein eigenes Target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:04:40 +02:00

80 lines
3.3 KiB
Swift

import Testing
@testable import FlixyDownloads
@Suite("Download-Zustandsautomat")
struct DownloadStateTests {
@Test("Ein abgeschlossener Download bleibt abgeschlossen")
func completedIsTerminal() {
// Der wichtigste Übergang, den es NICHT geben darf: wäre er erlaubt,
// könnte ein verspäteter Rückruf aus der Hintergrundsitzung eine
// fertige Datei wieder in die Warteschlange schieben — und sie damit
// ein zweites Mal herunterladen.
for next in DownloadState.allCases {
#expect(!DownloadState.completed.canTransition(to: next))
}
}
@Test("Gescheiterte Downloads dürfen zurück in die Warteschlange")
func failedCanBeRetried() {
#expect(DownloadState.failed.canTransition(to: .queued))
}
@Test("Gescheiterte Downloads springen nicht direkt ins Übertragen")
func failedCannotSkipTheQueue() {
// Sonst umgeht der Wiederholen-Knopf die Prüfungen auf WLAN und
// Speicherplatz, die beim Verlassen der Warteschlange stattfinden.
#expect(!DownloadState.failed.canTransition(to: .downloading))
}
@Test("Angehalten und laufend zählen beim Speicherplatz mit")
func partialTransfersOccupyStorage() {
// Beide haben bereits Daten auf der Platte. Zählte man sie nicht mit,
// liefe das Gerät trotz gesetzter Grenze voll.
#expect(DownloadState.downloading.occupiesStorage)
#expect(DownloadState.paused.occupiesStorage)
#expect(DownloadState.completed.occupiesStorage)
#expect(!DownloadState.queued.occupiesStorage)
#expect(!DownloadState.waiting.occupiesStorage)
#expect(!DownloadState.failed.occupiesStorage)
}
@Test("Nur wartende Zustände dürfen mit dem Übertragen beginnen")
func onlyWaitingStatesAreTransferable() {
#expect(DownloadState.queued.isTransferable)
#expect(DownloadState.waiting.isTransferable)
#expect(!DownloadState.downloading.isTransferable)
#expect(!DownloadState.paused.isTransferable)
#expect(!DownloadState.completed.isTransferable)
}
@Test("Kein Zustand geht in sich selbst über")
func noSelfTransitions() {
// Ein Übergang auf sich selbst wäre ein stilles Nichts und würde
// Zählerstände wie „Versuche" doppelt hochzählen.
for state in DownloadState.allCases {
#expect(!state.canTransition(to: state))
}
}
@Test("Jeder Zustand außer dem abgeschlossenen hat einen Ausgang")
func everyStateCanProgress() {
for state in DownloadState.allCases where state != .completed {
let hasExit = DownloadState.allCases.contains { state.canTransition(to: $0) }
#expect(hasExit, "\(state.rawValue) ist eine Sackgasse")
}
}
@Test("Die gespeicherten Namen bleiben stabil")
func rawValuesAreStable() {
// Sie landen in SwiftData. Ein umbenannter Fall würde beim nächsten
// Start alle laufenden Downloads unlesbar machen.
#expect(DownloadState.queued.rawValue == "queued")
#expect(DownloadState.downloading.rawValue == "downloading")
#expect(DownloadState.paused.rawValue == "paused")
#expect(DownloadState.waiting.rawValue == "waiting")
#expect(DownloadState.completed.rawValue == "completed")
#expect(DownloadState.failed.rawValue == "failed")
}
}