add model_test.go with comprehensive Model struct tests

This commit is contained in:
leonardotrapani
2026-02-01 00:41:14 +01:00
parent 4fe570081d
commit 16ef8bcf0a
3 changed files with 344 additions and 1 deletions
+331
View File
@@ -0,0 +1,331 @@
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 model",
model: Model{ID: "scribe_v1-streaming", Streaming: true},
expected: true,
},
{
name: "batch model",
model: Model{ID: "whisper-1", Streaming: false},
expected: false,
},
}
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_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,
Streaming: true,
Local: true,
AdapterType: "test-adapter",
SupportedLanguages: []string{"en", "es"},
Endpoint: &EndpointConfig{
BaseURL: "https://api.test.com",
Path: "/v1/test",
},
LocalInfo: &LocalModelInfo{
Filename: "test.bin",
Size: "100MB",
DownloadURL: "https://example.com/test.bin",
},
}
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.Streaming {
t.Error("Streaming 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")
}
}
+12
View File
@@ -62,3 +62,15 @@ Started: Sun Feb 1 12:22:47 AM CET 2026
- Added MockStreamingAdapter and comprehensive tests - Added MockStreamingAdapter and comprehensive tests
- Tests verify: start/stop, result accumulation, partial result filtering, error handling, concurrent access - Tests verify: start/stop, result accumulation, partial result filtering, error handling, concurrent access
- All tests passing with -race flag, typecheck passes - All tests passing with -race flag, typecheck passes
### Task 7: Write tests for Model, Provider, and interfaces
- Created `internal/provider/model_test.go`
- TestModel_NeedsDownload: local with LocalInfo = true, cloud = false, nil = false
- TestModel_IsStreaming: returns Streaming field value
- TestModel_SupportsLanguage: multilingual supports all, english-only supports en, auto always true
- TestModel_SupportsAllLanguages: true when 57 languages, false otherwise
- TestModelType_Constants: Transcription=0, LLM=1
- TestEndpointConfig_Fields, TestLocalModelInfo_Fields: struct fields accessible
- TestModel_AllFields: comprehensive struct field test
- provider_test.go already had GetModel, ModelsOfType, FindModelByID, ModelsForLanguage, ValidateModelLanguage tests
- All tests passing, typecheck passes
+1 -1
View File
@@ -185,7 +185,7 @@
"Language validation helpers tested with proper error messages", "Language validation helpers tested with proper error messages",
"Typecheck passes" "Typecheck passes"
], ],
"passes": false "passes": true
}, },
// ============================================================================ // ============================================================================
// PHASE 2: MIGRATE PROVIDERS TO NEW MODEL STRUCTURE // PHASE 2: MIGRATE PROVIDERS TO NEW MODEL STRUCTURE