refactor: add testEnv helpers, reduce SQLite test file sizes (bd-4opy)
- Add testEnv struct with common test helper methods to test_helpers.go - Methods include CreateIssue, CreateEpic, AddDep, AddParentChild, Close, GetReadyWork, AssertReady, AssertBlocked - Migrate 12+ tests in ready_test.go to use new helpers (-252 lines) - Migrate 3 tests in dependencies_test.go to use new helpers (-34 lines) - Total reduction: ~286 lines from test files The testEnv pattern makes tests more readable and maintainable by: - Eliminating boilerplate setup (store, cleanup, ctx) - Providing semantic helper methods (AddDep vs manual AddDependency) - Using t.Cleanup for automatic resource management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,152 @@ package sqlite
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
// testEnv provides a test environment with common setup and helpers.
|
||||
// Use newTestEnv(t) to create a test environment with automatic cleanup.
|
||||
type testEnv struct {
|
||||
t *testing.T
|
||||
Store *SQLiteStorage
|
||||
Ctx context.Context
|
||||
}
|
||||
|
||||
// newTestEnv creates a new test environment with a configured store.
|
||||
// The store is automatically cleaned up when the test completes.
|
||||
func newTestEnv(t *testing.T) *testEnv {
|
||||
t.Helper()
|
||||
store := newTestStore(t, "")
|
||||
return &testEnv{
|
||||
t: t,
|
||||
Store: store,
|
||||
Ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateIssue creates a test issue with the given title and defaults.
|
||||
// Returns the created issue with ID populated.
|
||||
func (e *testEnv) CreateIssue(title string) *types.Issue {
|
||||
e.t.Helper()
|
||||
return e.CreateIssueWith(title, types.StatusOpen, 2, types.TypeTask)
|
||||
}
|
||||
|
||||
// CreateIssueWith creates a test issue with specified attributes.
|
||||
func (e *testEnv) CreateIssueWith(title string, status types.Status, priority int, issueType types.IssueType) *types.Issue {
|
||||
e.t.Helper()
|
||||
issue := &types.Issue{
|
||||
Title: title,
|
||||
Status: status,
|
||||
Priority: priority,
|
||||
IssueType: issueType,
|
||||
}
|
||||
if err := e.Store.CreateIssue(e.Ctx, issue, "test-user"); err != nil {
|
||||
e.t.Fatalf("CreateIssue(%q) failed: %v", title, err)
|
||||
}
|
||||
return issue
|
||||
}
|
||||
|
||||
// CreateIssueWithAssignee creates a test issue with an assignee.
|
||||
func (e *testEnv) CreateIssueWithAssignee(title, assignee string) *types.Issue {
|
||||
e.t.Helper()
|
||||
issue := &types.Issue{
|
||||
Title: title,
|
||||
Status: types.StatusOpen,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
Assignee: assignee,
|
||||
}
|
||||
if err := e.Store.CreateIssue(e.Ctx, issue, "test-user"); err != nil {
|
||||
e.t.Fatalf("CreateIssue(%q) failed: %v", title, err)
|
||||
}
|
||||
return issue
|
||||
}
|
||||
|
||||
// CreateEpic creates an epic issue.
|
||||
func (e *testEnv) CreateEpic(title string) *types.Issue {
|
||||
e.t.Helper()
|
||||
return e.CreateIssueWith(title, types.StatusOpen, 1, types.TypeEpic)
|
||||
}
|
||||
|
||||
// CreateBug creates a bug issue.
|
||||
func (e *testEnv) CreateBug(title string, priority int) *types.Issue {
|
||||
e.t.Helper()
|
||||
return e.CreateIssueWith(title, types.StatusOpen, priority, types.TypeBug)
|
||||
}
|
||||
|
||||
// AddDep adds a blocking dependency (issue depends on dependsOn).
|
||||
func (e *testEnv) AddDep(issue, dependsOn *types.Issue) {
|
||||
e.t.Helper()
|
||||
e.AddDepType(issue, dependsOn, types.DepBlocks)
|
||||
}
|
||||
|
||||
// AddDepType adds a dependency with the specified type.
|
||||
func (e *testEnv) AddDepType(issue, dependsOn *types.Issue, depType types.DependencyType) {
|
||||
e.t.Helper()
|
||||
dep := &types.Dependency{
|
||||
IssueID: issue.ID,
|
||||
DependsOnID: dependsOn.ID,
|
||||
Type: depType,
|
||||
}
|
||||
if err := e.Store.AddDependency(e.Ctx, dep, "test-user"); err != nil {
|
||||
e.t.Fatalf("AddDependency(%s -> %s) failed: %v", issue.ID, dependsOn.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddParentChild adds a parent-child dependency (child belongs to parent).
|
||||
func (e *testEnv) AddParentChild(child, parent *types.Issue) {
|
||||
e.t.Helper()
|
||||
e.AddDepType(child, parent, types.DepParentChild)
|
||||
}
|
||||
|
||||
// Close closes the issue with the given reason.
|
||||
func (e *testEnv) Close(issue *types.Issue, reason string) {
|
||||
e.t.Helper()
|
||||
if err := e.Store.CloseIssue(e.Ctx, issue.ID, reason, "test-user"); err != nil {
|
||||
e.t.Fatalf("CloseIssue(%s) failed: %v", issue.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetReadyWork gets ready work with the given filter.
|
||||
func (e *testEnv) GetReadyWork(filter types.WorkFilter) []*types.Issue {
|
||||
e.t.Helper()
|
||||
ready, err := e.Store.GetReadyWork(e.Ctx, filter)
|
||||
if err != nil {
|
||||
e.t.Fatalf("GetReadyWork failed: %v", err)
|
||||
}
|
||||
return ready
|
||||
}
|
||||
|
||||
// GetReadyIDs returns a map of issue IDs that are ready (open status).
|
||||
func (e *testEnv) GetReadyIDs() map[string]bool {
|
||||
e.t.Helper()
|
||||
ready := e.GetReadyWork(types.WorkFilter{Status: types.StatusOpen})
|
||||
ids := make(map[string]bool)
|
||||
for _, issue := range ready {
|
||||
ids[issue.ID] = true
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// AssertReady asserts that the issue is in the ready work list.
|
||||
func (e *testEnv) AssertReady(issue *types.Issue) {
|
||||
e.t.Helper()
|
||||
ids := e.GetReadyIDs()
|
||||
if !ids[issue.ID] {
|
||||
e.t.Errorf("expected %s (%s) to be ready, but it was blocked", issue.ID, issue.Title)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertBlocked asserts that the issue is NOT in the ready work list.
|
||||
func (e *testEnv) AssertBlocked(issue *types.Issue) {
|
||||
e.t.Helper()
|
||||
ids := e.GetReadyIDs()
|
||||
if ids[issue.ID] {
|
||||
e.t.Errorf("expected %s (%s) to be blocked, but it was ready", issue.ID, issue.Title)
|
||||
}
|
||||
}
|
||||
|
||||
// newTestStore creates a SQLiteStorage with issue_prefix configured (bd-166)
|
||||
// This prevents "database not initialized" errors in tests
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user