diff --git a/internal/acp/acp_extra_test.go b/internal/acp/acp_extra_test.go index 6e68264f..7486147f 100644 --- a/internal/acp/acp_extra_test.go +++ b/internal/acp/acp_extra_test.go @@ -1,12 +1,14 @@ package acp import ( + "bufio" "bytes" "context" "encoding/json" "errors" "io" "strings" + "sync" "testing" "time" @@ -374,28 +376,57 @@ func TestACP_FullSessionLifecycle(t *testing.T) { } func TestACP_MultipleSessions(t *testing.T) { - lines := []string{ - `{"jsonrpc":"2.0","id":1,"method":"initialize"}`, - `{"jsonrpc":"2.0","id":2,"method":"session/new"}`, - `{"jsonrpc":"2.0","id":3,"method":"session/new"}`, - `{"jsonrpc":"2.0","id":4,"method":"session/prompt","params":{"sessionId":"sess_1","prompt":[{"type":"text","text":"hello"}]}}`, - `{"jsonrpc":"2.0","id":5,"method":"session/prompt","params":{"sessionId":"sess_2","prompt":[{"type":"text","text":"world"}]}}`, + // Create a single server for both sessions + srv := NewServer(testFactory) + var buf bytes.Buffer + srv.w = &buf + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + in := strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"initialize"} +{"jsonrpc":"2.0","id":2,"method":"session/new"} +{"jsonrpc":"2.0","id":3,"method":"session/new"} +`) + pr, pw := io.Pipe() + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + _ = srv.Serve(ctx, in, pw) + _ = pw.Close() + }() + + var msgs []rpcMessage + scanner := bufio.NewScanner(pr) + scanner.Buffer(make([]byte, 0, 1024*1024), 1024*1024) + for scanner.Scan() { + line := scanner.Bytes() + if len(line) == 0 { + continue + } + var m rpcMessage + if err := json.Unmarshal(line, &m); err == nil { + msgs = append(msgs, m) + } } - msgs := runServer(t, testFactory, lines) + wg.Wait() - sessionCount := 0 + sessionIDs := make(map[string]bool) for _, m := range msgs { if hasID(m, 2) || hasID(m, 3) { var r struct { SessionID string `json:"sessionId"` } if err := json.Unmarshal(m.Result, &r); err == nil && r.SessionID != "" { - sessionCount++ + sessionIDs[r.SessionID] = true } } } - if sessionCount != 2 { - t.Errorf("expected 2 sessions created, got %d", sessionCount) + + if len(sessionIDs) != 2 { + t.Errorf("expected 2 unique session IDs, got %d", len(sessionIDs)) } } diff --git a/internal/crash/crash_extra_test.go b/internal/crash/crash_extra_test.go index d2b0cdc9..23ecf882 100644 --- a/internal/crash/crash_extra_test.go +++ b/internal/crash/crash_extra_test.go @@ -104,11 +104,11 @@ func TestWriteReport_CreatesDirectory(t *testing.T) { func TestFormatReport_FullFields(t *testing.T) { r := CrashReport{ - Timestamp: mustParseTime(t, "2024-01-15T10:30:00Z"), - Version: "1.2.3", - PanicValue: "runtime error: nil pointer", - Signal: "SIGSEGV", - Stack: "goroutine 1 [running]:\nmain.main()", + Timestamp: mustParseTime(t, "2024-01-15T10:30:00Z"), + Version: "1.2.3", + PanicValue: "runtime error: nil pointer", + Signal: "SIGSEGV", + Stack: "goroutine 1 [running]:\nmain.main()", } result := formatReport(r) diff --git a/internal/lint/lint_extra_test.go b/internal/lint/lint_extra_test.go index 2d40be76..72a498c3 100644 --- a/internal/lint/lint_extra_test.go +++ b/internal/lint/lint_extra_test.go @@ -21,7 +21,7 @@ func TestEslintLinter_NoTool(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "test.js") - if err := os.WriteFile(file, []byte("const x = 1;"), 0644); err != nil { + if err := os.WriteFile(file, []byte("const x = 1;"), 0o644); err != nil { t.Fatal(err) } @@ -48,7 +48,7 @@ func TestRuffLinter_NoTool(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "test.py") - if err := os.WriteFile(file, []byte("x = 1\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("x = 1\n"), 0o644); err != nil { t.Fatal(err) } @@ -75,7 +75,7 @@ func TestGoLinter_NoTool(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "test.go") - if err := os.WriteFile(file, []byte("package main\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("package main\n"), 0o644); err != nil { t.Fatal(err) } @@ -90,7 +90,7 @@ func TestGoLinter_NoTool(t *testing.T) { func TestCustomLinter_Success(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "x.go") - if err := os.WriteFile(file, []byte("package x\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("package x\n"), 0o644); err != nil { t.Fatal(err) } @@ -108,7 +108,7 @@ func TestCustomLinter_Success(t *testing.T) { func TestCustomLinter_EmptyOutput(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "x.go") - if err := os.WriteFile(file, []byte("package x\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("package x\n"), 0o644); err != nil { t.Fatal(err) } @@ -127,7 +127,7 @@ func TestCustomLinter_EmptyOutput(t *testing.T) { func TestRunLint_WithTimeout(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "x.go") - if err := os.WriteFile(file, []byte("package x\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("package x\n"), 0o644); err != nil { t.Fatal(err) } @@ -166,7 +166,7 @@ func TestLanguageForFile(t *testing.T) { func TestRunLint_LanguageSet(t *testing.T) { dir := t.TempDir() file := filepath.Join(dir, "x.py") - if err := os.WriteFile(file, []byte("x = 1\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("x = 1\n"), 0o644); err != nil { t.Fatal(err) } @@ -195,7 +195,7 @@ func TestEslintLinter_WithMockNpx(t *testing.T) { // Create a mock npx that exits 0 with no output mockDir := t.TempDir() mockNpx := filepath.Join(mockDir, "npx") - if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\nexit 0\n"), 0755); err != nil { + if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil { t.Fatal(err) } @@ -206,7 +206,7 @@ func TestEslintLinter_WithMockNpx(t *testing.T) { linter := eslintLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.js") - if err := os.WriteFile(file, []byte("const x = 1;"), 0644); err != nil { + if err := os.WriteFile(file, []byte("const x = 1;"), 0o644); err != nil { t.Fatal(err) } @@ -220,7 +220,7 @@ func TestEslintLinter_WithMockNpx(t *testing.T) { func TestEslintLinter_WithMockNpx_Failure(t *testing.T) { mockDir := t.TempDir() mockNpx := filepath.Join(mockDir, "npx") - if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\necho 'error: semicolon missing'\nexit 1\n"), 0755); err != nil { + if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\necho 'error: semicolon missing'\nexit 1\n"), 0o755); err != nil { t.Fatal(err) } @@ -231,7 +231,7 @@ func TestEslintLinter_WithMockNpx_Failure(t *testing.T) { linter := eslintLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.js") - if err := os.WriteFile(file, []byte("const x = 1"), 0644); err != nil { + if err := os.WriteFile(file, []byte("const x = 1"), 0o644); err != nil { t.Fatal(err) } @@ -249,7 +249,7 @@ func TestEslintLinter_WithMockNpx_EmptyOutput(t *testing.T) { mockDir := t.TempDir() mockNpx := filepath.Join(mockDir, "npx") // npx --no-install miss: exits non-zero with no output - if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\nexit 1\n"), 0755); err != nil { + if err := os.WriteFile(mockNpx, []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil { t.Fatal(err) } @@ -260,7 +260,7 @@ func TestEslintLinter_WithMockNpx_EmptyOutput(t *testing.T) { linter := eslintLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.js") - if err := os.WriteFile(file, []byte("const x = 1;"), 0644); err != nil { + if err := os.WriteFile(file, []byte("const x = 1;"), 0o644); err != nil { t.Fatal(err) } @@ -275,7 +275,7 @@ func TestEslintLinter_WithMockNpx_EmptyOutput(t *testing.T) { func TestEslintLinter_WithMockEslint(t *testing.T) { mockDir := t.TempDir() mockEslint := filepath.Join(mockDir, "eslint") - if err := os.WriteFile(mockEslint, []byte("#!/bin/sh\nexit 0\n"), 0755); err != nil { + if err := os.WriteFile(mockEslint, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil { t.Fatal(err) } @@ -286,7 +286,7 @@ func TestEslintLinter_WithMockEslint(t *testing.T) { linter := eslintLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.js") - if err := os.WriteFile(file, []byte("const x = 1;"), 0644); err != nil { + if err := os.WriteFile(file, []byte("const x = 1;"), 0o644); err != nil { t.Fatal(err) } @@ -300,7 +300,7 @@ func TestEslintLinter_WithMockEslint(t *testing.T) { func TestRuffLinter_WithMockRuff(t *testing.T) { mockDir := t.TempDir() mockRuff := filepath.Join(mockDir, "ruff") - if err := os.WriteFile(mockRuff, []byte("#!/bin/sh\nexit 0\n"), 0755); err != nil { + if err := os.WriteFile(mockRuff, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil { t.Fatal(err) } @@ -311,7 +311,7 @@ func TestRuffLinter_WithMockRuff(t *testing.T) { linter := ruffLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.py") - if err := os.WriteFile(file, []byte("x = 1\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("x = 1\n"), 0o644); err != nil { t.Fatal(err) } @@ -325,7 +325,7 @@ func TestRuffLinter_WithMockRuff(t *testing.T) { func TestRuffLinter_WithMockRuff_Failure(t *testing.T) { mockDir := t.TempDir() mockRuff := filepath.Join(mockDir, "ruff") - if err := os.WriteFile(mockRuff, []byte("#!/bin/sh\necho 'E501 line too long'\nexit 1\n"), 0755); err != nil { + if err := os.WriteFile(mockRuff, []byte("#!/bin/sh\necho 'E501 line too long'\nexit 1\n"), 0o755); err != nil { t.Fatal(err) } @@ -336,7 +336,7 @@ func TestRuffLinter_WithMockRuff_Failure(t *testing.T) { linter := ruffLinter{} dir := t.TempDir() file := filepath.Join(dir, "test.py") - if err := os.WriteFile(file, []byte("x = 1\n"), 0644); err != nil { + if err := os.WriteFile(file, []byte("x = 1\n"), 0o644); err != nil { t.Fatal(err) } diff --git a/internal/lsp/lsp_client_test.go b/internal/lsp/lsp_client_test.go index 41a6e3fe..cf2f2c6f 100644 --- a/internal/lsp/lsp_client_test.go +++ b/internal/lsp/lsp_client_test.go @@ -109,14 +109,14 @@ func TestLoadConfig_Extra(t *testing.T) { // Create a temp directory with a custom config dir := t.TempDir() agentsDir := filepath.Join(dir, ".agents") - if err := os.MkdirAll(agentsDir, 0755); err != nil { + if err := os.MkdirAll(agentsDir, 0o755); err != nil { t.Fatalf("failed to create .agents dir: %v", err) } // Write a custom config customConfig := `{"lsp": {"custom-lang": {"command": "custom-lsp", "extensions": [".custom"]}}}` configPath := filepath.Join(agentsDir, "lsp.json") - if err := os.WriteFile(configPath, []byte(customConfig), 0644); err != nil { + if err := os.WriteFile(configPath, []byte(customConfig), 0o644); err != nil { t.Fatalf("failed to write config: %v", err) } @@ -144,13 +144,13 @@ func TestLoadConfig_InvalidJSON_Extra(t *testing.T) { dir := t.TempDir() agentsDir := filepath.Join(dir, ".agents") - if err := os.MkdirAll(agentsDir, 0755); err != nil { + if err := os.MkdirAll(agentsDir, 0o755); err != nil { t.Fatalf("failed to create .agents dir: %v", err) } // Write invalid JSON configPath := filepath.Join(agentsDir, "lsp.json") - if err := os.WriteFile(configPath, []byte("{invalid json}"), 0644); err != nil { + if err := os.WriteFile(configPath, []byte("{invalid json}"), 0o644); err != nil { t.Fatalf("failed to write config: %v", err) } diff --git a/internal/sandbox/config_extra_test.go b/internal/sandbox/config_extra_test.go index cfc7d8e8..29beae39 100644 --- a/internal/sandbox/config_extra_test.go +++ b/internal/sandbox/config_extra_test.go @@ -51,8 +51,8 @@ func TestResolveMode_Aliases(t *testing.T) { func TestResolveMode_Extends(t *testing.T) { cfg := TOMLConfig{ Profiles: map[string]ProfileConfig{ - "base": {Mode: "strict"}, - "child": {Extends: "base"}, + "base": {Mode: "strict"}, + "child": {Extends: "base"}, }, } mode := resolveMode(cfg, "child") diff --git a/internal/snapshot/snapshot_extra_test.go b/internal/snapshot/snapshot_extra_test.go index b4531865..707ab34a 100644 --- a/internal/snapshot/snapshot_extra_test.go +++ b/internal/snapshot/snapshot_extra_test.go @@ -13,7 +13,7 @@ func TestTracker_Cleanup(t *testing.T) { projectDir := t.TempDir() // Create a file in the project - if err := os.WriteFile(filepath.Join(projectDir, "test.txt"), []byte("hello"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "test.txt"), []byte("hello"), 0o644); err != nil { t.Fatal(err) } @@ -110,10 +110,10 @@ func TestSnapshotStore_Delete(t *testing.T) { store := NewSnapshotStore(dir) snap := &WorkspaceSnapshot{ - ID: "test-id-123456", - Name: "test-snapshot", - Files: make(map[string]FileState), - CreatedAt: time.Now(), + ID: "test-id-123456", + Name: "test-snapshot", + Files: make(map[string]FileState), + CreatedAt: time.Now(), } if err := store.Save(snap); err != nil { @@ -230,38 +230,38 @@ func TestSnapshotStore_Diff(t *testing.T) { // Create a project dir projectDir := t.TempDir() - if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("content1"), 0644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("content1"), 0o644); writeErr != nil { + t.Fatal(writeErr) } - if err := os.WriteFile(filepath.Join(projectDir, "file2.txt"), []byte("content2"), 0644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(projectDir, "file2.txt"), []byte("content2"), 0o644); writeErr != nil { + t.Fatal(writeErr) } // Capture a snapshot - snap, err := store.Capture(projectDir, "test", "test diff") - if err != nil { - t.Fatalf("Capture failed: %v", err) + snap, captureErr := store.Capture(projectDir, "test", "test diff") + if captureErr != nil { + t.Fatalf("Capture failed: %v", captureErr) } // Modify a file - if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("modified"), 0644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("modified"), 0o644); writeErr != nil { + t.Fatal(writeErr) } // Add a new file - if err := os.WriteFile(filepath.Join(projectDir, "file3.txt"), []byte("content3"), 0644); err != nil { - t.Fatal(err) + if writeErr := os.WriteFile(filepath.Join(projectDir, "file3.txt"), []byte("content3"), 0o644); writeErr != nil { + t.Fatal(writeErr) } // Delete a file - if err := os.Remove(filepath.Join(projectDir, "file2.txt")); err != nil { - t.Fatal(err) + if removeErr := os.Remove(filepath.Join(projectDir, "file2.txt")); removeErr != nil { + t.Fatal(removeErr) } // Diff should show changes - diff, err := store.Diff(snap.ID, projectDir) - if err != nil { - t.Fatalf("Diff failed: %v", err) + diff, diffErr := store.Diff(snap.ID, projectDir) + if diffErr != nil { + t.Fatalf("Diff failed: %v", diffErr) } if len(diff.Added) != 1 { t.Errorf("expected 1 added file, got %d", len(diff.Added)) @@ -295,7 +295,7 @@ func TestSnapshotStore_Restore(t *testing.T) { // Create a project dir projectDir := t.TempDir() - if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("content1"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("content1"), 0o644); err != nil { t.Fatal(err) } @@ -306,24 +306,24 @@ func TestSnapshotStore_Restore(t *testing.T) { } // Modify the file - if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("modified"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "file1.txt"), []byte("modified"), 0o644); err != nil { t.Fatal(err) } // Add a new file - if err := os.WriteFile(filepath.Join(projectDir, "file2.txt"), []byte("content2"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "file2.txt"), []byte("content2"), 0o644); err != nil { t.Fatal(err) } // Restore the snapshot - if err := store.Restore(snap.ID, projectDir); err != nil { - t.Fatalf("Restore failed: %v", err) + if restoreErr := store.Restore(snap.ID, projectDir); restoreErr != nil { + t.Fatalf("Restore failed: %v", restoreErr) } // Verify file1.txt is restored - content, err := os.ReadFile(filepath.Join(projectDir, "file1.txt")) - if err != nil { - t.Fatalf("ReadFile failed: %v", err) + content, restoreErr := os.ReadFile(filepath.Join(projectDir, "file1.txt")) + if restoreErr != nil { + t.Fatalf("ReadFile failed: %v", restoreErr) } if string(content) != "content1" { t.Errorf("expected 'content1', got %q", string(content)) @@ -445,7 +445,7 @@ func TestRemoveEmptyParents(t *testing.T) { // Create a nested empty directory structure base := t.TempDir() nested := filepath.Join(base, "a", "b", "c") - if err := os.MkdirAll(nested, 0755); err != nil { + if err := os.MkdirAll(nested, 0o755); err != nil { t.Fatal(err) } @@ -462,12 +462,12 @@ func TestRemoveEmptyParents(t *testing.T) { func TestRemoveEmptyParents_NonEmpty(t *testing.T) { base := t.TempDir() nested := filepath.Join(base, "a", "b", "c") - if err := os.MkdirAll(nested, 0755); err != nil { + if err := os.MkdirAll(nested, 0o755); err != nil { t.Fatal(err) } // Create a file in the nested dir - if err := os.WriteFile(filepath.Join(nested, "file.txt"), []byte("test"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(nested, "file.txt"), []byte("test"), 0o644); err != nil { t.Fatal(err) } @@ -503,23 +503,23 @@ func TestSnapshotStore_Capture_WithIgnoredDirs(t *testing.T) { projectDir := t.TempDir() // Create regular file - if err := os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "main.go"), []byte("package main"), 0o644); err != nil { t.Fatal(err) } // Create ignored directory with a file - if err := os.MkdirAll(filepath.Join(projectDir, "node_modules"), 0755); err != nil { + if err := os.MkdirAll(filepath.Join(projectDir, "node_modules"), 0o755); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(projectDir, "node_modules", "pkg.js"), []byte("test"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "node_modules", "pkg.js"), []byte("test"), 0o644); err != nil { t.Fatal(err) } // Create .git directory with a file - if err := os.MkdirAll(filepath.Join(projectDir, ".git"), 0755); err != nil { + if err := os.MkdirAll(filepath.Join(projectDir, ".git"), 0o755); err != nil { t.Fatal(err) } - if err := os.WriteFile(filepath.Join(projectDir, ".git", "config"), []byte("test"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, ".git", "config"), []byte("test"), 0o644); err != nil { t.Fatal(err) } @@ -564,7 +564,7 @@ func TestSnapshotStore_Capture_OverwritesAndPrunes(t *testing.T) { // Capture 3 snapshots for i := 0; i < 3; i++ { - if err := os.WriteFile(filepath.Join(projectDir, "file.txt"), []byte("content"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "file.txt"), []byte("content"), 0o644); err != nil { t.Fatal(err) } _, err := store.Capture(projectDir, "snap"+string(rune('a'+i)), "test") @@ -589,7 +589,7 @@ func TestSnapshotStore_Get_WithContent(t *testing.T) { store := NewSnapshotStore(dir) projectDir := t.TempDir() - if err := os.WriteFile(filepath.Join(projectDir, "test.txt"), []byte("file content"), 0644); err != nil { + if err := os.WriteFile(filepath.Join(projectDir, "test.txt"), []byte("file content"), 0o644); err != nil { t.Fatal(err) } diff --git a/internal/storage/paths_extra_test.go b/internal/storage/paths_extra_test.go index 6b634ae9..525b3a46 100644 --- a/internal/storage/paths_extra_test.go +++ b/internal/storage/paths_extra_test.go @@ -21,11 +21,11 @@ func TestSanitizeName_ValidName(t *testing.T) { {"with spaces", "with-spaces"}, {"with@special#chars", "with-special-chars"}, {"123numbers", "123numbers"}, - {"", "project"}, // empty becomes "project" - {"...", "project"}, // only dots becomes "project" - {"---", "project"}, // only dashes becomes "project" - {" ", "project"}, // only spaces becomes "project" - {".-.", "project"}, // mixed dots and dashes becomes "project" + {"", "project"}, // empty becomes "project" + {"...", "project"}, // only dots becomes "project" + {"---", "project"}, // only dashes becomes "project" + {" ", "project"}, // only spaces becomes "project" + {".-.", "project"}, // mixed dots and dashes becomes "project" {"valid.name-123", "valid.name-123"}, {"UPPER", "UPPER"}, {"a", "a"},