diff --git a/agent/tools_test.go b/agent/tools_test.go new file mode 100644 index 0000000..f571644 --- /dev/null +++ b/agent/tools_test.go @@ -0,0 +1,196 @@ +package agent + +import ( + "os" + "path/filepath" + "testing" +) + +// Hilfsfunktion: temporäres Arbeitsverzeichnis anlegen +func setupWorkDir(t *testing.T) string { + t.Helper() + dir, err := os.MkdirTemp("", "agent-tools-test-*") + if err != nil { + t.Fatalf("Konnte temp dir nicht anlegen: %v", err) + } + t.Cleanup(func() { os.RemoveAll(dir) }) // wird nach jedem Test aufgeräumt + return dir +} + +// ─── WRITE_FILE ────────────────────────────────────────── + +func TestWriteFile_CreatesFile(t *testing.T) { + dir := setupWorkDir(t) + + toolCall := "TOOL:WRITE_FILE:hello.go:package main\n\nfunc main() {}" + output := executeTool(toolCall, dir) + + if output != "WRITE_FILE OK: hello.go geschrieben" { + t.Errorf("Unerwarteter Output: %q", output) + } + + // Datei muss wirklich existieren + path := filepath.Join(dir, "hello.go") + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Error("Datei wurde nicht angelegt") + } +} + +func TestWriteFile_CorrectContent(t *testing.T) { + dir := setupWorkDir(t) + expected := "package main\n\nfunc main() {}" + + executeTool("TOOL:WRITE_FILE:hello.go:"+expected, dir) + + content, err := os.ReadFile(filepath.Join(dir, "hello.go")) + if err != nil { + t.Fatalf("Datei konnte nicht gelesen werden: %v", err) + } + if string(content) != expected { + t.Errorf("Inhalt falsch\n erwartet: %q\n bekommen: %q", expected, string(content)) + } +} + +func TestWriteFile_CreatesSubdirectory(t *testing.T) { + dir := setupWorkDir(t) + + executeTool("TOOL:WRITE_FILE:subdir/nested/file.go:package sub", dir) + + path := filepath.Join(dir, "subdir", "nested", "file.go") + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Error("Verschachtelte Datei wurde nicht angelegt") + } +} + +func TestWriteFile_MissingContent_ReturnsError(t *testing.T) { + dir := setupWorkDir(t) + + output := executeTool("TOOL:WRITE_FILE:hello.go", dir) + + if output != "ERROR: WRITE_FILE braucht Inhalt" { + t.Errorf("Erwartete Fehlermeldung, bekam: %q", output) + } +} + +// ─── READ_FILE ─────────────────────────────────────────── + +func TestReadFile_ReadsExistingFile(t *testing.T) { + dir := setupWorkDir(t) + expected := "Hello, World!" + + // Datei vorbereiten + os.WriteFile(filepath.Join(dir, "test.txt"), []byte(expected), 0644) + + output := executeTool("TOOL:READ_FILE:test.txt", dir) + + if output != "READ_FILE test.txt:\n"+expected { + t.Errorf("Unerwarteter Output: %q", output) + } +} + +func TestReadFile_NonExistentFile_ReturnsError(t *testing.T) { + dir := setupWorkDir(t) + + output := executeTool("TOOL:READ_FILE:gibts-nicht.txt", dir) + + if output == "" || output[:15] != "READ_FILE ERROR" { + t.Errorf("Erwartete Fehlermeldung, bekam: %q", output) + } +} + +// ─── LIST_FILES ────────────────────────────────────────── + +func TestListFiles_ReturnsFileNames(t *testing.T) { + dir := setupWorkDir(t) + + // Testdateien anlegen + os.WriteFile(filepath.Join(dir, "a.go"), []byte(""), 0644) + os.WriteFile(filepath.Join(dir, "b.go"), []byte(""), 0644) + + output := executeTool("TOOL:LIST_FILES:.", dir) + + if !contains(output, "a.go") || !contains(output, "b.go") { + t.Errorf("Erwartete beide Dateien in Output: %q", output) + } +} + +func TestListFiles_NonExistentDir_ReturnsError(t *testing.T) { + dir := setupWorkDir(t) + + output := executeTool("TOOL:LIST_FILES:gibts-nicht", dir) + + if output[:16] != "LIST_FILES ERROR" { + t.Errorf("Erwartete Fehlermeldung, bekam: %q", output) + } +} + +// ─── UNGÜLTIGE TOOL-CALLS ──────────────────────────────── + +func TestExecuteTool_UnknownTool_ReturnsError(t *testing.T) { + dir := setupWorkDir(t) + + output := executeTool("TOOL:UNKNOWN_TOOL:arg", dir) + + if output[:5] != "ERROR" { + t.Errorf("Erwartete Fehlermeldung, bekam: %q", output) + } +} + +func TestExecuteTool_InvalidFormat_ReturnsError(t *testing.T) { + dir := setupWorkDir(t) + + output := executeTool("TOOL:", dir) + + if output[:5] != "ERROR" { + t.Errorf("Erwartete Fehlermeldung, bekam: %q", output) + } +} + +// ─── EXECUTE_TOOLS (Top-Level) ─────────────────────────── + +func TestExecuteTools_ParsesMultipleToolCalls(t *testing.T) { + dir := setupWorkDir(t) + + response := `Ich schreibe zwei Dateien: +TOOL:WRITE_FILE:foo.go:package foo +TOOL:WRITE_FILE:bar.go:package bar` + + _, hadTools := ExecuteTools(response, dir) + + if !hadTools { + t.Error("Hätte Tool-Calls erkennen sollen") + } + + // Beide Dateien müssen existieren + for _, name := range []string{"foo.go", "bar.go"} { + if _, err := os.Stat(filepath.Join(dir, name)); os.IsNotExist(err) { + t.Errorf("Datei %s wurde nicht angelegt", name) + } + } +} + +func TestExecuteTools_NoToolCalls_ReturnsFalse(t *testing.T) { + dir := setupWorkDir(t) + + _, hadTools := ExecuteTools("Keine Tools hier, nur Text.", dir) + + if hadTools { + t.Error("Hätte keine Tool-Calls erkennen sollen") + } +} + +// ─── Hilfsfunktion ─────────────────────────────────────── + +func contains(s, substr string) bool { + return len(s) >= len(substr) && (s == substr || + len(s) > 0 && containsHelper(s, substr)) +} + +func containsHelper(s, substr string) bool { + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +}