mana-swift-llm/Tests/ManaLLMTests/LLMPreferenceStoresTests.swift
till 7bbde8ed1a v0.2.0 — Lift LLMBackendPreferenceStore + LLMDownloadOverCellularStore aus memoro-native
Stores leben jetzt im Package mit App-übergreifenden Keys
(mana.llm.backend, mana.llm.allowCellular). Auto-Migration aus
memoro.* Legacy-Keys beim ersten Read (memoro.llmBackend,
memoro.onDeviceLLMEnabled Bool-Toggle, memoro.llmDownloadOverCellular).

Ermöglicht ManaLLMUI in mana-swift-ui 0.8.0 als geteilte Settings-
Schicht für alle 4 Konsumenten (Memoro, Pageta, Comicello, Herbatrium).

Außerdem:
- LLMBackend.removeCachedModel() als Protocol-Methode mit Default-
  No-Op. GemmaBackend überschreibt (async throws statt throws).
- 13 neue Tests in LLMPreferenceStoresTests (.serialized wegen
  UserDefaults.standard).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:19:46 +02:00

98 lines
3.7 KiB
Swift

import Foundation
import Testing
@testable import ManaLLM
/// Tests für die UserDefaults-basierten Preference-Stores.
///
/// **Serialized:** Die Stores greifen hart auf `UserDefaults.standard`
/// zu (bewusst — App-Wide). Tests müssen daher seriell laufen, sonst
/// rennen sie sich gegenseitig in den State. `.serialized` reicht hier,
/// weil die Tests trivial schnell sind (<20 ms total).
@Suite(.serialized)
struct LLMPreferenceStoresTests {
// MARK: - LLMBackendPreferenceStore
@Test func returnsDefaultWhenNoKeySet() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
#expect(LLMBackendPreferenceStore.current == .appleFM)
}
}
@Test func roundTripsValue() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
LLMBackendPreferenceStore.set(.gemmaE4B)
#expect(LLMBackendPreferenceStore.current == .gemmaE4B)
}
}
@Test func migratesMemoroStringKey() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
UserDefaults.standard.set("gemmaE2B", forKey: "memoro.llmBackend")
#expect(LLMBackendPreferenceStore.current == .gemmaE2B)
// Legacy-Key wurde gelöscht
#expect(UserDefaults.standard.object(forKey: "memoro.llmBackend") == nil)
// Neuer Key gesetzt
#expect(UserDefaults.standard.string(forKey: "mana.llm.backend") == "gemmaE2B")
}
}
@Test func migratesMemoroBoolToggleTrueToAppleFM() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
UserDefaults.standard.set(true, forKey: "memoro.onDeviceLLMEnabled")
#expect(LLMBackendPreferenceStore.current == .appleFM)
#expect(UserDefaults.standard.object(forKey: "memoro.onDeviceLLMEnabled") == nil)
}
}
@Test func migratesMemoroBoolToggleFalseToNoOp() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
UserDefaults.standard.set(false, forKey: "memoro.onDeviceLLMEnabled")
#expect(LLMBackendPreferenceStore.current == .noOp)
}
}
@Test func newKeyTrumpsLegacy() {
withClean(keys: ["mana.llm.backend", "memoro.llmBackend", "memoro.onDeviceLLMEnabled"]) {
UserDefaults.standard.set("gemmaE4B", forKey: "mana.llm.backend")
UserDefaults.standard.set("appleFM", forKey: "memoro.llmBackend")
#expect(LLMBackendPreferenceStore.current == .gemmaE4B)
// Legacy bleibt unangetastet wenn neuer Key schon existiert
#expect(UserDefaults.standard.string(forKey: "memoro.llmBackend") == "appleFM")
}
}
// MARK: - LLMDownloadOverCellularStore
@Test func cellularDefaultsToFalse() {
withClean(keys: ["mana.llm.allowCellular", "memoro.llmDownloadOverCellular"]) {
#expect(LLMDownloadOverCellularStore.isAllowed == false)
}
}
@Test func cellularRoundTripsTrue() {
withClean(keys: ["mana.llm.allowCellular", "memoro.llmDownloadOverCellular"]) {
LLMDownloadOverCellularStore.set(true)
#expect(LLMDownloadOverCellularStore.isAllowed == true)
}
}
@Test func cellularMigratesMemoroKey() {
withClean(keys: ["mana.llm.allowCellular", "memoro.llmDownloadOverCellular"]) {
UserDefaults.standard.set(true, forKey: "memoro.llmDownloadOverCellular")
#expect(LLMDownloadOverCellularStore.isAllowed == true)
#expect(UserDefaults.standard.object(forKey: "memoro.llmDownloadOverCellular") == nil)
#expect(UserDefaults.standard.bool(forKey: "mana.llm.allowCellular") == true)
}
}
// MARK: - Helpers
/// Räumt vor und nach dem Block bestimmte Keys ab, damit Tests
/// reproduzierbar starten und kein Test-State über Tests hinweg
/// leakt.
private func withClean(keys: [String], _ block: () -> Void) {
for key in keys { UserDefaults.standard.removeObject(forKey: key) }
defer { for key in keys { UserDefaults.standard.removeObject(forKey: key) } }
block()
}
}