package services import ( "testing" "voicesnap/internal/config" "voicesnap/internal/model" ) func TestEngineServiceDownloadStatusTracksCancelAndFinish(t *testing.T) { service := NewEngineService(nil) status := service.GetModelDownloadStatus() if status["active"].(bool) { t.Fatal("new service should not report an active model download") } _, finish, err := service.beginModelDownload("zipformer-ko") if err != nil { t.Fatal(err) } status = service.GetModelDownloadStatus() if !status["active"].(bool) { t.Fatal("active download was not reported") } if got := status["modelID"]; got != "zipformer-ko" { t.Fatalf("modelID = %v, want zipformer-ko", got) } if status["cancelling"].(bool) { t.Fatal("new download should not start in cancelling state") } service.updateModelDownloadProgress("zipformer-ko", 42.5, 425, 1000) status = service.GetModelDownloadStatus() if got := status["percent"]; got != 42.5 { t.Fatalf("percent = %v, want 42.5", got) } if got := status["downloaded"]; got != int64(425) { t.Fatalf("downloaded = %v, want 425", got) } if got := status["total"]; got != int64(1000) { t.Fatalf("total = %v, want 1000", got) } if !service.CancelModelDownload("zipformer-ko") { t.Fatal("expected active download to be cancelled") } status = service.GetModelDownloadStatus() if !status["cancelling"].(bool) { t.Fatal("cancelled download should report cancelling state until finish") } finish() status = service.GetModelDownloadStatus() if status["active"].(bool) { t.Fatal("finished download should clear active state") } if got := status["modelID"]; got != "" { t.Fatalf("modelID after finish = %v, want empty", got) } } func TestListModelOptionsForCantoneseOnlyOffersSenseVoiceYue(t *testing.T) { cfg := config.Default() cfg.LanguageMode = config.LanguageModeManual cfg.LanguageID = model.CantoneseLanguageID cfg.ModelSelectionMode = config.ModelSelectionModeAuto service := NewEngineService(cfg) options := service.ListModelOptions() if len(options) != 1 { t.Fatalf("options len = %d, want 1: %#v", len(options), options) } if got := options[0]["modelID"]; got != model.SenseVoiceYueModelID { t.Fatalf("modelID = %v, want %s", got, model.SenseVoiceYueModelID) } if got := options[0]["isDefault"]; got != true { t.Fatalf("isDefault = %v, want true", got) } } func TestListModelOptionsForLanguagePreviewsCantoneseWithoutPersisting(t *testing.T) { cfg := config.Default() cfg.LanguageMode = config.LanguageModeManual cfg.LanguageID = model.DefaultLanguageID service := NewEngineService(cfg) options := service.ListModelOptionsForLanguage(model.CantoneseLanguageID) if len(options) != 1 { t.Fatalf("options len = %d, want 1: %#v", len(options), options) } if got := options[0]["modelID"]; got != model.SenseVoiceYueModelID { t.Fatalf("modelID = %v, want %s", got, model.SenseVoiceYueModelID) } if got := options[0]["languageID"]; got != model.CantoneseLanguageID { t.Fatalf("languageID = %v, want %s", got, model.CantoneseLanguageID) } if cfg.LanguageID != model.DefaultLanguageID { t.Fatalf("cfg.LanguageID = %q, want unchanged %q", cfg.LanguageID, model.DefaultLanguageID) } } func TestListModelOptionsForLanguageDoesNotMarkSharedCurrentModelForPendingLanguage(t *testing.T) { cfg := config.Default() cfg.LanguageMode = config.LanguageModeManual cfg.LanguageID = model.DefaultLanguageID cfg.ModelSelectionMode = config.ModelSelectionModeManual cfg.SelectedModelID = model.XASRZhEn960ModelID service := NewEngineService(cfg) options := service.ListModelOptionsForLanguage(model.EnglishLanguageID) var found bool for _, option := range options { if option["modelID"] != model.XASRZhEn960ModelID { continue } found = true if got := option["isCurrent"]; got != false { t.Fatalf("isCurrent = %v, want false for pending language shared model", got) } } if !found { t.Fatalf("expected %s in English options: %#v", model.XASRZhEn960ModelID, options) } } func TestAllowedModelProfileForLanguageAcceptsCantoneseWithoutPersisting(t *testing.T) { cfg := config.Default() cfg.LanguageMode = config.LanguageModeManual cfg.LanguageID = model.DefaultLanguageID service := NewEngineService(cfg) profile, languageProfile, err := service.allowedModelProfileForLanguage(model.CantoneseLanguageID, model.SenseVoiceYueModelID) if err != nil { t.Fatalf("allowedModelProfileForLanguage returned error: %v", err) } if profile.ID != model.SenseVoiceYueModelID { t.Fatalf("profile id = %q, want %q", profile.ID, model.SenseVoiceYueModelID) } if languageProfile.ID != model.CantoneseLanguageID { t.Fatalf("language profile id = %q, want %q", languageProfile.ID, model.CantoneseLanguageID) } if _, err := service.allowedModelProfile(model.SenseVoiceYueModelID); err == nil { t.Fatal("expected current Chinese language to reject SenseVoice Yue") } } func TestAllowedModelProfileForLanguageAcceptsTargetLanguageUpgradeWithoutPersisting(t *testing.T) { cfg := config.Default() cfg.LanguageMode = config.LanguageModeManual cfg.LanguageID = model.DefaultLanguageID service := NewEngineService(cfg) profile, languageProfile, err := service.allowedModelProfileForLanguage(model.EnglishLanguageID, model.ParakeetModelID) if err != nil { t.Fatalf("allowedModelProfileForLanguage returned error: %v", err) } if profile.ID != model.ParakeetModelID { t.Fatalf("profile id = %q, want %q", profile.ID, model.ParakeetModelID) } if languageProfile.ID != model.EnglishLanguageID { t.Fatalf("language profile id = %q, want %q", languageProfile.ID, model.EnglishLanguageID) } if _, err := service.allowedModelProfile(model.ParakeetModelID); err == nil { t.Fatal("expected current Chinese language to reject Parakeet") } }