Files
hyprvoice/internal/provider/model_test.go
T
2026-02-01 17:24:43 +01:00

416 lines
10 KiB
Go

package provider
import (
"testing"
"github.com/leonardotrapani/hyprvoice/internal/language"
)
func TestModel_NeedsDownload(t *testing.T) {
tests := []struct {
name string
model Model
expected bool
}{
{
name: "local model with LocalInfo",
model: Model{
ID: "base.en",
Local: true,
LocalInfo: &LocalModelInfo{
Filename: "ggml-base.en.bin",
Size: "142MB",
DownloadURL: "https://example.com/model.bin",
},
},
expected: true,
},
{
name: "cloud model without LocalInfo",
model: Model{
ID: "whisper-1",
Local: false,
Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/audio/transcriptions"},
},
expected: false,
},
{
name: "model with nil LocalInfo",
model: Model{
ID: "gpt-4o",
LocalInfo: nil,
},
expected: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.NeedsDownload(); got != tc.expected {
t.Errorf("NeedsDownload() = %v, want %v", got, tc.expected)
}
})
}
}
func TestModel_IsStreaming(t *testing.T) {
tests := []struct {
name string
model Model
expected bool
}{
{
name: "streaming-only model",
model: Model{ID: "scribe_v2_realtime", SupportsBatch: false, SupportsStreaming: true},
expected: true,
},
{
name: "batch-only model",
model: Model{ID: "whisper-1", SupportsBatch: true, SupportsStreaming: false},
expected: false,
},
{
name: "both modes model",
model: Model{ID: "nova-3", SupportsBatch: true, SupportsStreaming: true},
expected: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.IsStreaming(); got != tc.expected {
t.Errorf("IsStreaming() = %v, want %v", got, tc.expected)
}
})
}
}
func TestModel_SupportsBothModes(t *testing.T) {
tests := []struct {
name string
model Model
expected bool
}{
{
name: "streaming-only model",
model: Model{ID: "scribe_v2_realtime", SupportsBatch: false, SupportsStreaming: true},
expected: false,
},
{
name: "batch-only model",
model: Model{ID: "whisper-1", SupportsBatch: true, SupportsStreaming: false},
expected: false,
},
{
name: "both modes model",
model: Model{ID: "nova-3", SupportsBatch: true, SupportsStreaming: true},
expected: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.SupportsBothModes(); got != tc.expected {
t.Errorf("SupportsBothModes() = %v, want %v", got, tc.expected)
}
})
}
}
func TestModel_SupportsLanguage(t *testing.T) {
allCodes := language.AllLanguageCodes()
multilingualModel := Model{
ID: "whisper-large-v3",
SupportedLanguages: allCodes,
}
englishOnlyModel := Model{
ID: "base.en",
SupportedLanguages: []string{"en"},
}
tests := []struct {
name string
model Model
code string
expected bool
}{
{
name: "multilingual supports en",
model: multilingualModel,
code: "en",
expected: true,
},
{
name: "multilingual supports es",
model: multilingualModel,
code: "es",
expected: true,
},
{
name: "multilingual supports zh",
model: multilingualModel,
code: "zh",
expected: true,
},
{
name: "english-only supports en",
model: englishOnlyModel,
code: "en",
expected: true,
},
{
name: "english-only does not support es",
model: englishOnlyModel,
code: "es",
expected: false,
},
{
name: "english-only does not support zh",
model: englishOnlyModel,
code: "zh",
expected: false,
},
{
name: "auto always supported on multilingual",
model: multilingualModel,
code: "",
expected: true,
},
{
name: "auto always supported on english-only",
model: englishOnlyModel,
code: "",
expected: true,
},
{
name: "empty SupportedLanguages still supports auto",
model: Model{ID: "empty", SupportedLanguages: []string{}},
code: "",
expected: true,
},
{
name: "empty SupportedLanguages does not support en",
model: Model{ID: "empty", SupportedLanguages: []string{}},
code: "en",
expected: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.SupportsLanguage(tc.code); got != tc.expected {
t.Errorf("SupportsLanguage(%q) = %v, want %v", tc.code, got, tc.expected)
}
})
}
}
func TestModel_SupportsAllLanguages(t *testing.T) {
allCodes := language.AllLanguageCodes()
tests := []struct {
name string
model Model
expected bool
}{
{
name: "model with all 57 languages",
model: Model{
ID: "whisper-large-v3",
SupportedLanguages: allCodes,
},
expected: true,
},
{
name: "english-only model",
model: Model{
ID: "base.en",
SupportedLanguages: []string{"en"},
},
expected: false,
},
{
name: "model with some languages",
model: Model{
ID: "partial",
SupportedLanguages: []string{"en", "es", "fr", "de"},
},
expected: false,
},
{
name: "model with empty languages",
model: Model{
ID: "empty",
SupportedLanguages: []string{},
},
expected: false,
},
{
name: "model with nil languages",
model: Model{
ID: "nil",
SupportedLanguages: nil,
},
expected: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if got := tc.model.SupportsAllLanguages(); got != tc.expected {
t.Errorf("SupportsAllLanguages() = %v, want %v", got, tc.expected)
}
})
}
}
func TestModelType_Constants(t *testing.T) {
// verify ModelType constants exist and are distinct
if Transcription == LLM {
t.Error("Transcription and LLM should be different")
}
// verify they're the expected values
if Transcription != 0 {
t.Errorf("Transcription = %d, want 0", Transcription)
}
if LLM != 1 {
t.Errorf("LLM = %d, want 1", LLM)
}
}
func TestEndpointConfig_Fields(t *testing.T) {
endpoint := EndpointConfig{
BaseURL: "https://api.openai.com",
Path: "/v1/audio/transcriptions",
}
if endpoint.BaseURL != "https://api.openai.com" {
t.Errorf("BaseURL = %q, want 'https://api.openai.com'", endpoint.BaseURL)
}
if endpoint.Path != "/v1/audio/transcriptions" {
t.Errorf("Path = %q, want '/v1/audio/transcriptions'", endpoint.Path)
}
}
func TestLocalModelInfo_Fields(t *testing.T) {
info := LocalModelInfo{
Filename: "ggml-base.en.bin",
Size: "142MB",
DownloadURL: "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin",
}
if info.Filename != "ggml-base.en.bin" {
t.Errorf("Filename = %q, want 'ggml-base.en.bin'", info.Filename)
}
if info.Size != "142MB" {
t.Errorf("Size = %q, want '142MB'", info.Size)
}
if info.DownloadURL != "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin" {
t.Errorf("DownloadURL = %q", info.DownloadURL)
}
}
func TestModel_AllFields(t *testing.T) {
// verify all Model struct fields can be set and read correctly
model := Model{
ID: "test-model",
Name: "Test Model",
Description: "A test model for verification",
Type: Transcription,
SupportsBatch: true,
SupportsStreaming: true,
Local: true,
AdapterType: "test-adapter",
StreamingAdapter: "test-streaming-adapter",
SupportedLanguages: []string{"en", "es"},
Endpoint: &EndpointConfig{
BaseURL: "https://api.test.com",
Path: "/v1/test",
},
StreamingEndpoint: &EndpointConfig{
BaseURL: "wss://api.test.com",
Path: "/v1/stream",
},
LocalInfo: &LocalModelInfo{
Filename: "test.bin",
Size: "100MB",
DownloadURL: "https://example.com/test.bin",
},
DocsURL: "https://example.com/docs/languages",
}
if model.ID != "test-model" {
t.Errorf("ID = %q, want 'test-model'", model.ID)
}
if model.Name != "Test Model" {
t.Errorf("Name = %q, want 'Test Model'", model.Name)
}
if model.Description != "A test model for verification" {
t.Errorf("Description = %q", model.Description)
}
if model.Type != Transcription {
t.Errorf("Type = %v, want Transcription", model.Type)
}
if !model.SupportsBatch {
t.Error("SupportsBatch should be true")
}
if !model.SupportsStreaming {
t.Error("SupportsStreaming should be true")
}
if !model.Local {
t.Error("Local should be true")
}
if model.AdapterType != "test-adapter" {
t.Errorf("AdapterType = %q, want 'test-adapter'", model.AdapterType)
}
if len(model.SupportedLanguages) != 2 {
t.Errorf("SupportedLanguages length = %d, want 2", len(model.SupportedLanguages))
}
if model.Endpoint == nil {
t.Error("Endpoint should not be nil")
}
if model.LocalInfo == nil {
t.Error("LocalInfo should not be nil")
}
if model.DocsURL != "https://example.com/docs/languages" {
t.Errorf("DocsURL = %q, want 'https://example.com/docs/languages'", model.DocsURL)
}
}
func TestAllTranscriptionModels_HaveDocsURL(t *testing.T) {
// verify all transcription models have DocsURL set
providers := []string{"openai", "groq", "mistral", "elevenlabs", "deepgram", "whisper-cpp"}
expectedDocsURLs := map[string]string{
"openai": "https://platform.openai.com/docs/guides/speech-to-text#supported-languages",
"groq": "https://console.groq.com/docs/speech-to-text#supported-languages",
"mistral": "https://docs.mistral.ai/capabilities/speech/",
"elevenlabs": "https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages",
"deepgram": "https://developers.deepgram.com/docs/language",
"whisper-cpp": "https://github.com/openai/whisper#available-models-and-languages",
}
for _, pName := range providers {
p := GetProvider(pName)
if p == nil {
t.Errorf("GetProvider(%q) returned nil", pName)
continue
}
expectedURL := expectedDocsURLs[pName]
for _, m := range p.Models() {
if m.Type != Transcription {
continue
}
if m.DocsURL == "" {
t.Errorf("%s/%s: DocsURL is empty", pName, m.ID)
} else if m.DocsURL != expectedURL {
t.Errorf("%s/%s: DocsURL = %q, want %q", pName, m.ID, m.DocsURL, expectedURL)
}
}
}
}