Files
skrueckenandCursor 98d2253f44
CI / test-and-build (push) Failing after 1m19s
CI / demo (push) Skipped
Add YouTrack pull/push sync with project settings and time tracking.
Wire manual Y menu and CLI sync for issues and work items, persist
per-project credentials, and keep local edits until an explicit push.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-24 00:17:51 +02:00

168 lines
4.2 KiB
Go

package ticket_test
import (
"os"
"path/filepath"
"testing"
"time"
"task/internal/ticket"
)
func TestJSONStoreCreateListUpdate(t *testing.T) {
dir := t.TempDir()
store := ticket.NewJSONStore(dir)
created, err := store.Create(ticket.Ticket{
Title: "Write tests",
ProjectID: "inbox",
})
if err != nil {
t.Fatalf("Create: %v", err)
}
if created.ID == "" {
t.Fatal("expected ID to be set")
}
if created.Status != ticket.StatusTodo {
t.Fatalf("status = %s, want todo", created.Status)
}
list, err := store.List("inbox")
if err != nil {
t.Fatalf("List: %v", err)
}
if len(list) != 1 {
t.Fatalf("len(list) = %d, want 1", len(list))
}
path := filepath.Join(dir, "projects", "inbox", "tickets.json")
if _, err := os.Stat(path); err != nil {
t.Fatalf("tickets.json missing: %v", err)
}
created.Status = ticket.StatusDone
created.Title = "Write better tests"
if err := store.Update(created); err != nil {
t.Fatalf("Update: %v", err)
}
got, err := store.Get(created.ID)
if err != nil {
t.Fatalf("Get: %v", err)
}
if got.Status != ticket.StatusDone || got.Title != "Write better tests" {
t.Fatalf("got %+v", got)
}
if err := store.Delete(created.ID); err != nil {
t.Fatalf("Delete: %v", err)
}
list, err = store.List("inbox")
if err != nil {
t.Fatalf("List after delete: %v", err)
}
if len(list) != 0 {
t.Fatalf("len(list) = %d, want 0", len(list))
}
}
func TestUpdateAfterMarkSyncedIsDirty(t *testing.T) {
store := ticket.NewJSONStore(t.TempDir())
created, err := store.Create(ticket.Ticket{
Title: "Synced", ProjectID: "inbox", Status: ticket.StatusTodo,
Source: ticket.SourceYouTrack, ExternalID: "TAS-1",
})
if err != nil {
t.Fatal(err)
}
if err := store.MarkSynced(created.ID); err != nil {
t.Fatalf("MarkSynced: %v", err)
}
synced, err := store.Get(created.ID)
if err != nil {
t.Fatal(err)
}
if synced.UpdatedAt.After(synced.SyncedAt) {
t.Fatalf("freshly marked synced should not be dirty: updated=%v syncedAt=%v",
synced.UpdatedAt, synced.SyncedAt)
}
// Simulate UI edit: copy ticket (SyncedAt == UpdatedAt) and change title.
edited := synced
edited.Title = "Edited locally"
time.Sleep(time.Millisecond) // ensure clock can move
if err := store.Update(edited); err != nil {
t.Fatal(err)
}
got, err := store.Get(created.ID)
if err != nil {
t.Fatal(err)
}
if !got.UpdatedAt.After(got.SyncedAt) {
t.Fatalf("edit must bump UpdatedAt past SyncedAt: %v vs %v", got.UpdatedAt, got.SyncedAt)
}
}
func TestJSONStoreMigratesLegacyTasksJSON(t *testing.T) {
dir := t.TempDir()
projectDir := filepath.Join(dir, "projects", "inbox")
if err := os.MkdirAll(projectDir, 0o755); err != nil {
t.Fatal(err)
}
legacy := `[{"id":"abc","title":"Legacy","status":"todo","projectId":"inbox","createdAt":"2024-01-01T00:00:00Z","updatedAt":"2024-01-01T00:00:00Z"}]`
if err := os.WriteFile(filepath.Join(projectDir, "tasks.json"), []byte(legacy), 0o644); err != nil {
t.Fatal(err)
}
store := ticket.NewJSONStore(dir)
list, err := store.List("inbox")
if err != nil {
t.Fatalf("List: %v", err)
}
if len(list) != 1 || list[0].Title != "Legacy" {
t.Fatalf("got %+v", list)
}
// Write should create tickets.json and remove legacy.
list[0].Title = "Migrated"
if err := store.Update(list[0]); err != nil {
t.Fatalf("Update: %v", err)
}
if _, err := os.Stat(filepath.Join(projectDir, "tickets.json")); err != nil {
t.Fatalf("tickets.json missing after write: %v", err)
}
if _, err := os.Stat(filepath.Join(projectDir, "tasks.json")); !os.IsNotExist(err) {
t.Fatalf("legacy tasks.json should be removed, err=%v", err)
}
}
func TestJSONStoreMove(t *testing.T) {
dir := t.TempDir()
store := ticket.NewJSONStore(dir)
created, err := store.Create(ticket.Ticket{
Title: "Misfiled",
ProjectID: "inbox",
})
if err != nil {
t.Fatal(err)
}
moved, err := store.Move(created.ID, "client")
if err != nil {
t.Fatal(err)
}
if moved.ProjectID != "client" {
t.Fatalf("ProjectID = %q", moved.ProjectID)
}
from, err := store.List("inbox")
if err != nil || len(from) != 0 {
t.Fatalf("inbox after move: %v len=%d", err, len(from))
}
to, err := store.List("client")
if err != nil || len(to) != 1 || to[0].ID != created.ID {
t.Fatalf("client after move: %v %+v", err, to)
}
}