fix: add test parallelism and increase Windows CI timeout

- Increase Windows test timeout from 20m to 30m
- Add -parallel=4 flag to allow concurrent test execution
- Add t.Parallel() to safe table-driven tests in validate_test.go,
  autoimport_test.go, and sync_test.go

This should prevent the Windows CI timeout caused by the cumulative
runtime of cmd/bd tests exceeding 20 minutes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-03 18:30:53 -08:00
parent 76f7341cf4
commit 6267f3b7f5
4 changed files with 24 additions and 1 deletions

View File

@@ -71,7 +71,7 @@ jobs:
run: go build -v ./cmd/bd run: go build -v ./cmd/bd
- name: Test - name: Test
run: go test -v -short -timeout=20m ./... run: go test -v -short -timeout=30m -parallel=4 ./...
lint: lint:
name: Lint name: Lint

View File

@@ -283,6 +283,7 @@ func TestCheckGitForIssues_NoBeadsDir(t *testing.T) {
} }
func TestBoolToFlag(t *testing.T) { func TestBoolToFlag(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
name string name string
condition bool condition bool
@@ -296,7 +297,9 @@ func TestBoolToFlag(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
tt := tt // capture range variable
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := boolToFlag(tt.condition, tt.flag) got := boolToFlag(tt.condition, tt.flag)
if got != tt.want { if got != tt.want {
t.Errorf("boolToFlag(%v, %q) = %q, want %q", tt.condition, tt.flag, got, tt.want) t.Errorf("boolToFlag(%v, %q) = %q, want %q", tt.condition, tt.flag, got, tt.want)

View File

@@ -141,6 +141,7 @@ func TestGitCommit_AutoMessage(t *testing.T) {
} }
func TestCountIssuesInJSONL_NonExistent(t *testing.T) { func TestCountIssuesInJSONL_NonExistent(t *testing.T) {
t.Parallel()
count, err := countIssuesInJSONL("/nonexistent/path.jsonl") count, err := countIssuesInJSONL("/nonexistent/path.jsonl")
if err == nil { if err == nil {
t.Error("expected error for nonexistent file") t.Error("expected error for nonexistent file")
@@ -151,6 +152,7 @@ func TestCountIssuesInJSONL_NonExistent(t *testing.T) {
} }
func TestCountIssuesInJSONL_EmptyFile(t *testing.T) { func TestCountIssuesInJSONL_EmptyFile(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
jsonlPath := filepath.Join(tmpDir, "empty.jsonl") jsonlPath := filepath.Join(tmpDir, "empty.jsonl")
os.WriteFile(jsonlPath, []byte(""), 0644) os.WriteFile(jsonlPath, []byte(""), 0644)
@@ -165,6 +167,7 @@ func TestCountIssuesInJSONL_EmptyFile(t *testing.T) {
} }
func TestCountIssuesInJSONL_MultipleIssues(t *testing.T) { func TestCountIssuesInJSONL_MultipleIssues(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
jsonlPath := filepath.Join(tmpDir, "issues.jsonl") jsonlPath := filepath.Join(tmpDir, "issues.jsonl")
content := `{"id":"bd-1"} content := `{"id":"bd-1"}
@@ -183,6 +186,7 @@ func TestCountIssuesInJSONL_MultipleIssues(t *testing.T) {
} }
func TestCountIssuesInJSONL_WithMalformedLines(t *testing.T) { func TestCountIssuesInJSONL_WithMalformedLines(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
jsonlPath := filepath.Join(tmpDir, "mixed.jsonl") jsonlPath := filepath.Join(tmpDir, "mixed.jsonl")
content := `{"id":"bd-1"} content := `{"id":"bd-1"}
@@ -797,6 +801,7 @@ func TestMaybeAutoCompactDeletions_BelowThreshold(t *testing.T) {
} }
func TestSanitizeJSONLWithDeletions_NoDeletions(t *testing.T) { func TestSanitizeJSONLWithDeletions_NoDeletions(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -825,6 +830,7 @@ func TestSanitizeJSONLWithDeletions_NoDeletions(t *testing.T) {
} }
func TestSanitizeJSONLWithDeletions_EmptyDeletions(t *testing.T) { func TestSanitizeJSONLWithDeletions_EmptyDeletions(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -848,6 +854,7 @@ func TestSanitizeJSONLWithDeletions_EmptyDeletions(t *testing.T) {
} }
func TestSanitizeJSONLWithDeletions_RemovesDeletedIssues(t *testing.T) { func TestSanitizeJSONLWithDeletions_RemovesDeletedIssues(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -911,6 +918,7 @@ func TestSanitizeJSONLWithDeletions_RemovesDeletedIssues(t *testing.T) {
} }
func TestSanitizeJSONLWithDeletions_NoMatchingDeletions(t *testing.T) { func TestSanitizeJSONLWithDeletions_NoMatchingDeletions(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -947,6 +955,7 @@ func TestSanitizeJSONLWithDeletions_NoMatchingDeletions(t *testing.T) {
} }
func TestSanitizeJSONLWithDeletions_PreservesMalformedLines(t *testing.T) { func TestSanitizeJSONLWithDeletions_PreservesMalformedLines(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -987,6 +996,7 @@ this is not valid json
} }
func TestSanitizeJSONLWithDeletions_NonexistentJSONL(t *testing.T) { func TestSanitizeJSONLWithDeletions_NonexistentJSONL(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir() tmpDir := t.TempDir()
beadsDir := filepath.Join(tmpDir, ".beads") beadsDir := filepath.Join(tmpDir, ".beads")
os.MkdirAll(beadsDir, 0755) os.MkdirAll(beadsDir, 0755)
@@ -1110,6 +1120,7 @@ func TestHashBasedStalenessDetection_bd_f2f(t *testing.T) {
// to prevent creating incorrect deletion records for locally-created beads. // to prevent creating incorrect deletion records for locally-created beads.
// See: https://github.com/steveyegge/beads/issues/417 // See: https://github.com/steveyegge/beads/issues/417
func TestResolveNoGitHistoryForFromMain(t *testing.T) { func TestResolveNoGitHistoryForFromMain(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
name string name string
fromMain bool fromMain bool
@@ -1143,7 +1154,9 @@ func TestResolveNoGitHistoryForFromMain(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
tt := tt // capture range variable
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := resolveNoGitHistoryForFromMain(tt.fromMain, tt.noGitHistory) got := resolveNoGitHistoryForFromMain(tt.fromMain, tt.noGitHistory)
if got != tt.want { if got != tt.want {
t.Errorf("resolveNoGitHistoryForFromMain(%v, %v) = %v, want %v", t.Errorf("resolveNoGitHistoryForFromMain(%v, %v) = %v, want %v",

View File

@@ -9,6 +9,7 @@ import (
) )
func TestParseChecks(t *testing.T) { func TestParseChecks(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
name string name string
input string input string
@@ -63,7 +64,9 @@ func TestParseChecks(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
tt := tt // capture range variable
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got, err := parseChecks(tt.input) got, err := parseChecks(tt.input)
if tt.wantError { if tt.wantError {
if err == nil { if err == nil {
@@ -88,6 +91,7 @@ func TestParseChecks(t *testing.T) {
} }
func TestValidationResultsHasFailures(t *testing.T) { func TestValidationResultsHasFailures(t *testing.T) {
t.Parallel()
tests := []struct { tests := []struct {
name string name string
checks map[string]checkResult checks map[string]checkResult
@@ -125,7 +129,9 @@ func TestValidationResultsHasFailures(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
tt := tt // capture range variable
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
t.Parallel()
r := &validationResults{checks: tt.checks} r := &validationResults{checks: tt.checks}
got := r.hasFailures() got := r.hasFailures()
if got != tt.want { if got != tt.want {
@@ -136,6 +142,7 @@ func TestValidationResultsHasFailures(t *testing.T) {
} }
func TestValidationResultsToJSON(t *testing.T) { func TestValidationResultsToJSON(t *testing.T) {
t.Parallel()
r := &validationResults{ r := &validationResults{
checks: map[string]checkResult{ checks: map[string]checkResult{
"orphans": { "orphans": {