Multiple test files were still using the old sqlite.New(path) signature instead of the new sqlite.New(ctx, path) signature. This was causing compilation failures in the test suite. Fixed files: - internal/importer/importer_test.go - internal/importer/external_ref_test.go - internal/importer/timestamp_test.go - internal/rpc/limits_test.go - internal/rpc/list_filters_test.go - internal/rpc/rpc_test.go - internal/rpc/status_test.go - internal/syncbranch/syncbranch_test.go
375 lines
9.7 KiB
Go
375 lines
9.7 KiB
Go
//go:build integration
|
|
// +build integration
|
|
|
|
package importer
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
|
"github.com/steveyegge/beads/internal/types"
|
|
)
|
|
|
|
func TestImportWithExternalRef(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping slow import test in short mode")
|
|
}
|
|
ctx := context.Background()
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
|
|
// Create database
|
|
store, err := sqlite.New(context.Background(), dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create database: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
// Set prefix
|
|
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
|
t.Fatalf("Failed to set prefix: %v", err)
|
|
}
|
|
|
|
// Create initial issue with external_ref
|
|
externalRef := "JIRA-100"
|
|
initial := &types.Issue{
|
|
ID: "bd-test-1",
|
|
Title: "Initial title",
|
|
Description: "Initial description",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
ExternalRef: &externalRef,
|
|
CreatedAt: time.Now().Add(-2 * time.Hour),
|
|
UpdatedAt: time.Now().Add(-2 * time.Hour),
|
|
}
|
|
|
|
err = store.CreateIssue(ctx, initial, "test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create initial issue: %v", err)
|
|
}
|
|
|
|
// Import updated issue with same external_ref but different content
|
|
updated := &types.Issue{
|
|
ID: "bd-test-1", // Same ID
|
|
Title: "Updated title from Jira",
|
|
Description: "Updated description from Jira",
|
|
Status: types.StatusInProgress,
|
|
Priority: 2,
|
|
IssueType: types.TypeBug,
|
|
ExternalRef: &externalRef, // Same external_ref
|
|
CreatedAt: initial.CreatedAt,
|
|
UpdatedAt: time.Now(), // Newer timestamp
|
|
}
|
|
|
|
opts := Options{
|
|
DryRun: false,
|
|
SkipUpdate: false,
|
|
SkipPrefixValidation: true,
|
|
}
|
|
|
|
result, err := ImportIssues(ctx, dbPath, store, []*types.Issue{updated}, opts)
|
|
if err != nil {
|
|
t.Fatalf("ImportIssues failed: %v", err)
|
|
}
|
|
|
|
// Should have updated 1 issue
|
|
if result.Updated != 1 {
|
|
t.Errorf("Expected 1 updated issue, got %d", result.Updated)
|
|
}
|
|
|
|
if result.Created != 0 {
|
|
t.Errorf("Expected 0 created issues, got %d", result.Created)
|
|
}
|
|
|
|
// Verify the update
|
|
issue, err := store.GetIssue(ctx, "bd-test-1")
|
|
if err != nil {
|
|
t.Fatalf("Failed to get issue: %v", err)
|
|
}
|
|
|
|
if issue.Title != "Updated title from Jira" {
|
|
t.Errorf("Expected title 'Updated title from Jira', got '%s'", issue.Title)
|
|
}
|
|
|
|
if issue.Status != types.StatusInProgress {
|
|
t.Errorf("Expected status in_progress, got %s", issue.Status)
|
|
}
|
|
}
|
|
|
|
func TestImportWithExternalRefDifferentID(t *testing.T) {
|
|
ctx := context.Background()
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
|
|
// Create database
|
|
store, err := sqlite.New(context.Background(), dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create database: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
// Set prefix
|
|
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
|
t.Fatalf("Failed to set prefix: %v", err)
|
|
}
|
|
|
|
// Create initial issue with external_ref
|
|
externalRef := "GH-200"
|
|
initial := &types.Issue{
|
|
ID: "bd-old-id",
|
|
Title: "Initial title",
|
|
Description: "Initial description",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeFeature,
|
|
ExternalRef: &externalRef,
|
|
CreatedAt: time.Now().Add(-2 * time.Hour),
|
|
UpdatedAt: time.Now().Add(-2 * time.Hour),
|
|
}
|
|
|
|
err = store.CreateIssue(ctx, initial, "test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create initial issue: %v", err)
|
|
}
|
|
|
|
// Import issue with same external_ref but DIFFERENT ID
|
|
// This simulates re-syncing from GitHub where ID changed
|
|
updated := &types.Issue{
|
|
ID: "bd-new-id", // Different ID
|
|
Title: "Updated title from GitHub",
|
|
Description: "Updated description from GitHub",
|
|
Status: types.StatusInProgress,
|
|
Priority: 2,
|
|
IssueType: types.TypeFeature,
|
|
ExternalRef: &externalRef, // Same external_ref
|
|
CreatedAt: initial.CreatedAt,
|
|
UpdatedAt: time.Now(), // Newer timestamp
|
|
}
|
|
|
|
opts := Options{
|
|
DryRun: false,
|
|
SkipUpdate: false,
|
|
SkipPrefixValidation: true,
|
|
}
|
|
|
|
result, err := ImportIssues(ctx, dbPath, store, []*types.Issue{updated}, opts)
|
|
if err != nil {
|
|
t.Fatalf("ImportIssues failed: %v", err)
|
|
}
|
|
|
|
// Should have updated the existing issue (matched by external_ref)
|
|
if result.Updated != 1 {
|
|
t.Errorf("Expected 1 updated issue, got %d", result.Updated)
|
|
}
|
|
|
|
// Verify the old ID was updated (not deleted/recreated)
|
|
oldIssue, err := store.GetIssue(ctx, "bd-old-id")
|
|
if err != nil {
|
|
t.Fatalf("Failed to get issue by old ID: %v", err)
|
|
}
|
|
|
|
if oldIssue == nil {
|
|
t.Fatal("Expected old ID to still exist and be updated")
|
|
}
|
|
|
|
if oldIssue.Title != "Updated title from GitHub" {
|
|
t.Errorf("Expected title 'Updated title from GitHub', got '%s'", oldIssue.Title)
|
|
}
|
|
|
|
// The new ID should NOT exist (we updated the existing one)
|
|
newIssue, err := store.GetIssue(ctx, "bd-new-id")
|
|
if err != nil {
|
|
t.Fatalf("Failed to check for new ID: %v", err)
|
|
}
|
|
|
|
if newIssue != nil {
|
|
t.Error("Expected new ID to NOT be created, but it exists")
|
|
}
|
|
}
|
|
|
|
func TestImportLocalIssueNotOverwrittenByExternalRef(t *testing.T) {
|
|
ctx := context.Background()
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
|
|
// Create database
|
|
store, err := sqlite.New(context.Background(), dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create database: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
// Set prefix
|
|
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
|
t.Fatalf("Failed to set prefix: %v", err)
|
|
}
|
|
|
|
// Create local issue WITHOUT external_ref
|
|
local := &types.Issue{
|
|
ID: "bd-local-1",
|
|
Title: "Local task",
|
|
Description: "Created locally",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeTask,
|
|
// No ExternalRef
|
|
CreatedAt: time.Now().Add(-2 * time.Hour),
|
|
UpdatedAt: time.Now().Add(-2 * time.Hour),
|
|
}
|
|
|
|
err = store.CreateIssue(ctx, local, "test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create local issue: %v", err)
|
|
}
|
|
|
|
// Import external issue with external_ref but different ID
|
|
externalRef := "JIRA-300"
|
|
external := &types.Issue{
|
|
ID: "bd-external-1",
|
|
Title: "External issue",
|
|
Description: "From Jira",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
ExternalRef: &externalRef,
|
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
|
UpdatedAt: time.Now().Add(-1 * time.Hour),
|
|
}
|
|
|
|
opts := Options{
|
|
DryRun: false,
|
|
SkipUpdate: false,
|
|
SkipPrefixValidation: true,
|
|
}
|
|
|
|
result, err := ImportIssues(ctx, dbPath, store, []*types.Issue{external}, opts)
|
|
if err != nil {
|
|
t.Fatalf("ImportIssues failed: %v", err)
|
|
}
|
|
|
|
// Should create new issue (not overwrite local one)
|
|
if result.Created != 1 {
|
|
t.Errorf("Expected 1 created issue, got %d", result.Created)
|
|
}
|
|
|
|
// Verify local issue still exists unchanged
|
|
localIssue, err := store.GetIssue(ctx, "bd-local-1")
|
|
if err != nil {
|
|
t.Fatalf("Failed to get local issue: %v", err)
|
|
}
|
|
|
|
if localIssue == nil {
|
|
t.Fatal("Local issue was deleted!")
|
|
}
|
|
|
|
if localIssue.Title != "Local task" {
|
|
t.Errorf("Local issue was modified! Title: %s", localIssue.Title)
|
|
}
|
|
|
|
if localIssue.ExternalRef != nil {
|
|
t.Error("Local issue should not have external_ref")
|
|
}
|
|
|
|
// Verify external issue was created
|
|
externalIssue, err := store.GetIssue(ctx, "bd-external-1")
|
|
if err != nil {
|
|
t.Fatalf("Failed to get external issue: %v", err)
|
|
}
|
|
|
|
if externalIssue == nil {
|
|
t.Fatal("External issue was not created")
|
|
}
|
|
|
|
if externalIssue.ExternalRef == nil || *externalIssue.ExternalRef != externalRef {
|
|
t.Error("External issue missing external_ref")
|
|
}
|
|
}
|
|
|
|
func TestImportExternalRefTimestampCheck(t *testing.T) {
|
|
ctx := context.Background()
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "test.db")
|
|
|
|
// Create database
|
|
store, err := sqlite.New(context.Background(), dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create database: %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
// Set prefix
|
|
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
|
t.Fatalf("Failed to set prefix: %v", err)
|
|
}
|
|
|
|
// Create issue with external_ref and recent timestamp
|
|
externalRef := "LINEAR-400"
|
|
recent := &types.Issue{
|
|
ID: "bd-test-1",
|
|
Title: "Recent version",
|
|
Description: "Most recent",
|
|
Status: types.StatusInProgress,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
ExternalRef: &externalRef,
|
|
CreatedAt: time.Now().Add(-1 * time.Hour),
|
|
UpdatedAt: time.Now(), // Recent
|
|
}
|
|
|
|
err = store.CreateIssue(ctx, recent, "test")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create recent issue: %v", err)
|
|
}
|
|
|
|
// Try to import older version with same external_ref
|
|
older := &types.Issue{
|
|
ID: "bd-test-1",
|
|
Title: "Older version",
|
|
Description: "Older",
|
|
Status: types.StatusOpen,
|
|
Priority: 2,
|
|
IssueType: types.TypeBug,
|
|
ExternalRef: &externalRef,
|
|
CreatedAt: time.Now().Add(-2 * time.Hour),
|
|
UpdatedAt: time.Now().Add(-2 * time.Hour), // Older
|
|
}
|
|
|
|
opts := Options{
|
|
DryRun: false,
|
|
SkipUpdate: false,
|
|
SkipPrefixValidation: true,
|
|
}
|
|
|
|
result, err := ImportIssues(ctx, dbPath, store, []*types.Issue{older}, opts)
|
|
if err != nil {
|
|
t.Fatalf("ImportIssues failed: %v", err)
|
|
}
|
|
|
|
// Should NOT update (incoming is older)
|
|
if result.Updated != 0 {
|
|
t.Errorf("Expected 0 updated issues (timestamp check), got %d", result.Updated)
|
|
}
|
|
|
|
if result.Unchanged != 1 {
|
|
t.Errorf("Expected 1 unchanged issue, got %d", result.Unchanged)
|
|
}
|
|
|
|
// Verify the issue was not changed
|
|
issue, err := store.GetIssue(ctx, "bd-test-1")
|
|
if err != nil {
|
|
t.Fatalf("Failed to get issue: %v", err)
|
|
}
|
|
|
|
if issue.Title != "Recent version" {
|
|
t.Errorf("Issue was updated when it shouldn't be! Title: %s", issue.Title)
|
|
}
|
|
|
|
if issue.Status != types.StatusInProgress {
|
|
t.Errorf("Issue status changed! Got %s", issue.Status)
|
|
}
|
|
}
|