2026-07-07 17:11:25 +08:00
|
|
|
package model
|
|
|
|
|
|
2026-07-08 10:37:06 +08:00
|
|
|
import (
|
|
|
|
|
"fmt"
|
2026-07-09 16:31:19 +08:00
|
|
|
"strings"
|
2026-07-08 10:37:06 +08:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"modelRT/constants"
|
|
|
|
|
)
|
2026-07-07 17:11:25 +08:00
|
|
|
|
|
|
|
|
func TestFuzzyRecommendOffsetUsesFullMatchedInput(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
searchPrefix string
|
|
|
|
|
searchInput string
|
|
|
|
|
want int
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "zone fuzzy prefix",
|
|
|
|
|
searchPrefix: "grid000",
|
|
|
|
|
searchInput: "z",
|
|
|
|
|
want: len([]rune("grid000.z")),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "level one fuzzy",
|
|
|
|
|
searchPrefix: "",
|
|
|
|
|
searchInput: "g",
|
|
|
|
|
want: len([]rune("g")),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "measurement fuzzy preserves config token",
|
|
|
|
|
searchPrefix: "grid.zone.station.nspath.comp.config",
|
|
|
|
|
searchInput: "m",
|
|
|
|
|
want: len([]rune("grid.zone.station.nspath.comp.config.m")),
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
got := fuzzyRecommendOffset(tt.searchPrefix, tt.searchInput)
|
|
|
|
|
if got != tt.want {
|
|
|
|
|
t.Fatalf("expected offset %d, got %d", tt.want, got)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-08 10:37:06 +08:00
|
|
|
|
2026-07-09 16:31:19 +08:00
|
|
|
func TestNormalizeRecommendResultsUsesInputLengthForExactCompletion(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
input string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "full token1 to token7 structure",
|
|
|
|
|
input: "grid000.zone000.station000.220kV_学府路1-testProject1.compTag.config.IA_rms_CTA-testProject1",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "local token4 to token7 structure",
|
|
|
|
|
input: "220kV_学府路1-testProject1.IA_rms_CTA-testProject1",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
results := map[string]SearchResult{
|
|
|
|
|
constants.MeasTagRecommendHierarchyType.String(): {
|
|
|
|
|
RecommendType: constants.MeasTagRecommendHierarchyType,
|
|
|
|
|
QueryDatas: []string{""},
|
|
|
|
|
IsFuzzy: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := normalizeRecommendResults(tt.input, results)
|
|
|
|
|
result := got[constants.MeasTagRecommendHierarchyType.String()]
|
|
|
|
|
wantOffset := len([]rune(tt.input))
|
|
|
|
|
if result.Offset != wantOffset {
|
|
|
|
|
t.Fatalf("expected offset %d, got %d", wantOffset, result.Offset)
|
|
|
|
|
}
|
|
|
|
|
if len(result.QueryDatas) != 0 {
|
|
|
|
|
t.Fatalf("expected exact completion to return no suffix, got %v", result.QueryDatas)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNormalizeRecommendResultsUsesInputLengthForLevelContinuation(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
input string
|
|
|
|
|
recommendType constants.RecommendHierarchyType
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "token1 grid can continue",
|
|
|
|
|
input: "grid000",
|
|
|
|
|
recommendType: constants.GridRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "token1 token2 zone can continue",
|
|
|
|
|
input: "grid000.zone000",
|
|
|
|
|
recommendType: constants.ZoneRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "token1 token2 token3 station can continue",
|
|
|
|
|
input: "grid000.zone000.station000",
|
|
|
|
|
recommendType: constants.StationRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "token1 token2 token3 token4 nspath can continue",
|
|
|
|
|
input: "grid000.zone000.station000.110kV_TV-demoProject",
|
|
|
|
|
recommendType: constants.CompNSPathRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "token1 token2 token3 token4 token5 compTag can continue",
|
|
|
|
|
input: "grid000.zone000.station000.110kV_TV-demoProject.cable_26-demoProject110kV_TV",
|
|
|
|
|
recommendType: constants.CompTagRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "token1 through token6 config can continue",
|
|
|
|
|
input: "grid000.zone000.station000.110kV_TV-demoProject.cable_26-demoProject110kV_TV.base_extend",
|
|
|
|
|
recommendType: constants.ConfigRecommendHierarchyType,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
results := map[string]SearchResult{
|
|
|
|
|
tt.recommendType.String(): {
|
|
|
|
|
RecommendType: tt.recommendType,
|
|
|
|
|
QueryDatas: []string{"."},
|
|
|
|
|
IsFuzzy: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := normalizeRecommendResults(tt.input, results)
|
|
|
|
|
result := got[tt.recommendType.String()]
|
|
|
|
|
wantOffset := len([]rune(tt.input))
|
|
|
|
|
if result.Offset != wantOffset {
|
|
|
|
|
t.Fatalf("expected offset %d, got %d", wantOffset, result.Offset)
|
|
|
|
|
}
|
|
|
|
|
if len(result.QueryDatas) != 1 || result.QueryDatas[0] != "." {
|
|
|
|
|
t.Fatalf("expected level continuation '.', got %v", result.QueryDatas)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNormalizeRecommendResultsKeepsFallbackOffsetZero(t *testing.T) {
|
|
|
|
|
input := "x"
|
|
|
|
|
results := map[string]SearchResult{
|
|
|
|
|
constants.GridRecommendHierarchyType.String(): {
|
|
|
|
|
RecommendType: constants.GridRecommendHierarchyType,
|
|
|
|
|
QueryDatas: []string{"grid000", "grid001"},
|
|
|
|
|
IsFuzzy: true,
|
|
|
|
|
IsFallback: true,
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
constants.CompNSPathRecommendHierarchyType.String(): {
|
|
|
|
|
RecommendType: constants.CompNSPathRecommendHierarchyType,
|
|
|
|
|
QueryDatas: []string{"nspath000", "nspath001"},
|
|
|
|
|
IsFuzzy: true,
|
|
|
|
|
IsFallback: true,
|
|
|
|
|
Offset: 0,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := normalizeRecommendResults(input, results)
|
|
|
|
|
for key, result := range got {
|
|
|
|
|
if result.Offset != 0 {
|
|
|
|
|
t.Fatalf("expected fallback offset 0 for %s, got %d", key, result.Offset)
|
|
|
|
|
}
|
|
|
|
|
if len(result.QueryDatas) != 2 {
|
|
|
|
|
t.Fatalf("expected fallback recommends to remain intact for %s, got %v", key, result.QueryDatas)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNormalizeRecommendResultsKeepsParentPrefixOffsetForSpecificFallback(t *testing.T) {
|
|
|
|
|
nsPath := "220kV_学府路1-testProject1"
|
|
|
|
|
input := nsPath + ".x"
|
|
|
|
|
offset := recommendPrefixOffset([]string{nsPath})
|
|
|
|
|
results := map[string]SearchResult{
|
|
|
|
|
constants.CompTagRecommendHierarchyType.String(): {
|
|
|
|
|
RecommendType: constants.CompTagRecommendHierarchyType,
|
|
|
|
|
QueryDatas: []string{
|
|
|
|
|
nsPath + ".CTA-testProject1220kV_学府路1",
|
|
|
|
|
nsPath + ".CB-testProject1220kV_学府路1",
|
|
|
|
|
},
|
|
|
|
|
IsFuzzy: true,
|
|
|
|
|
IsFallback: true,
|
|
|
|
|
Offset: offset,
|
|
|
|
|
},
|
|
|
|
|
constants.MeasTagRecommendHierarchyType.String(): {
|
|
|
|
|
RecommendType: constants.MeasTagRecommendHierarchyType,
|
|
|
|
|
QueryDatas: []string{
|
|
|
|
|
nsPath + ".IA_rms_CTA-testProject1",
|
|
|
|
|
nsPath + ".IB_rms_CTA-testProject1",
|
|
|
|
|
},
|
|
|
|
|
IsFuzzy: true,
|
|
|
|
|
IsFallback: true,
|
|
|
|
|
Offset: offset,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
got := normalizeRecommendResults(input, results)
|
|
|
|
|
if offset != 24 {
|
|
|
|
|
t.Fatalf("expected sample parent prefix offset 24, got %d", offset)
|
|
|
|
|
}
|
|
|
|
|
for key, result := range got {
|
|
|
|
|
if result.Offset != 24 {
|
|
|
|
|
t.Fatalf("expected specific fallback offset 24 for %s, got %d", key, result.Offset)
|
|
|
|
|
}
|
|
|
|
|
for _, recommend := range result.QueryDatas {
|
|
|
|
|
if strings.HasPrefix(recommend, nsPath+".") {
|
|
|
|
|
t.Fatalf("expected trimmed fallback recommend for %s, got %s", key, recommend)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRecommendGroupTokens(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
inputSlice []string
|
|
|
|
|
wantCompTag string
|
|
|
|
|
wantConfig string
|
|
|
|
|
wantOK bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "full token1 to token7 input",
|
|
|
|
|
inputSlice: []string{"grid000", "zone000", "station000", "nspath", "comp_tag", "base_extend", ""},
|
|
|
|
|
wantCompTag: "comp_tag",
|
|
|
|
|
wantConfig: "base_extend",
|
|
|
|
|
wantOK: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "local token4 to token7 input",
|
|
|
|
|
inputSlice: []string{"nspath", "comp_tag", "component", ""},
|
|
|
|
|
wantCompTag: "comp_tag",
|
|
|
|
|
wantConfig: "component",
|
|
|
|
|
wantOK: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing config token",
|
|
|
|
|
inputSlice: []string{"nspath", ""},
|
|
|
|
|
wantOK: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
gotCompTag, gotConfig, gotOK := recommendGroupTokens(tt.inputSlice)
|
|
|
|
|
if gotOK != tt.wantOK {
|
|
|
|
|
t.Fatalf("expected ok %t, got %t", tt.wantOK, gotOK)
|
|
|
|
|
}
|
|
|
|
|
if gotCompTag != tt.wantCompTag || gotConfig != tt.wantConfig {
|
|
|
|
|
t.Fatalf("expected compTag/config %q/%q, got %q/%q", tt.wantCompTag, tt.wantConfig, gotCompTag, gotConfig)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 10:37:06 +08:00
|
|
|
func TestFallbackSpecificSetKey(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
hierarchy constants.RecommendHierarchyType
|
|
|
|
|
inputSlice []string
|
|
|
|
|
want string
|
|
|
|
|
wantOK bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "zone fallback uses grid specific set",
|
|
|
|
|
hierarchy: constants.ZoneRecommendHierarchyType,
|
|
|
|
|
inputSlice: []string{"grid000", "z"},
|
|
|
|
|
want: fmt.Sprintf(constants.RedisSpecGridZoneSetKey, "grid000"),
|
|
|
|
|
wantOK: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "component tag fallback uses previous nspath token",
|
|
|
|
|
hierarchy: constants.CompTagRecommendHierarchyType,
|
|
|
|
|
inputSlice: []string{"grid000", "I"},
|
|
|
|
|
want: fmt.Sprintf(constants.RedisSpecCompNSPathCompTagSetKey, "grid000"),
|
|
|
|
|
wantOK: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "measurement fallback skips config token and uses component tag",
|
|
|
|
|
hierarchy: constants.MeasTagRecommendHierarchyType,
|
|
|
|
|
inputSlice: []string{"nspath", "comp_tag", "config", "m"},
|
|
|
|
|
want: fmt.Sprintf(constants.RedisSpecCompTagMeasSetKey, "comp_tag"),
|
|
|
|
|
wantOK: true,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "config fallback has no parent specific set",
|
|
|
|
|
hierarchy: constants.ConfigRecommendHierarchyType,
|
|
|
|
|
inputSlice: []string{"nspath", "comp_tag", ""},
|
|
|
|
|
wantOK: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "missing previous token",
|
|
|
|
|
hierarchy: constants.ZoneRecommendHierarchyType,
|
|
|
|
|
inputSlice: []string{"z"},
|
|
|
|
|
wantOK: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
|
got, ok := fallbackSpecificSetKey(tt.hierarchy, tt.inputSlice)
|
|
|
|
|
if ok != tt.wantOK {
|
|
|
|
|
t.Fatalf("expected ok %t, got %t", tt.wantOK, ok)
|
|
|
|
|
}
|
|
|
|
|
if got != tt.want {
|
|
|
|
|
t.Fatalf("expected key %q, got %q", tt.want, got)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-08 17:19:07 +08:00
|
|
|
|
|
|
|
|
func TestShouldFallbackToInitialRecommend(t *testing.T) {
|
|
|
|
|
tests := []struct {
|
|
|
|
|
input string
|
|
|
|
|
want bool
|
|
|
|
|
}{
|
|
|
|
|
{input: "", want: false},
|
|
|
|
|
{input: ".", want: true},
|
|
|
|
|
{input: ".x", want: true},
|
|
|
|
|
{input: "..x", want: true},
|
|
|
|
|
{input: "...x", want: true},
|
|
|
|
|
{input: "grid000", want: false},
|
|
|
|
|
{input: "grid000.", want: false},
|
|
|
|
|
{input: "grid000.zone000", want: false},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, tt := range tests {
|
|
|
|
|
t.Run(tt.input, func(t *testing.T) {
|
|
|
|
|
got := shouldFallbackToInitialRecommend(tt.input)
|
|
|
|
|
if got != tt.want {
|
|
|
|
|
t.Fatalf("expected %t, got %t", tt.want, got)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|