cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
29
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 267 lines 6.9 kB view raw
1package handlers 2 3import ( 4 "context" 5 "os" 6 "path/filepath" 7 "testing" 8 9 "github.com/stormlightlabs/noteleaf/internal/repo" 10 "github.com/stormlightlabs/noteleaf/internal/store" 11) 12 13// HandlerTestSuite provides reusable test infrastructure for handlers 14type HandlerTestSuite struct { 15 t *testing.T 16 tempDir string 17 ctx context.Context 18 cleanup func() 19} 20 21// NewHandlerTestSuite creates a new test suite with isolated environment 22func NewHandlerTestSuite(t *testing.T) *HandlerTestSuite { 23 t.Helper() 24 25 tempDir, err := os.MkdirTemp("", "noteleaf-handler-suite-*") 26 if err != nil { 27 t.Fatalf("Failed to create temp dir: %v", err) 28 } 29 30 oldNoteleafConfig := os.Getenv("NOTELEAF_CONFIG") 31 oldNoteleafDataDir := os.Getenv("NOTELEAF_DATA_DIR") 32 os.Setenv("NOTELEAF_CONFIG", filepath.Join(tempDir, ".noteleaf.conf.toml")) 33 os.Setenv("NOTELEAF_DATA_DIR", tempDir) 34 35 cleanup := func() { 36 os.Setenv("NOTELEAF_CONFIG", oldNoteleafConfig) 37 os.Setenv("NOTELEAF_DATA_DIR", oldNoteleafDataDir) 38 os.RemoveAll(tempDir) 39 } 40 41 ctx := context.Background() 42 if err := Setup(ctx, []string{}); err != nil { 43 cleanup() 44 t.Fatalf("Failed to setup database: %v", err) 45 } 46 47 suite := &HandlerTestSuite{ 48 t: t, 49 tempDir: tempDir, 50 ctx: ctx, 51 cleanup: cleanup, 52 } 53 54 t.Cleanup(suite.Cleanup) 55 56 return suite 57} 58 59// Context returns the test context 60func (s *HandlerTestSuite) Context() context.Context { 61 return s.ctx 62} 63 64// TempDir returns the temporary directory for this test 65func (s *HandlerTestSuite) TempDir() string { 66 return s.tempDir 67} 68 69// Cleanup performs test cleanup 70func (s *HandlerTestSuite) Cleanup() { 71 if s.cleanup != nil { 72 s.cleanup() 73 } 74} 75 76// AssertNoError fails the test if err is not nil 77func (s *HandlerTestSuite) AssertNoError(err error, msg string) { 78 s.t.Helper() 79 if err != nil { 80 s.t.Fatalf("%s: unexpected error: %v", msg, err) 81 } 82} 83 84// AssertError fails the test if err is nil 85func (s *HandlerTestSuite) AssertError(err error, msg string) { 86 s.t.Helper() 87 if err == nil { 88 s.t.Fatalf("%s: expected error but got nil", msg) 89 } 90} 91 92// CreateTestDatabase creates an isolated test database 93func (s *HandlerTestSuite) CreateTestDatabase() (*store.Database, *repo.Repositories, error) { 94 db, err := store.NewDatabase() 95 if err != nil { 96 return nil, nil, err 97 } 98 99 repos := repo.NewRepositories(db.DB) 100 return db, repos, nil 101} 102 103// HandlerLifecycleTest tests basic handler lifecycle (create, close) 104// 105// This is a reusable test pattern for any handler implementing Closeable 106func HandlerLifecycleTest[H Closeable](t *testing.T, createHandler func() (H, error)) { 107 t.Helper() 108 109 t.Run("creates handler successfully", func(t *testing.T) { 110 handler, err := createHandler() 111 if err != nil { 112 t.Fatalf("Failed to create handler: %v", err) 113 } 114 115 if err := handler.Close(); err != nil { 116 t.Errorf("Close failed: %v", err) 117 } 118 }) 119 120 t.Run("handles close gracefully", func(t *testing.T) { 121 handler, err := createHandler() 122 if err != nil { 123 t.Fatalf("Failed to create handler: %v", err) 124 } 125 126 if err := handler.Close(); err != nil { 127 t.Errorf("First close should succeed: %v", err) 128 } 129 130 _ = handler.Close() 131 }) 132} 133 134// ViewableTest tests View functionality for handlers 135func ViewableTest[H Viewable](t *testing.T, handler H, validID, invalidID int64) { 136 t.Helper() 137 ctx := context.Background() 138 139 t.Run("views valid item", func(t *testing.T) { 140 err := handler.View(ctx, validID) 141 if err != nil { 142 t.Errorf("View with valid ID should succeed: %v", err) 143 } 144 }) 145 146 t.Run("fails with invalid ID", func(t *testing.T) { 147 err := handler.View(ctx, invalidID) 148 if err == nil { 149 t.Error("View with invalid ID should fail") 150 } 151 }) 152} 153 154// InputReaderTest tests SetInputReader functionality 155func InputReaderTest[H InputReader](t *testing.T, handler H) { 156 t.Helper() 157 158 t.Run("sets input reader", func(t *testing.T) { 159 sim := NewInputSimulator("test") 160 handler.SetInputReader(sim) 161 // If we get here without panic, the test passes 162 }) 163 164 t.Run("handles nil reader", func(t *testing.T) { 165 handler.SetInputReader(nil) 166 // If we get here without panic, the test passes 167 }) 168} 169 170// MediaHandlerTestSuite provides specialized test utilities for media handlers 171type MediaHandlerTestSuite struct { 172 *HandlerTestSuite 173} 174 175// NewMediaHandlerTestSuite creates a test suite for media handlers 176func NewMediaHandlerTestSuite(t *testing.T) *MediaHandlerTestSuite { 177 return &MediaHandlerTestSuite{ 178 HandlerTestSuite: NewHandlerTestSuite(t), 179 } 180} 181 182// TestSearchAndAdd is a reusable test pattern for SearchAndAdd operations 183func (s *MediaHandlerTestSuite) TestSearchAndAdd(handler MediaHandler, query string, shouldSucceed bool) { 184 s.t.Helper() 185 186 err := handler.SearchAndAdd(s.ctx, query, false) 187 if shouldSucceed && err != nil { 188 s.t.Errorf("SearchAndAdd should succeed: %v", err) 189 } 190 if !shouldSucceed && err == nil { 191 s.t.Error("SearchAndAdd should fail") 192 } 193} 194 195// TestList is a reusable test pattern for List operations 196func (s *MediaHandlerTestSuite) TestList(handler MediaHandler, status string) { 197 s.t.Helper() 198 199 err := handler.List(s.ctx, status) 200 if err != nil { 201 s.t.Errorf("List should succeed: %v", err) 202 } 203} 204 205// TestUpdateStatus is a reusable test pattern for UpdateStatus operations 206func (s *MediaHandlerTestSuite) TestUpdateStatus(handler MediaHandler, id, status string, shouldSucceed bool) { 207 s.t.Helper() 208 209 err := handler.UpdateStatus(s.ctx, id, status) 210 if shouldSucceed && err != nil { 211 s.t.Errorf("UpdateStatus should succeed: %v", err) 212 } 213 if !shouldSucceed && err == nil { 214 s.t.Error("UpdateStatus should fail") 215 } 216} 217 218// TestRemove is a reusable test pattern for Remove operations 219func (s *MediaHandlerTestSuite) TestRemove(handler MediaHandler, id string, shouldSucceed bool) { 220 s.t.Helper() 221 222 err := handler.Remove(s.ctx, id) 223 if shouldSucceed && err != nil { 224 s.t.Errorf("Remove should succeed: %v", err) 225 } 226 if !shouldSucceed && err == nil { 227 s.t.Error("Remove should fail") 228 } 229} 230 231// CreateHandler creates a handler with automatic cleanup 232func CreateHandler[H Closeable](t *testing.T, factory func() (H, error)) H { 233 t.Helper() 234 235 handler, err := factory() 236 if err != nil { 237 t.Fatalf("Failed to create handler: %v", err) 238 } 239 240 t.Cleanup(func() { 241 handler.Close() 242 }) 243 244 return handler 245} 246 247// AssertExists verifies that an item exists using a getter 248func AssertExists[T any](t *testing.T, getter func(context.Context, int64) (*T, error), id int64, itemType string) { 249 t.Helper() 250 251 ctx := context.Background() 252 _, err := getter(ctx, id) 253 if err != nil { 254 t.Errorf("%s %d should exist but got error: %v", itemType, id, err) 255 } 256} 257 258// AssertNotExists verifies that an item does not exist using a getter 259func AssertNotExists[T any](t *testing.T, getter func(context.Context, int64) (*T, error), id int64, itemType string) { 260 t.Helper() 261 262 ctx := context.Background() 263 _, err := getter(ctx, id) 264 if err == nil { 265 t.Errorf("%s %d should not exist but was found", itemType, id) 266 } 267}