Add Beads library API for Go integration
Expose full Storage interface and all types through public beads.go API, enabling external Go projects (like VC) to import Beads directly instead of spawning CLI processes. Changes: - Expanded beads.go with all public types (Issue, Dependency, Comment, etc.) - Added all constants (Status, IssueType, DependencyType, EventType) - Created comprehensive integration tests (beads_integration_test.go) - Added library usage example at examples/library-usage/ - Documented library integration in README.md Test coverage: 96.4% on public API, 14 integration tests, all passing. Closes bd-58, bd-59 Amp-Thread-ID: https://ampcode.com/threads/T-f0093c79-7422-45e2-b0ed-0ddfebc9ffea Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
169
examples/library-usage/main_test.go
Normal file
169
examples/library-usage/main_test.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/steveyegge/beads"
|
||||
)
|
||||
|
||||
// TestExampleCompiles ensures the example code compiles and basic API works
|
||||
func TestExampleCompiles(t *testing.T) {
|
||||
// Create temporary database for testing
|
||||
tmpDir, err := os.MkdirTemp("", "beads-example-test-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dbPath := filepath.Join(tmpDir, "test.db")
|
||||
|
||||
// Open storage
|
||||
store, err := beads.NewSQLiteStorage(dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open storage: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create an issue (from example code)
|
||||
newIssue := &beads.Issue{
|
||||
ID: "",
|
||||
Title: "Test library-created issue",
|
||||
Description: "This verifies the library example works",
|
||||
Status: beads.StatusOpen,
|
||||
Priority: 2,
|
||||
IssueType: beads.TypeTask,
|
||||
}
|
||||
|
||||
if err := store.CreateIssue(ctx, newIssue, "test"); err != nil {
|
||||
t.Fatalf("Failed to create issue: %v", err)
|
||||
}
|
||||
|
||||
if newIssue.ID == "" {
|
||||
t.Error("Issue ID should be auto-generated")
|
||||
}
|
||||
|
||||
// Get ready work (from example code)
|
||||
ready, err := store.GetReadyWork(ctx, beads.WorkFilter{
|
||||
Status: beads.StatusOpen,
|
||||
Limit: 5,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get ready work: %v", err)
|
||||
}
|
||||
|
||||
if len(ready) == 0 {
|
||||
t.Error("Expected at least one ready issue")
|
||||
}
|
||||
|
||||
// Add label (from example code)
|
||||
if err := store.AddLabel(ctx, newIssue.ID, "test-label", "test"); err != nil {
|
||||
t.Fatalf("Failed to add label: %v", err)
|
||||
}
|
||||
|
||||
labels, err := store.GetLabels(ctx, newIssue.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get labels: %v", err)
|
||||
}
|
||||
|
||||
if len(labels) != 1 || labels[0] != "test-label" {
|
||||
t.Errorf("Expected label 'test-label', got %v", labels)
|
||||
}
|
||||
|
||||
// Update status (from example code)
|
||||
updates := map[string]interface{}{
|
||||
"status": beads.StatusInProgress,
|
||||
}
|
||||
if err := store.UpdateIssue(ctx, newIssue.ID, updates, "test"); err != nil {
|
||||
t.Fatalf("Failed to update issue: %v", err)
|
||||
}
|
||||
|
||||
// Get statistics (from example code)
|
||||
stats, err := store.GetStatistics(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get statistics: %v", err)
|
||||
}
|
||||
|
||||
if stats.TotalIssues == 0 {
|
||||
t.Error("Expected at least one total issue")
|
||||
}
|
||||
|
||||
// Close issue (from example code)
|
||||
if err := store.CloseIssue(ctx, newIssue.ID, "Test complete", "test"); err != nil {
|
||||
t.Fatalf("Failed to close issue: %v", err)
|
||||
}
|
||||
|
||||
// Verify closed
|
||||
closed, err := store.GetIssue(ctx, newIssue.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get issue: %v", err)
|
||||
}
|
||||
|
||||
if closed.Status != beads.StatusClosed {
|
||||
t.Errorf("Expected status closed, got %v", closed.Status)
|
||||
}
|
||||
|
||||
t.Log("✅ All example operations work correctly")
|
||||
}
|
||||
|
||||
// TestDependencyConstants ensures all constants are accessible
|
||||
func TestDependencyConstants(t *testing.T) {
|
||||
// Test that all constants from the example are accessible
|
||||
_ = beads.StatusOpen
|
||||
_ = beads.StatusInProgress
|
||||
_ = beads.StatusClosed
|
||||
_ = beads.StatusBlocked
|
||||
|
||||
_ = beads.TypeBug
|
||||
_ = beads.TypeFeature
|
||||
_ = beads.TypeTask
|
||||
_ = beads.TypeEpic
|
||||
_ = beads.TypeChore
|
||||
|
||||
_ = beads.DepBlocks
|
||||
_ = beads.DepRelated
|
||||
_ = beads.DepParentChild
|
||||
_ = beads.DepDiscoveredFrom
|
||||
}
|
||||
|
||||
// TestFindDatabasePath tests database discovery
|
||||
func TestFindDatabasePath(t *testing.T) {
|
||||
// Create temp directory with .beads
|
||||
tmpDir, err := os.MkdirTemp("", "beads-finddb-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
beadsDir := filepath.Join(tmpDir, ".beads")
|
||||
if err := os.MkdirAll(beadsDir, 0o755); err != nil {
|
||||
t.Fatalf("Failed to create .beads dir: %v", err)
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
f, err := os.Create(dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create db file: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
// Change to temp directory
|
||||
originalWd, _ := os.Getwd()
|
||||
defer os.Chdir(originalWd)
|
||||
|
||||
if err := os.Chdir(tmpDir); err != nil {
|
||||
t.Fatalf("Failed to chdir: %v", err)
|
||||
}
|
||||
|
||||
// Test FindDatabasePath
|
||||
found := beads.FindDatabasePath()
|
||||
if found == "" {
|
||||
t.Error("Expected to find database, got empty string")
|
||||
}
|
||||
|
||||
t.Logf("Found database: %s", found)
|
||||
}
|
||||
Reference in New Issue
Block a user