package matching import ( "context" "testing" "github.com/shopspring/decimal" "github.com/missionworkforce/missionmatch/internal/modules/matching/engine" ) type stubEmbedder struct{ vecs map[string][]float32 } func (s stubEmbedder) Embed(_ context.Context, t string) ([]float32, error) { if v, ok := s.vecs[t]; ok { return v, nil } return []float32{0, 0}, nil } func (s stubEmbedder) EmbedBatch(ctx context.Context, ts []string) ([][]float32, error) { out := make([][]float32, len(ts)) for i, t := range ts { out[i], _ = s.Embed(ctx, t) } return out, nil } func (s stubEmbedder) Dimension() int { return 2 } func TestEmbeddingSkillMatcher(t *testing.T) { // Unit vectors: cosine("Amazon EKS","Kubernetes")=0.9 ; cosine with "Java"=0. emb := stubEmbedder{vecs: map[string][]float32{ "Amazon EKS": {1, 0}, "Kubernetes": {0.9, 0.43589}, // |v| ~= 1 "Java": {0, 1}, }} m := NewEmbeddingSkillMatcher(emb) req := engine.RequiredSkill{SkillID: "eks", Name: "Amazon EKS"} cands := []engine.CandidateSkill{{SkillID: "k8s", Name: "Kubernetes"}, {SkillID: "java", Name: "Java"}} got, ok := m.Match(context.Background(), req, cands, decimal.RequireFromString("0.80")) if !ok || got.CandidateName != "Kubernetes" || got.CandidateSkillID != "k8s" { t.Fatalf("threshold 0.80 should match Kubernetes, got %+v ok=%v", got, ok) } if got.Similarity.LessThan(decimal.RequireFromString("0.89")) { t.Fatalf("similarity should be ~0.9, got %s", got.Similarity) } if _, ok := m.Match(context.Background(), req, cands, decimal.RequireFromString("0.95")); ok { t.Fatal("threshold 0.95 must reject a 0.9 similarity") } if _, ok := m.Match(context.Background(), req, nil, decimal.RequireFromString("0.50")); ok { t.Fatal("no candidate skills must yield no match") } }