Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

143 lines
4.6 KiB
Go

package search
import (
"slices"
"testing"
)
func TestEquivalenceTable_CuratedLookup(t *testing.T) {
tbl := NewEquivalenceTable(nil)
// A word resolves to its class siblings, never itself.
got := tbl.Expand("login")
if len(got) == 0 {
t.Fatal("Expand(login) returned no siblings")
}
if slices.Contains(got, "login") {
t.Error("Expand must not include the query token itself")
}
for _, want := range []string{"auth", "authentication", "signin", "credential"} {
if !slices.Contains(got, want) {
t.Errorf("Expand(login) missing curated sibling %q; got %v", want, got)
}
}
// Case-insensitive.
if len(tbl.Expand("LOGIN")) != len(got) {
t.Error("Expand should be case-insensitive")
}
// A word in no class returns nil.
if tbl.Expand("zzzznotaword") != nil {
t.Error("Expand of an unknown token should return nil")
}
// delete/remove are siblings.
if !slices.Contains(tbl.Expand("delete"), "remove") {
t.Error("delete should expand to remove")
}
if !slices.Contains(tbl.Expand("remove"), "delete") {
t.Error("remove should expand to delete (symmetric)")
}
}
func TestEquivalenceTable_RepoExtra(t *testing.T) {
// A repo-custom class is added and its label joins the class.
tbl := NewEquivalenceTable(map[string][]string{
"widget": {"gadget", "gizmo"},
})
got := tbl.Expand("gadget")
if !slices.Contains(got, "gizmo") || !slices.Contains(got, "widget") {
t.Errorf("repo-extra class not wired: Expand(gadget) = %v", got)
}
// A repo extra whose label is a curated word extends that class.
tbl2 := NewEquivalenceTable(map[string][]string{
"auth": {"oauth", "sso"},
})
authSibs := tbl2.Expand("oauth")
if !slices.Contains(authSibs, "login") {
t.Errorf("repo extra keyed on a curated word should extend the curated class; got %v", authSibs)
}
}
func TestEquivalenceTable_NilSafe(t *testing.T) {
var tbl *EquivalenceTable
if tbl.Expand("auth") != nil {
t.Error("nil EquivalenceTable.Expand should return nil")
}
if tbl.ExpandRelated("auth") != nil {
t.Error("nil EquivalenceTable.ExpandRelated should return nil")
}
if tbl.ClassCount() != 0 {
t.Error("nil EquivalenceTable.ClassCount should be 0")
}
if tbl.RelatedClassCount() != 0 {
t.Error("nil EquivalenceTable.RelatedClassCount should be 0")
}
}
// TestEquivalenceTable_ExpandRelated confirms the concept-relatedness
// thesaurus bridges adjacent concepts (auth -> token / session)
// WITHOUT folding them into the synonym class.
func TestEquivalenceTable_ExpandRelated(t *testing.T) {
tbl := NewEquivalenceTable(nil)
// "auth" relates to the token and session classes.
rel := tbl.ExpandRelated("auth")
if len(rel) == 0 {
t.Fatal("ExpandRelated(auth) returned no related concepts")
}
for _, want := range []string{"token", "jwt", "session"} {
if !slices.Contains(rel, want) {
t.Errorf("ExpandRelated(auth) missing related concept %q; got %v", want, rel)
}
}
// Critical precision guard: the related concepts must NOT be
// synonyms of "auth" -- Expand (the union-find synonym channel)
// must still treat them as separate classes.
authSyn := tbl.Expand("auth")
for _, related := range []string{"token", "session", "jwt"} {
if slices.Contains(authSyn, related) {
t.Errorf("synonym Expand(auth) must NOT contain related concept %q -- "+
"the thesaurus must stay separate from the union-find classes", related)
}
}
// The relation is symmetric: "token" relates back to "auth".
if !slices.Contains(tbl.ExpandRelated("token"), "auth") {
t.Errorf("ExpandRelated(token) should relate back to auth; got %v", tbl.ExpandRelated("token"))
}
// The query token itself is never returned.
if slices.Contains(tbl.ExpandRelated("auth"), "auth") {
t.Error("ExpandRelated must not include the query token itself")
}
// A word in no class has no related concepts.
if tbl.ExpandRelated("zzznotaword") != nil {
t.Error("ExpandRelated of an unknown word should be nil")
}
}
// TestEquivalenceTable_RelatedSeparateFromClasses is the structural
// invariant: adding the thesaurus must not have merged any concepts
// into one giant class. The class count stays at the curated baseline
// and the token / auth classes remain distinct.
func TestEquivalenceTable_RelatedSeparateFromClasses(t *testing.T) {
tbl := NewEquivalenceTable(nil)
if tbl.RelatedClassCount() == 0 {
t.Fatal("the curated thesaurus should produce related-class edges")
}
// "auth" and "token" must be in DIFFERENT union-find classes.
authIdx, ok1 := tbl.member["auth"]
tokenIdx, ok2 := tbl.member["token"]
if !ok1 || !ok2 {
t.Fatal("auth and token must both be curated class members")
}
if authIdx == tokenIdx {
t.Error("auth and token must stay in distinct classes -- the thesaurus must not merge them")
}
}