Files
kandev/apps/backend/internal/prompts/store/sqlite_test.go
T
José Almeida b277dcdcdf fix: simplify walkthrough prompt requests (#1660)
* fix: simplify walkthrough prompt requests

* fix: address walkthrough review feedback
2026-07-10 16:04:12 +01:00

225 lines
6.1 KiB
Go

package store
import (
"context"
"path/filepath"
"strings"
"testing"
"time"
"github.com/jmoiron/sqlx"
"github.com/kandev/kandev/internal/db"
"github.com/kandev/kandev/internal/prompts/models"
)
func createTestRepo(t *testing.T) (*sqliteRepository, func()) {
t.Helper()
tmpDir := t.TempDir()
dbConn, err := db.OpenSQLite(filepath.Join(tmpDir, "test.db"))
if err != nil {
t.Fatalf("failed to open sqlite db: %v", err)
}
sqlxDB := sqlx.NewDb(dbConn, "sqlite3")
repo, err := newSQLiteRepositoryWithDB(sqlxDB, sqlxDB)
if err != nil {
t.Fatalf("failed to create repo: %v", err)
}
cleanup := func() {
if err := sqlxDB.Close(); err != nil {
t.Errorf("failed to close sqlite db: %v", err)
}
if err := repo.Close(); err != nil {
t.Errorf("failed to close repo: %v", err)
}
}
return repo, cleanup
}
func TestSQLiteRepository_CRUD(t *testing.T) {
repo, cleanup := createTestRepo(t)
defer cleanup()
ctx := context.Background()
prompt := &models.Prompt{Name: "Daily Summary", Content: "Summarize the work."}
if err := repo.CreatePrompt(ctx, prompt); err != nil {
t.Fatalf("create prompt: %v", err)
}
if prompt.ID == "" {
t.Fatalf("expected id to be set")
}
fetched, err := repo.GetPromptByID(ctx, prompt.ID)
if err != nil {
t.Fatalf("get prompt: %v", err)
}
if fetched.Name != prompt.Name {
t.Fatalf("expected name %q, got %q", prompt.Name, fetched.Name)
}
fetchedByName, err := repo.GetPromptByName(ctx, prompt.Name)
if err != nil {
t.Fatalf("get prompt by name: %v", err)
}
if fetchedByName.ID != prompt.ID {
t.Fatalf("expected prompt id %q, got %q", prompt.ID, fetchedByName.ID)
}
prompt.Name = "Standup"
prompt.Content = "What did you do yesterday?"
if err := repo.UpdatePrompt(ctx, prompt); err != nil {
t.Fatalf("update prompt: %v", err)
}
list, err := repo.ListPrompts(ctx)
if err != nil {
t.Fatalf("list prompts: %v", err)
}
// Should have 1 custom prompt + built-in prompts.
if len(list) < 1 {
t.Fatalf("expected at least 1 prompt, got %d", len(list))
}
// Find our custom prompt (built-in prompts come first due to ORDER BY)
var found bool
for _, p := range list {
if p.ID == prompt.ID && p.Name == "Standup" {
found = true
break
}
}
if !found {
t.Fatalf("expected to find updated prompt with name 'Standup'")
}
if err := repo.DeletePrompt(ctx, prompt.ID); err != nil {
t.Fatalf("delete prompt: %v", err)
}
list, err = repo.ListPrompts(ctx)
if err != nil {
t.Fatalf("list prompts after delete: %v", err)
}
// Should only have built-in prompts left.
builtinCount := 0
for _, p := range list {
if p.Builtin {
builtinCount++
}
if p.ID == prompt.ID {
t.Fatalf("expected custom prompt to be deleted, but it still exists")
}
}
if builtinCount != 5 {
t.Fatalf("expected 5 built-in prompts, got %d", builtinCount)
}
}
func TestSQLiteRepository_BuiltinPrompts(t *testing.T) {
repo, cleanup := createTestRepo(t)
defer cleanup()
ctx := context.Background()
// List prompts should include built-in prompts
list, err := repo.ListPrompts(ctx)
if err != nil {
t.Fatalf("list prompts: %v", err)
}
// Should include the CI auto-fix and changes walkthrough built-in prompts.
builtinCount := 0
var ciAutoFixContent string
var changesWalkthroughContent string
for _, p := range list {
if p.Builtin {
builtinCount++
}
if p.ID == "builtin-ci-auto-fix" && p.Name == "ci-auto-fix" && p.Content != "" {
ciAutoFixContent = p.Content
}
if p.ID == "builtin-changes-walkthrough" && p.Name == "changes-walkthrough" && p.Content != "" {
changesWalkthroughContent = p.Content
}
}
if builtinCount != 5 {
t.Fatalf("expected 5 built-in prompts, got %d", builtinCount)
}
if ciAutoFixContent == "" {
t.Fatalf("expected ci-auto-fix built-in prompt")
}
for _, want := range []string{
"If the new feedback is not actionable",
"do not modify files",
"do not commit",
"do not push",
"nothing actionable to address",
"{{pr.feedback}}",
"resolve the addressed PR review threads",
} {
if !strings.Contains(ciAutoFixContent, want) {
t.Fatalf("expected ci-auto-fix prompt to contain %q, got:\n%s", want, ciAutoFixContent)
}
}
if changesWalkthroughContent == "" {
t.Fatalf("expected changes-walkthrough built-in prompt")
}
for _, want := range []string{
"show_walkthrough_kandev",
"Inspect the changed files yourself",
"compare the PR head against the PR base branch",
"compare against the task/repository base",
"Use `line_end`",
"do not assume the PR head is checked out locally",
"first walkthrough step",
"ELI5:",
} {
if !strings.Contains(changesWalkthroughContent, want) {
t.Fatalf("expected changes-walkthrough prompt to contain %q, got:\n%s", want, changesWalkthroughContent)
}
}
}
func TestSQLiteRepository_BuiltinSeedIgnoresUserPromptNameConflict(t *testing.T) {
tmpDir := t.TempDir()
dbConn, err := db.OpenSQLite(filepath.Join(tmpDir, "test.db"))
if err != nil {
t.Fatalf("failed to open sqlite db: %v", err)
}
sqlxDB := sqlx.NewDb(dbConn, "sqlite3")
defer func() {
if err := sqlxDB.Close(); err != nil {
t.Errorf("failed to close sqlite db: %v", err)
}
}()
if _, err := sqlxDB.Exec(`
CREATE TABLE custom_prompts (
id TEXT PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
content TEXT NOT NULL,
builtin INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
`); err != nil {
t.Fatalf("create schema: %v", err)
}
now := time.Now().UTC()
if _, err := sqlxDB.Exec(
`INSERT INTO custom_prompts (id, name, content, builtin, created_at, updated_at) VALUES (?, ?, ?, 0, ?, ?)`,
"user-walkthrough", "changes-walkthrough", "user-owned prompt", now, now,
); err != nil {
t.Fatalf("seed user prompt: %v", err)
}
repo, err := newSQLiteRepositoryWithDB(sqlxDB, sqlxDB)
if err != nil {
t.Fatalf("repo init should ignore built-in name conflicts: %v", err)
}
got, err := repo.GetPromptByName(context.Background(), "changes-walkthrough")
if err != nil {
t.Fatalf("get existing prompt: %v", err)
}
if got.ID != "user-walkthrough" || got.Builtin {
t.Fatalf("expected user prompt to remain canonical, got %+v", got)
}
}