summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-09-04 16:29:57 +0300
committerPaul Buetow <paul@buetow.org>2025-09-04 16:29:57 +0300
commitd99bdf981dbee7e038e4a8e262504c1a15047c38 (patch)
treeeb866c854c2c8b10bbacf53e38fdd9c7fe66d66f
parent2a6ff853c20e6c1c780c69affdadacda2db202b6 (diff)
tests: add more table-driven cases and negative provider tests; update report
-rw-r--r--REPORT.md4
-rw-r--r--internal/llm/copilot_http_test.go19
-rw-r--r--internal/lsp/build_prompts_table_test.go16
-rw-r--r--internal/lsp/compute_textedit_table_test.go33
4 files changed, 70 insertions, 2 deletions
diff --git a/REPORT.md b/REPORT.md
index 7da6b27..9c2dfa3 100644
--- a/REPORT.md
+++ b/REPORT.md
@@ -77,7 +77,7 @@ Legend: [ ] pending · [~] in progress · [x] done/partially done
- [x] Expand Copilot mocked responses: multi-choice, error object in body; assert parsing and error propagation.
6) General
-- [x] Convert repetitive tests to table-driven style where appropriate (e.g., completion prefix/strip; instruction markers; label/filter; code fences/inline spans).
+- [x] Convert repetitive tests to table-driven style where appropriate (e.g., completion prefix/strip; instruction markers; label/filter; code fences/inline spans; buildPrompts variants; computeTextEditAndFilter variants).
- [ ] Introduce a shared set of realistic mock responses (multi-line code, markdown, malformed json) and reuse across tests.
## Progress (latest)
@@ -103,7 +103,7 @@ Legend: [ ] pending · [~] in progress · [x] done/partially done
- [x] 3) lsp e2e chat/document: chat test now uses multi-line reply and validates insertion contains both lines; document resolve uses multi-line docblock.
- [x] 4) lsp completion: manual-invoke test now uses a multi-line realistic function signature with body; still passes and exercises formatting.
- [x] 5) llm providers: added OpenAI success + SSE stream and Copilot token+chat + Codex SSE tests; coverage ≥80%. Expanded with multi-choice and error-body cases.
-- [x] 6) General: introduced shared fixtures (internal/testutil) and added table-driven tests for code fences, inline spans, label selection, prefix stripping, and instruction markers. Documented patterns in docs/testing.md.
+- [x] 6) General: introduced shared fixtures (internal/testutil) and added table-driven tests for code fences, inline spans, label selection, prefix stripping, instruction markers, buildPrompts, and computeTextEditAndFilter. Documented patterns in docs/testing.md.
- [x] Added table-driven tests for instruction marker extraction and prefix stripping.
## Next actions (prioritized)
diff --git a/internal/llm/copilot_http_test.go b/internal/llm/copilot_http_test.go
index 30144d1..2e46d68 100644
--- a/internal/llm/copilot_http_test.go
+++ b/internal/llm/copilot_http_test.go
@@ -108,6 +108,25 @@ func TestCopilot_Chat_MultiChoice_And_ErrorBody(t *testing.T) {
}
}
+func TestCopilot_Chat_DecodeError_StatusOK(t *testing.T) {
+ // Chat returns 200 but invalid JSON; expect decode error
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, "{invalid")
+ }))
+ defer srv.Close()
+ c := newCopilot(srv.URL, "gpt-4o-mini", "KEY", f64p(0.1)).(copilotClient)
+ tr := rtFunc2(func(r *http.Request) (*http.Response, error) {
+ if r.URL.Host == "api.github.com" && r.URL.Path == "/copilot_internal/v2/token" {
+ rw := httptest.NewRecorder(); _ = json.NewEncoder(rw).Encode(map[string]string{"token":"tok"}); res := rw.Result(); res.StatusCode = 200; return res, nil
+ }
+ return http.DefaultTransport.RoundTrip(r)
+ })
+ c.httpClient = &http.Client{Transport: tr, Timeout: 5 * time.Second}
+ if _, err := c.Chat(context.Background(), []Message{{Role:"user", Content:"hi"}}); err == nil {
+ t.Fatalf("expected decode error for invalid body")
+ }
+}
+
func TestCopilot_CodeCompletion_MalformedAndEmpty(t *testing.T) {
c := newCopilot("https://api.githubcopilot.com", "gpt-4o-mini", "API", f64p(0.1)).(copilotClient)
tr := rtFunc2(func(r *http.Request) (*http.Response, error) {
diff --git a/internal/lsp/build_prompts_table_test.go b/internal/lsp/build_prompts_table_test.go
new file mode 100644
index 0000000..b0092e2
--- /dev/null
+++ b/internal/lsp/build_prompts_table_test.go
@@ -0,0 +1,16 @@
+package lsp
+
+import "testing"
+
+func TestBuildPrompts_Table(t *testing.T) {
+ p := CompletionParams{TextDocument: TextDocumentIdentifier{URI: "file:///x.go"}, Position: Position{Line:5, Character:7}}
+ cases := []struct{ name string; inParams bool }{
+ {"generic", false},
+ {"in_params", true},
+ }
+ for _, c := range cases {
+ sys, user := buildPrompts(c.inParams, p, "above", "current", "below", "func ctx")
+ if sys == "" || user == "" { t.Fatalf("%s: prompts empty", c.name) }
+ }
+}
+
diff --git a/internal/lsp/compute_textedit_table_test.go b/internal/lsp/compute_textedit_table_test.go
new file mode 100644
index 0000000..d82e91d
--- /dev/null
+++ b/internal/lsp/compute_textedit_table_test.go
@@ -0,0 +1,33 @@
+package lsp
+
+import "testing"
+
+func TestComputeTextEditAndFilter_Table(t *testing.T) {
+ cases := []struct{
+ name string
+ inParams bool
+ current string
+ pos Position
+ cleaned string
+ }{
+ {"ident_replace", false, "ab cd", Position{Line:1, Character:4}, "X"},
+ {"params_inside", true, "func add(a int, b string)", Position{Line:0, Character:15}, "c bool"},
+ {"params_at_close", true, "func add(a int)", Position{Line:0, Character:len("func add(a int)")}, "b string"},
+ }
+ for _, c := range cases {
+ te, filter := computeTextEditAndFilter(c.cleaned, c.inParams, c.current, CompletionParams{Position: c.pos})
+ if te == nil {
+ t.Fatalf("%s: expected edit", c.name)
+ }
+ if c.inParams && te.Range.Start.Character == 0 {
+ t.Fatalf("%s: expected param range (non-zero start)", c.name)
+ }
+ if filter == "" && c.current != "" {
+ // For ident_replace, filter may be non-empty; for params, it can be empty when replacing entire segment
+ }
+ if te.NewText != c.cleaned {
+ t.Fatalf("%s: newText got %q want %q", c.name, te.NewText, c.cleaned)
+ }
+ }
+}
+