package store_test import ( "context" "testing" "github.com/syonad/clonepack/internal/store" ) func newCloneJobStore(t *testing.T) (*store.SQLiteCloneJobStore, int64) { t.Helper() db := newTestDB(t) repoID := insertTestRepo(t, db) return store.NewCloneJobStore(db), repoID } func TestCreateAndGetCloneJob(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() jobID, err := s.CreateCloneJob(ctx, repoID) if err != nil { t.Fatalf("CreateCloneJob: %v", err) } job, err := s.GetCloneJob(ctx, jobID) if err != nil { t.Fatalf("GetCloneJob: %v", err) } if job.RepoID != repoID { t.Errorf("RepoID: got %d, want %d", job.RepoID, repoID) } if job.Status != store.CloneJobPending { t.Errorf("Status: got %q, want pending", job.Status) } if job.StartedAt != nil { t.Errorf("StartedAt should be nil initially") } if job.FinishedAt != nil { t.Errorf("FinishedAt should be nil initially") } } func TestGetCloneJob_notFound(t *testing.T) { s, _ := newCloneJobStore(t) _, err := s.GetCloneJob(context.Background(), 9999) if err != store.ErrNotFound { t.Errorf("expected ErrNotFound, got %v", err) } } func TestGetLatestCloneJob(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() s.CreateCloneJob(ctx, repoID) id2, _ := s.CreateCloneJob(ctx, repoID) job, err := s.GetLatestCloneJob(ctx, repoID) if err != nil { t.Fatalf("GetLatestCloneJob: %v", err) } if job.ID != id2 { t.Errorf("expected latest job ID %d, got %d", id2, job.ID) } } func TestGetLatestCloneJob_notFound(t *testing.T) { s, repoID := newCloneJobStore(t) _, err := s.GetLatestCloneJob(context.Background(), repoID) if err != store.ErrNotFound { t.Errorf("expected ErrNotFound, got %v", err) } } func TestMarkCloneJobStarted(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() jobID, _ := s.CreateCloneJob(ctx, repoID) if err := s.MarkCloneJobStarted(ctx, jobID); err != nil { t.Fatalf("MarkCloneJobStarted: %v", err) } job, _ := s.GetCloneJob(ctx, jobID) if job.Status != store.CloneJobRunning { t.Errorf("Status: got %q, want running", job.Status) } if job.StartedAt == nil { t.Error("StartedAt should be set") } } func TestMarkCloneJobFinished_completed(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() jobID, _ := s.CreateCloneJob(ctx, repoID) s.MarkCloneJobStarted(ctx, jobID) if err := s.MarkCloneJobFinished(ctx, jobID, store.CloneJobCompleted, nil); err != nil { t.Fatalf("MarkCloneJobFinished: %v", err) } job, _ := s.GetCloneJob(ctx, jobID) if job.Status != store.CloneJobCompleted { t.Errorf("Status: got %q, want completed", job.Status) } if job.FinishedAt == nil { t.Error("FinishedAt should be set") } if job.Error != nil { t.Errorf("Error should be nil, got %q", *job.Error) } } func TestMarkCloneJobFinished_failed(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() jobID, _ := s.CreateCloneJob(ctx, repoID) s.MarkCloneJobStarted(ctx, jobID) errMsg := "connection refused" if err := s.MarkCloneJobFinished(ctx, jobID, store.CloneJobFailed, &errMsg); err != nil { t.Fatalf("MarkCloneJobFinished: %v", err) } job, _ := s.GetCloneJob(ctx, jobID) if job.Status != store.CloneJobFailed { t.Errorf("Status: got %q, want failed", job.Status) } if job.Error == nil || *job.Error != errMsg { t.Errorf("Error: got %v, want %q", job.Error, errMsg) } } func TestHasRunningCloneJob(t *testing.T) { s, repoID := newCloneJobStore(t) ctx := context.Background() running, err := s.HasRunningCloneJob(ctx, repoID) if err != nil { t.Fatalf("HasRunningCloneJob: %v", err) } if running { t.Error("expected no running job initially") } jobID, _ := s.CreateCloneJob(ctx, repoID) running, _ = s.HasRunningCloneJob(ctx, repoID) if !running { t.Error("expected running job after create (status=pending)") } s.MarkCloneJobStarted(ctx, jobID) running, _ = s.HasRunningCloneJob(ctx, repoID) if !running { t.Error("expected running job after start") } s.MarkCloneJobFinished(ctx, jobID, store.CloneJobCompleted, nil) running, _ = s.HasRunningCloneJob(ctx, repoID) if running { t.Error("expected no running job after completion") } }