package cmd import ( "bytes" "context" "path/filepath" "strings" "testing" "github.com/lerko/nib/internal/db" "github.com/spf13/cobra" ) func testStore(t *testing.T) *db.Store { t.Helper() dbPath := filepath.Join(t.TempDir(), "test.db") t.Setenv("NIB_DB", dbPath) store, err := db.Open(dbPath) if err != nil { t.Fatal(err) } t.Cleanup(func() { store.Close() }) return store } func newCmd() *cobra.Command { c := &cobra.Command{} c.SetContext(context.Background()) return c } func captureOutput(t *testing.T, fn func()) string { t.Helper() var buf bytes.Buffer rootCmd.SetOut(&buf) defer rootCmd.SetOut(nil) fn() return buf.String() } func seedEntity(t *testing.T, store *db.Store, body string, glyph db.Glyph) *db.Entity { t.Helper() e := &db.Entity{Body: body, Glyph: glyph, Tags: []string{}} if err := store.Create(context.Background(), e); err != nil { t.Fatal(err) } return e } func TestRunAdd(t *testing.T) { testStore(t) err := runAdd(newCmd(), []string{"hello", "world"}) if err != nil { t.Fatalf("runAdd: %v", err) } } func TestRunAddWithGlyph(t *testing.T) { testStore(t) err := runAdd(newCmd(), []string{"-", "buy", "milk", "#errands"}) if err != nil { t.Fatalf("runAdd todo: %v", err) } } func TestRunAddWithTimeAnchor(t *testing.T) { testStore(t) err := runAdd(newCmd(), []string{"@", "dentist", "@14:00"}) if err != nil { t.Fatalf("runAdd event: %v", err) } } func TestRunDelete(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "to delete", db.GlyphNote) store.Close() err := runDelete(newCmd(), []string{e.ID}) if err != nil { t.Fatalf("runDelete soft: %v", err) } err = runDelete(newCmd(), []string{e.ID}) if err != nil { t.Fatalf("runDelete hard: %v", err) } } func TestRunDeleteNotFound(t *testing.T) { testStore(t) err := runDelete(newCmd(), []string{"nonexistent"}) if err == nil { t.Fatal("expected error for nonexistent id") } if !strings.Contains(err.Error(), "not_found") { t.Fatalf("expected not_found error, got: %v", err) } } func TestRunPromote(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "reusable snippet", db.GlyphNote) store.Close() err := runPromote(newCmd(), []string{e.ID, "snippet"}) if err != nil { t.Fatalf("runPromote: %v", err) } } func TestRunPromoteAlreadyPromoted(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "already a card", db.GlyphNote) store.Promote(context.Background(), e.ID, db.CardSnippet, nil) store.Close() err := runPromote(newCmd(), []string{e.ID, "snippet"}) if err == nil { t.Fatal("expected error for already promoted") } } func TestRunDemote(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "demote me", db.GlyphNote) store.Promote(context.Background(), e.ID, db.CardSnippet, nil) store.Close() err := runDemote(newCmd(), []string{e.ID}) if err != nil { t.Fatalf("runDemote: %v", err) } } func TestRunDemoteAlreadyFluid(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "already fluid", db.GlyphNote) store.Close() err := runDemote(newCmd(), []string{e.ID}) if err == nil { t.Fatal("expected error for already fluid") } } func TestRunAbsorb(t *testing.T) { store := testStore(t) target := seedEntity(t, store, "target body", db.GlyphNote) source := seedEntity(t, store, "source body", db.GlyphNote) store.Close() err := runAbsorb(newCmd(), []string{target.ID, source.ID}) if err != nil { t.Fatalf("runAbsorb: %v", err) } } func TestRunAbsorbSameEntity(t *testing.T) { store := testStore(t) e := seedEntity(t, store, "same entity", db.GlyphNote) store.Close() err := runAbsorb(newCmd(), []string{e.ID, e.ID}) if err == nil { t.Fatal("expected error for same entity absorb") } } func TestRunAbsorbCrystallizedTarget(t *testing.T) { store := testStore(t) target := seedEntity(t, store, "crystallized", db.GlyphNote) source := seedEntity(t, store, "source", db.GlyphNote) store.Promote(context.Background(), target.ID, db.CardSnippet, nil) store.Close() err := runAbsorb(newCmd(), []string{target.ID, source.ID}) if err == nil { t.Fatal("expected error for crystallized target") } } func TestRunLs(t *testing.T) { store := testStore(t) seedEntity(t, store, "recent note", db.GlyphNote) store.Close() lsTag = "" lsDate = "" lsMonth = "" lsFrom = "" lsTo = "" lsLimit = 0 lsAll = false err := runLs(newCmd(), nil) if err != nil { t.Fatalf("runLs: %v", err) } } func TestRunLsEmpty(t *testing.T) { testStore(t) lsTag = "" lsDate = "" lsMonth = "" lsFrom = "" lsTo = "" lsLimit = 0 lsAll = false err := runLs(newCmd(), nil) if err != nil { t.Fatalf("runLs empty: %v", err) } }