Files
gart/code/Hex-main/HexTests/ModelDownloadFeatureTests.swift
T

135 lines
4.7 KiB
Swift
Executable File

import ComposableArchitecture
import HexCore
import XCTest
@testable import Hex
@MainActor
final class ModelDownloadFeatureTests: XCTestCase {
func testPersistedModelDisplaysWhileAvailabilityLoads() {
let state = makeState(selectedModel: ParakeetModel.englishV2.identifier)
XCTAssertTrue(state.availableModels.isEmpty)
XCTAssertEqual(state.selectedModelNameForDisplay, ParakeetModel.englishV2.identifier)
}
func testSuggestedParakeetFollowsOutputLanguage() {
var state = makeState(selectedModel: "")
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.englishV2.identifier)
state.$hexSettings.withLock { $0.outputLanguage = "en" }
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.englishV2.identifier)
state.$hexSettings.withLock { $0.outputLanguage = "fr" }
XCTAssertEqual(state.preferredParakeetIdentifier, ParakeetModel.multilingualV3.identifier)
}
func testFailedModelRefreshPreservesInstalledSelection() async {
var state = makeState(selectedModel: "installed-model")
state.availableModels = [ModelInfo(name: "installed-model", isDownloaded: true)]
let store = TestStore(initialState: state) {
ModelDownloadFeature()
} withDependencies: {
$0.transcription.getRecommendedModels = { throw TestError() }
$0.transcription.getAvailableModels = { [] }
}
await store.send(.fetchModels) {
$0.isLoadingModels = true
}
await store.receive(\.modelsLoadFailed) {
$0.isLoadingModels = false
}
XCTAssertEqual(store.state.hexSettings.selectedModel, "installed-model")
XCTAssertTrue(store.state.availableModels[id: "installed-model"]?.isDownloaded == true)
}
func testStaleDownloadUpdatesAreIgnored() async {
let currentID = UUID()
var state = makeState(selectedModel: "installed-model")
state.isDownloading = true
state.downloadingModelName = "new-model"
state.activeDownloadID = currentID
state.downloadProgress = 0.25
let store = TestStore(initialState: state) {
ModelDownloadFeature()
}
await store.send(.downloadProgress(id: UUID(), progress: 0.9))
await store.send(.downloadCompleted(id: UUID(), result: .success("other-model")))
XCTAssertEqual(store.state.activeDownloadID, currentID)
XCTAssertEqual(store.state.downloadProgress, 0.25)
XCTAssertEqual(store.state.hexSettings.selectedModel, "installed-model")
}
func testModelsLoadedNeverClearsSelectionWhenNothingDetected() async {
// Regression: 0.8.0 cleared selectedModel to "" when an availability scan
// came back empty (e.g. after FluidAudio moved its cache directory),
// permanently breaking transcription with no visible error.
var state = makeState(selectedModel: ParakeetModel.englishV2.identifier)
state.availableModels = []
let store = TestStore(initialState: state) {
ModelDownloadFeature()
}
store.exhaustivity = .off
await store.send(.modelsLoaded(recommended: "", available: []))
XCTAssertEqual(store.state.hexSettings.selectedModel, ParakeetModel.englishV2.identifier)
}
func testSelectedModelIsDownloadedMatchesPatterns() {
var state = makeState(selectedModel: "distil*large-v3")
state.availableModels = [
ModelInfo(name: "distil-whisper_distil-large-v3", isDownloaded: true)
]
XCTAssertTrue(state.selectedModelIsDownloaded)
state.availableModels = [
ModelInfo(name: "distil-whisper_distil-large-v3", isDownloaded: false)
]
XCTAssertFalse(state.selectedModelIsDownloaded)
var emptyState = makeState(selectedModel: "")
emptyState.availableModels = [ModelInfo(name: "some-model", isDownloaded: true)]
XCTAssertFalse(emptyState.selectedModelIsDownloaded)
}
func testDeletingAnotherModelDoesNotChangeSelection() async {
var state = makeState(selectedModel: "selected-model")
state.availableModels = [
ModelInfo(name: "selected-model", isDownloaded: true),
ModelInfo(name: "other-model", isDownloaded: true),
]
let store = TestStore(initialState: state) {
ModelDownloadFeature()
} withDependencies: {
$0.transcription.deleteModel = { _ in
try await Task.sleep(for: .seconds(60))
}
}
await store.send(.deleteModel("other-model"))
XCTAssertEqual(store.state.hexSettings.selectedModel, "selected-model")
await store.skipInFlightEffects()
}
private func makeState(selectedModel: String) -> ModelDownloadFeature.State {
var settings = HexSettings()
settings.selectedModel = selectedModel
settings.hasCompletedModelBootstrap = true
return ModelDownloadFeature.State(
hexSettings: Shared(value: settings),
modelBootstrapState: Shared(value: .init(isModelReady: true))
)
}
}
private struct TestError: Error {}