Files
Maven/cmd/mavend/speaker_wiring_test.go
T
kami ec5167de3a speaker: do not ship three methods that cannot work
The package comment, the embedder log and the startup line all said
enrolment was live and only recognition was blocked. Enroll embeds every
sample before it stores anything, so with no model on the box it fails
on the first sample with ErrDisabled and nothing is ever stored. List
then returns an empty list forever and Forget has nothing to delete. The
shipped state was three methods, all no-ops, announced as a working
half.

SpeakerConfig.Recognizes was written as the gate for this and never
called, so a block with enabled and no model_path wired everything and
skipped the one warning the operator needed. It is the gate now, and
that config shape logs why it stayed off.

Three smaller repairs. ErrDisabled had no case in speakerErr and reached
the surface as an opaque core failure, when it means the same thing
ErrUnknownMethod does. Forget read the row first and answered ErrNotFound
on a second call, so the layer documented as the one that must always
work reintroduced a failure for a voiceprint that was already gone.
And a row with unparsable metadata listed as a plausible profile named
after its own id with 0 samples, which is what a real minimal enrolment
looks like; it is reported as damaged now.

Found in review of #74.
2026-08-01 14:12:43 +04:00

51 lines
1.7 KiB
Go

package main
import (
"errors"
"testing"
"github.com/kami/maven/internal/config"
"github.com/kami/maven/internal/ipc"
"github.com/kami/maven/internal/speaker"
)
// "enabled": true with no model_path used to wire all three methods and log
// "enrolment on". Nothing behind them works without an embedder, so the
// capability stays off and the socket answers "no such method".
func TestSpeakerStaysOffWithoutAModelPath(t *testing.T) {
srv := &ipc.Server{}
cfg := &config.Config{Speaker: &config.SpeakerConfig{Enabled: true}}
wireSpeaker(srv, nil, cfg)
if srv.EnrollSpeakerFn != nil || srv.ListSpeakersFn != nil || srv.ForgetSpeakerFn != nil {
t.Error("speaker methods were wired with nothing to embed with")
}
}
// The gate is Recognizes(), so a disabled block with a model path is off too.
func TestSpeakerStaysOffWhenDisabled(t *testing.T) {
srv := &ipc.Server{}
cfg := &config.Config{Speaker: &config.SpeakerConfig{ModelPath: "/nope/ecapa.onnx"}}
wireSpeaker(srv, nil, cfg)
if srv.EnrollSpeakerFn != nil {
t.Error("speaker methods were wired for a disabled block")
}
}
// ErrDisabled is "this capability is off", not "core broke". It used to fall
// through speakerErr's default and reach the surface as an opaque failure.
func TestSpeakerErrMapsDisabledToUnknownMethod(t *testing.T) {
if got := speakerErr(speaker.ErrDisabled); !errors.Is(got, ipc.ErrUnknownMethod) {
t.Errorf("speakerErr(ErrDisabled) = %v, want ErrUnknownMethod", got)
}
if got := speakerErr(speaker.ErrNotFound); !errors.Is(got, ipc.ErrNoFact) {
t.Errorf("speakerErr(ErrNotFound) = %v, want ErrNoFact", got)
}
if got := speakerErr(nil); got != nil {
t.Errorf("speakerErr(nil) = %v", got)
}
}