package intent import ( "errors" "testing" "testing/fstest" ) func mapFS(files map[string]string) fstest.MapFS { m := make(fstest.MapFS, len(files)) for k, v := range files { m[k] = &fstest.MapFile{Data: []byte(v)} } return m } const overlayAddJSON = `{ "intents": [ {"id": "demo.misc.added", "domain": "demo", "version": 1, "matcher": {"includes_any": ["新增"]}} ] }` func TestLoadOverlaysOrderAndSources(t *testing.T) { fsys := mapFS(map[string]string{ "specs/a.json": overlayAddJSON, "specs/b.json": `{"disable": ["demo.misc.added"]}`, }) entries, err := loadOverlays(fsys, []string{"specs"}) if err != nil { t.Fatalf("load: %v", err) } if len(entries) != 2 { t.Fatalf("expected 2 entries, got %d", len(entries)) } if entries[0].spec.ID != "demo.misc.added" || entries[0].source != "specs/a.json#intents[0]" { t.Fatalf("entry0 mismatch: %+v", entries[0]) } if !entries[1].disable || entries[1].id != "demo.misc.added" || entries[1].source != "specs/b.json#disable[0]" { t.Fatalf("entry1 mismatch: %+v", entries[1]) } } func TestLoadOverlaysFileOrder(t *testing.T) { // 同目录按文件名字典序:a.json 先应用,b.json 后应用。 fsys := mapFS(map[string]string{ "specs/a.json": `{"intents": [{"id": "demo.x.one", "domain": "demo", "version": 1, "matcher": {"includes_any": ["a"]}}]}`, "specs/b.json": `{"intents": [{"id": "demo.x.one", "domain": "demo", "version": 2, "matcher": {"includes_any": ["b"]}}]}`, }) entries, err := loadOverlays(fsys, []string{"specs"}) if err != nil { t.Fatalf("load: %v", err) } if len(entries) != 2 || entries[0].spec.Version != 1 || entries[1].spec.Version != 2 { t.Fatalf("file order not applied: %+v", entries) } } func TestLoadOverlaysUnknownField(t *testing.T) { fsys := mapFS(map[string]string{ "specs/bad.json": `{"intents": [{"id": "demo.x.one", "domain": "demo", "version": 1, "matcher": {"includes_any": ["a"]}, "unknown_key": 1}]}`, }) _, err := loadOverlays(fsys, []string{"specs"}) var e *Error if !errors.As(err, &e) || e.Code != InvalidSpec { t.Fatalf("expected intent.invalid_spec, got %v", err) } if e.Fields["file"] != "specs/bad.json" { t.Fatalf("error missing file source: %v", e.Fields) } } func TestLoadOverlaysBadJSON(t *testing.T) { fsys := mapFS(map[string]string{"specs/broken.json": `{"intents": [}`}) _, err := loadOverlays(fsys, []string{"specs"}) var e *Error if !errors.As(err, &e) || e.Code != InvalidSpec { t.Fatalf("expected intent.invalid_spec for broken json, got %v", err) } } func TestLoadOverlaysMissingRoot(t *testing.T) { fsys := mapFS(map[string]string{}) _, err := loadOverlays(fsys, []string{"specs"}) var e *Error if !errors.As(err, &e) || e.Code != LoadFailed { t.Fatalf("expected intent.load_failed, got %v", err) } if e.Fields["root"] != "specs" { t.Fatalf("error missing root: %v", e.Fields) } } func TestLoadOverlaysSingleFileRoot(t *testing.T) { fsys := mapFS(map[string]string{"single.json": overlayAddJSON}) entries, err := loadOverlays(fsys, []string{"single.json"}) if err != nil { t.Fatalf("load: %v", err) } if len(entries) != 1 || entries[0].source != "single.json#intents[0]" { t.Fatalf("single file root mismatch: %+v", entries) } }