test: add unit tests for chat history, edit helper, and code cleaning
- Introduce tests for chat history file handling, loading/saving, and error cases in cmd/chat_test.go - Add tests for removeLastModifiedComments in cmd/edit_helper_test.go - Add comprehensive tests for CleanCodeResponse in internal/grok/cleancode_test.go - Update Makefile to centralize build artifacts in build/ directory for coverage reports and binary - Adjust .gitignore to ignore chat_history.json and remove obsolete coverage file entries
This commit is contained in:
parent
8b6449c947
commit
13519438a2
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,6 +5,4 @@ grokkit
|
|||||||
*.log
|
*.log
|
||||||
*.tmp
|
*.tmp
|
||||||
.env
|
.env
|
||||||
coverage.out
|
|
||||||
coverage.html
|
|
||||||
chat_history.json
|
chat_history.json
|
||||||
10
Makefile
10
Makefile
@ -4,14 +4,16 @@ test:
|
|||||||
go test ./... -v
|
go test ./... -v
|
||||||
|
|
||||||
test-cover:
|
test-cover:
|
||||||
go test ./... -coverprofile=coverage.out
|
@mkdir -p build
|
||||||
go tool cover -html=coverage.out -o coverage.html
|
go test ./... -coverprofile=build/coverage.out
|
||||||
@echo "✅ Coverage report: open coverage.html in your browser"
|
go tool cover -html=build/coverage.out -o build/coverage.html
|
||||||
|
@echo "✅ Coverage report: open build/coverage.html in your browser"
|
||||||
|
|
||||||
test-agent:
|
test-agent:
|
||||||
go test -run TestAgent ./... -v
|
go test -run TestAgent ./... -v
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
@mkdir -p build
|
||||||
go build -ldflags "-s -w" -trimpath -o build/grokkit .
|
go build -ldflags "-s -w" -trimpath -o build/grokkit .
|
||||||
|
|
||||||
install: build
|
install: build
|
||||||
@ -21,7 +23,7 @@ install: build
|
|||||||
@echo "✅ grokkit installed to ~/.local/bin"
|
@echo "✅ grokkit installed to ~/.local/bin"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build/ coverage.out coverage.html
|
rm -rf build/
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "Available targets:"
|
@echo "Available targets:"
|
||||||
|
|||||||
93
cmd/chat_test.go
Normal file
93
cmd/chat_test.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetChatHistoryFile(t *testing.T) {
|
||||||
|
// Save original HOME
|
||||||
|
oldHome := os.Getenv("HOME")
|
||||||
|
defer os.Setenv("HOME", oldHome)
|
||||||
|
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
os.Setenv("HOME", tmpDir)
|
||||||
|
|
||||||
|
histFile := getChatHistoryFile()
|
||||||
|
expected := filepath.Join(tmpDir, ".config", "grokkit", "chat_history.json")
|
||||||
|
|
||||||
|
if histFile != expected {
|
||||||
|
t.Errorf("getChatHistoryFile() = %q, want %q", histFile, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadChatHistory_NoFile(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
oldHome := os.Getenv("HOME")
|
||||||
|
os.Setenv("HOME", tmpDir)
|
||||||
|
defer os.Setenv("HOME", oldHome)
|
||||||
|
|
||||||
|
history := loadChatHistory()
|
||||||
|
if history != nil {
|
||||||
|
t.Errorf("loadChatHistory() expected nil for non-existent file, got %v", history)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSaveAndLoadChatHistory(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
oldHome := os.Getenv("HOME")
|
||||||
|
os.Setenv("HOME", tmpDir)
|
||||||
|
defer os.Setenv("HOME", oldHome)
|
||||||
|
|
||||||
|
// Create test messages
|
||||||
|
messages := []map[string]string{
|
||||||
|
{"role": "system", "content": "test system message"},
|
||||||
|
{"role": "user", "content": "hello"},
|
||||||
|
{"role": "assistant", "content": "hi there"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
err := saveChatHistory(messages)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("saveChatHistory() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load
|
||||||
|
loaded := loadChatHistory()
|
||||||
|
if loaded == nil {
|
||||||
|
t.Fatal("loadChatHistory() returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(loaded) != len(messages) {
|
||||||
|
t.Errorf("loadChatHistory() length = %d, want %d", len(loaded), len(messages))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify content
|
||||||
|
for i, msg := range loaded {
|
||||||
|
if msg["role"] != messages[i]["role"] {
|
||||||
|
t.Errorf("Message[%d] role = %q, want %q", i, msg["role"], messages[i]["role"])
|
||||||
|
}
|
||||||
|
if msg["content"] != messages[i]["content"] {
|
||||||
|
t.Errorf("Message[%d] content = %q, want %q", i, msg["content"], messages[i]["content"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadChatHistory_InvalidJSON(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
oldHome := os.Getenv("HOME")
|
||||||
|
os.Setenv("HOME", tmpDir)
|
||||||
|
defer os.Setenv("HOME", oldHome)
|
||||||
|
|
||||||
|
// Create invalid JSON file
|
||||||
|
histDir := filepath.Join(tmpDir, ".config", "grokkit")
|
||||||
|
os.MkdirAll(histDir, 0755)
|
||||||
|
histFile := filepath.Join(histDir, "chat_history.json")
|
||||||
|
os.WriteFile(histFile, []byte("invalid json{{{"), 0644)
|
||||||
|
|
||||||
|
history := loadChatHistory()
|
||||||
|
if history != nil {
|
||||||
|
t.Errorf("loadChatHistory() expected nil for invalid JSON, got %v", history)
|
||||||
|
}
|
||||||
|
}
|
||||||
53
cmd/edit_helper_test.go
Normal file
53
cmd/edit_helper_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRemoveLastModifiedComments(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "removes last modified comment",
|
||||||
|
input: "// Last modified: 2024-01-01\npackage main\n\nfunc main() {}",
|
||||||
|
expected: "package main\n\nfunc main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "removes multiple last modified comments",
|
||||||
|
input: "// Last modified: 2024-01-01\npackage main\n// Last modified by: user\nfunc main() {}",
|
||||||
|
expected: "package main\nfunc main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves code without last modified",
|
||||||
|
input: "package main\n\nfunc main() {}",
|
||||||
|
expected: "package main\n\nfunc main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles empty string",
|
||||||
|
input: "",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves other comments",
|
||||||
|
input: "// This is a regular comment\npackage main\n// Last modified: 2024\n// Another comment\nfunc main() {}",
|
||||||
|
expected: "// This is a regular comment\npackage main\n// Another comment\nfunc main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles line with only last modified",
|
||||||
|
input: "Last modified: 2024-01-01",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := removeLastModifiedComments(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("removeLastModifiedComments() = %q, want %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
86
internal/grok/cleancode_test.go
Normal file
86
internal/grok/cleancode_test.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package grok
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCleanCodeResponse_Comprehensive(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "removes go markdown fences",
|
||||||
|
input: "```go\npackage main\n\nfunc main() {}\n```",
|
||||||
|
expected: "package main\n\nfunc main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "removes python markdown fences",
|
||||||
|
input: "```python\ndef hello():\n print('hello')\n```",
|
||||||
|
expected: "def hello():\n print('hello')",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "removes plain markdown fences",
|
||||||
|
input: "```\nsome code\n```",
|
||||||
|
expected: "some code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles no fences",
|
||||||
|
input: "func main() {}",
|
||||||
|
expected: "func main() {}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves internal blank lines",
|
||||||
|
input: "```\nline1\n\nline2\n\nline3\n```",
|
||||||
|
expected: "line1\n\nline2\n\nline3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trims leading whitespace",
|
||||||
|
input: " \n\n```\ncode\n```",
|
||||||
|
expected: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trims trailing whitespace",
|
||||||
|
input: "```\ncode\n```\n\n ",
|
||||||
|
expected: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles multiple languages",
|
||||||
|
input: "```javascript\nconst x = 1;\n```",
|
||||||
|
expected: "const x = 1;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles fences with extra spaces",
|
||||||
|
input: "``` \ncode\n``` ",
|
||||||
|
expected: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "removes only fence lines",
|
||||||
|
input: "text before\n```go\ncode\n```\ntext after",
|
||||||
|
expected: "text before\n\ncode\n\ntext after",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles empty input",
|
||||||
|
input: "",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "handles only fences",
|
||||||
|
input: "```\n```",
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserves code indentation",
|
||||||
|
input: "```\nfunc main() {\n fmt.Println(\"hello\")\n}\n```",
|
||||||
|
expected: "func main() {\n fmt.Println(\"hello\")\n}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := CleanCodeResponse(tt.input)
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("CleanCodeResponse():\ngot: %q\nwant: %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user