| 1 | package outputstyle |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | "path/filepath" |
| 6 | "strings" |
| 7 | "testing" |
| 8 | |
| 9 | fileencoding "reasonix/internal/fileutil/encoding" |
| 10 | ) |
| 11 | |
| 12 | func TestResolveBuiltin(t *testing.T) { |
| 13 | st, ok := Resolve("explanatory", nil) |
| 14 | if !ok { |
| 15 | t.Fatal("explanatory built-in should resolve") |
| 16 | } |
| 17 | if !st.Builtin || !st.KeepCoding || strings.TrimSpace(st.Body) == "" { |
| 18 | t.Errorf("unexpected built-in shape: %+v", st) |
| 19 | } |
| 20 | // Case-insensitive. |
| 21 | if _, ok := Resolve("LEARNING", nil); !ok { |
| 22 | t.Error("resolve should be case-insensitive") |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | func TestResolveDefaultIsNone(t *testing.T) { |
| 27 | for _, name := range []string{"", " ", "default"} { |
| 28 | if _, ok := Resolve(name, nil); ok { |
| 29 | t.Errorf("Resolve(%q) should be no-style", name) |
| 30 | } |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | func TestApply(t *testing.T) { |
| 35 | append1 := Apply("BASE", OutputStyle{Body: "X", KeepCoding: true}) |
| 36 | if append1 != "BASE\n\nX" { |
| 37 | t.Errorf("keep-coding append = %q", append1) |
| 38 | } |
| 39 | replace := Apply("BASE", OutputStyle{Body: "X", KeepCoding: false}) |
| 40 | if replace != "X" { |
| 41 | t.Errorf("replace = %q, want X", replace) |
| 42 | } |
| 43 | if got := Apply("BASE", OutputStyle{Body: " "}); got != "BASE" { |
| 44 | t.Errorf("empty body should leave base untouched, got %q", got) |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | func TestListIncludesBuiltinsSorted(t *testing.T) { |
| 49 | got := List(nil) |
| 50 | if len(got) < 3 { |
| 51 | t.Fatalf("expected at least the 3 built-ins, got %d", len(got)) |
| 52 | } |
| 53 | for i := 1; i < len(got); i++ { |
| 54 | if got[i-1].Name > got[i].Name { |
| 55 | t.Errorf("not sorted by name: %q before %q", got[i-1].Name, got[i].Name) |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | func TestCustomFileOverridesBuiltinAndParses(t *testing.T) { |
| 61 | dir := t.TempDir() |
| 62 | // Override the built-in "explanatory" with a custom replace-style file. |
| 63 | md := "---\ndescription: my persona\nkeep-coding-instructions: false\n---\nYou are a pirate. Answer in pirate speak.\n" |
| 64 | if err := os.WriteFile(filepath.Join(dir, "explanatory.md"), []byte(md), 0o644); err != nil { |
| 65 | t.Fatal(err) |
| 66 | } |
| 67 | |
| 68 | st, ok := Resolve("explanatory", []string{dir}) |
| 69 | if !ok { |
| 70 | t.Fatal("custom explanatory should resolve") |
| 71 | } |
| 72 | if st.Builtin { |
| 73 | t.Error("custom file should override the built-in (Builtin=false)") |
| 74 | } |
| 75 | if st.KeepCoding { |
| 76 | t.Error("keep-coding-instructions: false should disable KeepCoding") |
| 77 | } |
| 78 | if st.Description != "my persona" || !strings.Contains(st.Body, "pirate") { |
| 79 | t.Errorf("frontmatter/body not parsed: %+v", st) |
| 80 | } |
| 81 | if got := Apply("CODING PROMPT", st); got != st.Body { |
| 82 | t.Errorf("a replace-style should drop the base prompt, got %q", got) |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | func TestResolveDecodesGB18030CustomFile(t *testing.T) { |
| 87 | dir := t.TempDir() |
| 88 | body := "---\nname: concise-cn\ndescription: 中文风格\n---\n请用中文简洁回答。" |
| 89 | if err := os.WriteFile(filepath.Join(dir, "concise-cn.md"), fileencoding.Encode(body, fileencoding.GB18030), 0o644); err != nil { |
| 90 | t.Fatal(err) |
| 91 | } |
| 92 | |
| 93 | st, ok := Resolve("concise-cn", []string{dir}) |
| 94 | if !ok { |
| 95 | t.Fatal("custom style should resolve") |
| 96 | } |
| 97 | if st.Description != "中文风格" || st.Body != "请用中文简洁回答。" { |
| 98 | t.Fatalf("decoded style = %+v", st) |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | func TestParseFileNameFromFilename(t *testing.T) { |
| 103 | dir := t.TempDir() |
| 104 | if err := os.WriteFile(filepath.Join(dir, "snappy.md"), []byte("Be snappy."), 0o644); err != nil { |
| 105 | t.Fatal(err) |
| 106 | } |
| 107 | st, ok := Resolve("snappy", []string{dir}) |
| 108 | if !ok || st.Name != "snappy" || !st.KeepCoding { // default keep-coding when unspecified |
| 109 | t.Errorf("filename-derived style wrong: %+v ok=%v", st, ok) |
| 110 | } |
| 111 | } |
| 112 |