Optimize test suite: split integration tests, add -short support
- Split slow importer integration tests into separate file - Add t.Short() guards to 10 slow daemon tests - Document test organization in TEST_OPTIMIZATION.md - Fast tests now run in ~50s vs 3+ minutes - Use 'go test -short ./...' for fast feedback Amp-Thread-ID: https://ampcode.com/threads/T-29ae21ac-749d-43d7-bf0c-2c5f7a06ae76 Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
93
internal/importer/importer_integration_test.go
Normal file
93
internal/importer/importer_integration_test.go
Normal file
@@ -0,0 +1,93 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
// TestConcurrentExternalRefUpdates tests concurrent updates to same external_ref with different timestamps
|
||||
// This is a slow integration test that verifies no deadlocks occur
|
||||
func TestConcurrentExternalRefUpdates(t *testing.T) {
|
||||
store, err := sqlite.New(":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
||||
t.Fatalf("Failed to set prefix: %v", err)
|
||||
}
|
||||
|
||||
externalRef := "JIRA-200"
|
||||
existing := &types.Issue{
|
||||
ID: "bd-1",
|
||||
Title: "Existing issue",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
ExternalRef: &externalRef,
|
||||
}
|
||||
|
||||
if err := store.CreateIssue(ctx, existing, "test"); err != nil {
|
||||
t.Fatalf("Failed to create existing issue: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
results := make([]*Result, 3)
|
||||
done := make(chan bool, 1)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
updated := &types.Issue{
|
||||
ID: "bd-import-" + string(rune('1'+idx)),
|
||||
Title: "Updated from worker " + string(rune('A'+idx)),
|
||||
Status: types.StatusInProgress,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
ExternalRef: &externalRef,
|
||||
UpdatedAt: time.Now().Add(time.Duration(idx) * time.Second),
|
||||
}
|
||||
|
||||
result, _ := ImportIssues(ctx, "", store, []*types.Issue{updated}, Options{})
|
||||
results[idx] = result
|
||||
}(i)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
// Test completed normally
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Fatal("Test timed out after 30 seconds - likely deadlock in concurrent imports")
|
||||
}
|
||||
|
||||
finalIssue, err := store.GetIssueByExternalRef(ctx, externalRef)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get final issue: %v", err)
|
||||
}
|
||||
|
||||
if finalIssue == nil {
|
||||
t.Fatal("Expected final issue to exist")
|
||||
}
|
||||
|
||||
// Verify that we got the update with the latest timestamp (worker 2)
|
||||
if finalIssue.Title != "Updated from worker C" {
|
||||
t.Errorf("Expected last update to win, got title: %s", finalIssue.Title)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
//go:build !integration
|
||||
// +build !integration
|
||||
|
||||
package importer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -1040,82 +1042,4 @@ func TestConcurrentExternalRefImports(t *testing.T) {
|
||||
t.Errorf("Expected title to be updated, got %s", finalIssue.Title)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("concurrent updates to same external_ref with different timestamps", func(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping slow concurrent test in short mode")
|
||||
}
|
||||
|
||||
store, err := sqlite.New(":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
||||
t.Fatalf("Failed to set prefix: %v", err)
|
||||
}
|
||||
|
||||
externalRef := "JIRA-200"
|
||||
existing := &types.Issue{
|
||||
ID: "bd-1",
|
||||
Title: "Existing issue",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
ExternalRef: &externalRef,
|
||||
}
|
||||
|
||||
if err := store.CreateIssue(ctx, existing, "test"); err != nil {
|
||||
t.Fatalf("Failed to create existing issue: %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
results := make([]*Result, 3)
|
||||
done := make(chan bool, 1)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func(idx int) {
|
||||
defer wg.Done()
|
||||
|
||||
updated := &types.Issue{
|
||||
ID: "bd-import-" + string(rune('1'+idx)),
|
||||
Title: "Updated from worker " + string(rune('A'+idx)),
|
||||
Status: types.StatusInProgress,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
ExternalRef: &externalRef,
|
||||
UpdatedAt: time.Now().Add(time.Duration(idx) * time.Second),
|
||||
}
|
||||
|
||||
result, _ := ImportIssues(ctx, "", store, []*types.Issue{updated}, Options{})
|
||||
results[idx] = result
|
||||
}(i)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
// Test completed normally
|
||||
case <-time.After(30 * time.Second):
|
||||
t.Fatal("Test timed out after 30 seconds - likely deadlock in concurrent imports")
|
||||
}
|
||||
|
||||
finalIssue, err := store.GetIssueByExternalRef(ctx, externalRef)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get final issue: %v", err)
|
||||
}
|
||||
|
||||
if finalIssue == nil {
|
||||
t.Error("Expected final issue to exist")
|
||||
}
|
||||
|
||||
t.Logf("Final issue title: %s, status: %s", finalIssue.Title, finalIssue.Status)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user