summaryrefslogtreecommitdiff
path: root/internal/hexaiaction/parse_test.go
blob: ba5cd9671148a6d05e16287a69ade99dcc8a582f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package hexaiaction

import (
	"context"
	"strings"
	"testing"

	"codeberg.org/snonux/hexai/internal/appconfig"
	"codeberg.org/snonux/hexai/internal/llm"
)

func TestParseInput_NoDiagnostics(t *testing.T) {
	in := "some code here"
	parts, err := ParseInput(strings.NewReader(in))
	if err != nil {
		t.Fatalf("unexpected err: %v", err)
	}
	if parts.Selection != in || len(parts.Diagnostics) != 0 {
		t.Fatalf("unexpected parse: %#v", parts)
	}
}

func TestParseInput_WithDiagnostics(t *testing.T) {
	in := "Diagnostics:\nmissing return\nuse of undefined: foo\n\nfunc a() {}"
	parts, err := ParseInput(strings.NewReader(in))
	if err != nil {
		t.Fatalf("unexpected err: %v", err)
	}
	if parts.Selection != "func a() {}" {
		t.Fatalf("selection wrong: %q", parts.Selection)
	}
	if len(parts.Diagnostics) != 2 || parts.Diagnostics[0] != "missing return" {
		t.Fatalf("diags wrong: %#v", parts.Diagnostics)
	}
}

func TestExtractInstruction_Variants(t *testing.T) {
	cases := []struct{ in, wantInstr string }{
		{";rewrite to X;\ncode", "rewrite to X"},
		{"/* fix it */\ncode", "fix it"},
		{"<!-- doc me -->\ncode", "doc me"},
		{"// change it\ncode", "change it"},
		{"# tweak\ncode", "tweak"},
		{"-- fix\ncode", "fix"},
	}
	for _, c := range cases {
		got, cleaned := ExtractInstruction(c.in)
		if got != c.wantInstr {
			t.Fatalf("instr mismatch: %q != %q", got, c.wantInstr)
		}
		if strings.Contains(cleaned, c.wantInstr) && strings.Contains(c.in, c.wantInstr) {
			t.Fatalf("expected instruction removed from selection: %q", cleaned)
		}
	}
}

func TestRenderAndStrip(t *testing.T) {
	tpl := "Hello, {{name}}"
	out := Render(tpl, map[string]string{"name": "Hex"})
	if out != "Hello, Hex" {
		t.Fatalf("unexpected render: %q", out)
	}
	fenced := "```go\npackage x\n```"
	if StripFences(fenced) != "package x" {
		t.Fatalf("unexpected strip")
	}
}

type fakeClient struct {
	last []llm.Message
	out  string
	err  error
}

func (f *fakeClient) Chat(_ context.Context, msgs []llm.Message, _ ...llm.RequestOption) (string, error) {
	f.last = msgs
	return f.out, f.err
}

func (f *fakeClient) DefaultModel() string { return "m" }

func TestRuners_Prompts(t *testing.T) {
	cfg := appconfig.App{
		PromptCodeActionRewriteSystem:     "SYS-R",
		PromptCodeActionRewriteUser:       "R {{instruction}} :: {{selection}}",
		PromptCodeActionDiagnosticsSystem: "SYS-D",
		PromptCodeActionDiagnosticsUser:   "D {{diagnostics}} :: {{selection}}",
		PromptCodeActionDocumentSystem:    "SYS-C",
		PromptCodeActionDocumentUser:      "C {{selection}}",
		PromptCodeActionGoTestSystem:      "SYS-T",
		PromptCodeActionGoTestUser:        "T {{function}}",
	}
	f := &fakeClient{out: "```\nDONE\n```"}
	ctx := context.Background()
	// rewrite
	if out, err := runRewrite(ctx, cfg, f, "instr", "sel"); err != nil || out != "DONE" {
		t.Fatalf("rewrite failed: %q %v", out, err)
	}
	if len(f.last) != 2 || f.last[0].Content != "SYS-R" || !strings.Contains(f.last[1].Content, "instr") {
		t.Fatalf("rewrite prompts wrong: %#v", f.last)
	}
	// diagnostics
	if out, err := runDiagnostics(ctx, cfg, f, []string{"a", "b"}, "sel"); err != nil || out != "DONE" {
		t.Fatalf("diagnostics failed: %q %v", out, err)
	}
	if f.last[0].Content != "SYS-D" || !strings.Contains(f.last[1].Content, "a\nb") {
		t.Fatalf("diagnostics prompts wrong: %#v", f.last)
	}
	// document
	if out, err := runDocument(ctx, cfg, f, "sel"); err != nil || out != "DONE" {
		t.Fatalf("document failed: %q %v", out, err)
	}
	if f.last[0].Content != "SYS-C" || !strings.Contains(f.last[1].Content, "sel") {
		t.Fatalf("document prompts wrong: %#v", f.last)
	}
	// gotest
	if out, err := runGoTest(ctx, cfg, f, "func A(){}"); err != nil || out != "DONE" {
		t.Fatalf("gotest failed: %q %v", out, err)
	}
	if f.last[0].Content != "SYS-T" || !strings.Contains(f.last[1].Content, "func A(){") {
		t.Fatalf("gotest prompts wrong: %#v", f.last)
	}
}