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 2a281596f9d6660d4ecd8152b65c6add2959d05c 445 lines 12 kB view raw
1package main 2 3import ( 4 "context" 5 "strings" 6 "testing" 7 8 "github.com/stormlightlabs/noteleaf/internal/handlers" 9) 10 11func createTestPublicationHandler(t *testing.T) (*handlers.PublicationHandler, func()) { 12 cleanup := setupCommandTest(t) 13 handler, err := handlers.NewPublicationHandler() 14 if err != nil { 15 cleanup() 16 t.Fatalf("Failed to create test publication handler: %v", err) 17 } 18 return handler, func() { 19 handler.Close() 20 cleanup() 21 } 22} 23 24func TestPublicationCommand(t *testing.T) { 25 t.Run("CommandGroup Interface", func(t *testing.T) { 26 handler, cleanup := createTestPublicationHandler(t) 27 defer cleanup() 28 29 var _ CommandGroup = NewPublicationCommand(handler) 30 }) 31 32 t.Run("Create", func(t *testing.T) { 33 t.Run("creates command with correct structure", func(t *testing.T) { 34 handler, cleanup := createTestPublicationHandler(t) 35 defer cleanup() 36 37 cmd := NewPublicationCommand(handler).Create() 38 39 if cmd == nil { 40 t.Fatal("Create returned nil") 41 } 42 if cmd.Use != "pub" { 43 t.Errorf("Expected Use to be 'pub', got '%s'", cmd.Use) 44 } 45 if cmd.Short != "Manage leaflet publication sync" { 46 t.Errorf("Expected Short to be 'Manage leaflet publication sync', got '%s'", cmd.Short) 47 } 48 if !cmd.HasSubCommands() { 49 t.Error("Expected command to have subcommands") 50 } 51 }) 52 53 t.Run("has all expected subcommands", func(t *testing.T) { 54 handler, cleanup := createTestPublicationHandler(t) 55 defer cleanup() 56 57 cmd := NewPublicationCommand(handler).Create() 58 subcommands := cmd.Commands() 59 subcommandNames := make([]string, len(subcommands)) 60 for i, subcmd := range subcommands { 61 subcommandNames[i] = subcmd.Use 62 } 63 64 expectedSubcommands := []string{ 65 "auth [handle]", 66 "pull", 67 "list [--published|--draft|--all]", 68 "status", 69 "post [note-id]", 70 "patch [note-id]", 71 } 72 73 for _, expected := range expectedSubcommands { 74 if !findSubcommand(subcommandNames, expected) { 75 t.Errorf("Expected subcommand '%s' not found in %v", expected, subcommandNames) 76 } 77 } 78 }) 79 }) 80 81 t.Run("Status Command", func(t *testing.T) { 82 t.Run("shows not authenticated initially", func(t *testing.T) { 83 handler, cleanup := createTestPublicationHandler(t) 84 defer cleanup() 85 86 cmd := NewPublicationCommand(handler).Create() 87 cmd.SetArgs([]string{"status"}) 88 err := cmd.Execute() 89 90 if err != nil { 91 t.Errorf("status command failed: %v", err) 92 } 93 }) 94 }) 95 96 t.Run("List Command", func(t *testing.T) { 97 t.Run("default filter", func(t *testing.T) { 98 handler, cleanup := createTestPublicationHandler(t) 99 defer cleanup() 100 101 cmd := NewPublicationCommand(handler).Create() 102 cmd.SetArgs([]string{"list"}) 103 err := cmd.Execute() 104 105 if err != nil { 106 t.Errorf("list command failed: %v", err) 107 } 108 }) 109 110 t.Run("with published flag", func(t *testing.T) { 111 handler, cleanup := createTestPublicationHandler(t) 112 defer cleanup() 113 114 cmd := NewPublicationCommand(handler).Create() 115 cmd.SetArgs([]string{"list", "--published"}) 116 err := cmd.Execute() 117 118 if err != nil { 119 t.Errorf("list --published failed: %v", err) 120 } 121 }) 122 123 t.Run("with draft flag", func(t *testing.T) { 124 handler, cleanup := createTestPublicationHandler(t) 125 defer cleanup() 126 127 cmd := NewPublicationCommand(handler).Create() 128 cmd.SetArgs([]string{"list", "--draft"}) 129 err := cmd.Execute() 130 131 if err != nil { 132 t.Errorf("list --draft failed: %v", err) 133 } 134 }) 135 136 t.Run("with all flag", func(t *testing.T) { 137 handler, cleanup := createTestPublicationHandler(t) 138 defer cleanup() 139 140 cmd := NewPublicationCommand(handler).Create() 141 cmd.SetArgs([]string{"list", "--all"}) 142 err := cmd.Execute() 143 144 if err != nil { 145 t.Errorf("list --all failed: %v", err) 146 } 147 }) 148 149 t.Run("published takes precedence over draft", func(t *testing.T) { 150 handler, cleanup := createTestPublicationHandler(t) 151 defer cleanup() 152 153 cmd := NewPublicationCommand(handler).Create() 154 cmd.SetArgs([]string{"list", "--published", "--draft"}) 155 err := cmd.Execute() 156 157 if err != nil { 158 t.Errorf("list with multiple flags failed: %v", err) 159 } 160 }) 161 }) 162 163 t.Run("Pull Command", func(t *testing.T) { 164 t.Run("fails when not authenticated", func(t *testing.T) { 165 handler, cleanup := createTestPublicationHandler(t) 166 defer cleanup() 167 168 cmd := NewPublicationCommand(handler).Create() 169 cmd.SetArgs([]string{"pull"}) 170 err := cmd.Execute() 171 172 if err == nil { 173 t.Error("Expected pull to fail when not authenticated") 174 } 175 if err != nil && !strings.Contains(err.Error(), "not authenticated") { 176 t.Errorf("Expected 'not authenticated' error, got: %v", err) 177 } 178 }) 179 }) 180 181 t.Run("Post Command", func(t *testing.T) { 182 t.Run("requires note ID argument", func(t *testing.T) { 183 handler, cleanup := createTestPublicationHandler(t) 184 defer cleanup() 185 186 cmd := NewPublicationCommand(handler).Create() 187 cmd.SetArgs([]string{"post"}) 188 err := cmd.Execute() 189 190 if err == nil { 191 t.Error("Expected error for missing note ID") 192 } 193 }) 194 195 t.Run("rejects invalid note ID", func(t *testing.T) { 196 handler, cleanup := createTestPublicationHandler(t) 197 defer cleanup() 198 199 cmd := NewPublicationCommand(handler).Create() 200 cmd.SetArgs([]string{"post", "not-a-number"}) 201 err := cmd.Execute() 202 203 if err == nil { 204 t.Error("Expected error for invalid note ID") 205 } 206 if !strings.Contains(err.Error(), "invalid note ID") { 207 t.Errorf("Expected 'invalid note ID' error, got: %v", err) 208 } 209 }) 210 211 t.Run("fails when not authenticated", func(t *testing.T) { 212 handler, cleanup := createTestPublicationHandler(t) 213 defer cleanup() 214 215 cmd := NewPublicationCommand(handler).Create() 216 cmd.SetArgs([]string{"post", "123"}) 217 err := cmd.Execute() 218 219 if err == nil { 220 t.Error("Expected post to fail when not authenticated") 221 } 222 if !strings.Contains(err.Error(), "not authenticated") { 223 t.Errorf("Expected 'not authenticated' error, got: %v", err) 224 } 225 }) 226 227 t.Run("preview mode fails when not authenticated", func(t *testing.T) { 228 handler, cleanup := createTestPublicationHandler(t) 229 defer cleanup() 230 231 cmd := NewPublicationCommand(handler).Create() 232 cmd.SetArgs([]string{"post", "123", "--preview"}) 233 err := cmd.Execute() 234 235 if err == nil { 236 t.Error("Expected post --preview to fail when not authenticated") 237 } 238 if !strings.Contains(err.Error(), "not authenticated") { 239 t.Errorf("Expected 'not authenticated' error, got: %v", err) 240 } 241 }) 242 243 t.Run("validate mode fails when not authenticated", func(t *testing.T) { 244 handler, cleanup := createTestPublicationHandler(t) 245 defer cleanup() 246 247 cmd := NewPublicationCommand(handler).Create() 248 cmd.SetArgs([]string{"post", "123", "--validate"}) 249 err := cmd.Execute() 250 251 if err == nil { 252 t.Error("Expected post --validate to fail when not authenticated") 253 } 254 if !strings.Contains(err.Error(), "not authenticated") { 255 t.Errorf("Expected 'not authenticated' error, got: %v", err) 256 } 257 }) 258 259 t.Run("accepts draft flag", func(t *testing.T) { 260 handler, cleanup := createTestPublicationHandler(t) 261 defer cleanup() 262 263 cmd := NewPublicationCommand(handler).Create() 264 cmd.SetArgs([]string{"post", "123", "--draft"}) 265 err := cmd.Execute() 266 267 if err == nil { 268 t.Error("Expected post --draft to fail when not authenticated") 269 } 270 if !strings.Contains(err.Error(), "not authenticated") { 271 t.Errorf("Expected 'not authenticated' error, got: %v", err) 272 } 273 }) 274 275 t.Run("accepts preview and draft flags together", func(t *testing.T) { 276 handler, cleanup := createTestPublicationHandler(t) 277 defer cleanup() 278 279 cmd := NewPublicationCommand(handler).Create() 280 cmd.SetArgs([]string{"post", "123", "--preview", "--draft"}) 281 err := cmd.Execute() 282 283 if err == nil { 284 t.Error("Expected post --preview --draft to fail when not authenticated") 285 } 286 if !strings.Contains(err.Error(), "not authenticated") { 287 t.Errorf("Expected 'not authenticated' error, got: %v", err) 288 } 289 }) 290 }) 291 292 t.Run("Patch Command", func(t *testing.T) { 293 t.Run("requires note ID argument", func(t *testing.T) { 294 handler, cleanup := createTestPublicationHandler(t) 295 defer cleanup() 296 297 cmd := NewPublicationCommand(handler).Create() 298 cmd.SetArgs([]string{"patch"}) 299 err := cmd.Execute() 300 301 if err == nil { 302 t.Error("Expected error for missing note ID") 303 } 304 }) 305 306 t.Run("rejects invalid note ID", func(t *testing.T) { 307 handler, cleanup := createTestPublicationHandler(t) 308 defer cleanup() 309 310 cmd := NewPublicationCommand(handler).Create() 311 cmd.SetArgs([]string{"patch", "not-a-number"}) 312 err := cmd.Execute() 313 314 if err == nil { 315 t.Error("Expected error for invalid note ID") 316 } 317 if !strings.Contains(err.Error(), "invalid note ID") { 318 t.Errorf("Expected 'invalid note ID' error, got: %v", err) 319 } 320 }) 321 322 t.Run("fails when not authenticated", func(t *testing.T) { 323 handler, cleanup := createTestPublicationHandler(t) 324 defer cleanup() 325 326 cmd := NewPublicationCommand(handler).Create() 327 cmd.SetArgs([]string{"patch", "123"}) 328 err := cmd.Execute() 329 330 if err == nil { 331 t.Error("Expected patch to fail when not authenticated") 332 } 333 if !strings.Contains(err.Error(), "not authenticated") { 334 t.Errorf("Expected 'not authenticated' error, got: %v", err) 335 } 336 }) 337 338 t.Run("preview mode fails when not authenticated", func(t *testing.T) { 339 handler, cleanup := createTestPublicationHandler(t) 340 defer cleanup() 341 342 cmd := NewPublicationCommand(handler).Create() 343 cmd.SetArgs([]string{"patch", "123", "--preview"}) 344 err := cmd.Execute() 345 346 if err == nil { 347 t.Error("Expected patch --preview to fail when not authenticated") 348 } 349 if !strings.Contains(err.Error(), "not authenticated") { 350 t.Errorf("Expected 'not authenticated' error, got: %v", err) 351 } 352 }) 353 354 t.Run("validate mode fails when not authenticated", func(t *testing.T) { 355 handler, cleanup := createTestPublicationHandler(t) 356 defer cleanup() 357 358 cmd := NewPublicationCommand(handler).Create() 359 cmd.SetArgs([]string{"patch", "123", "--validate"}) 360 err := cmd.Execute() 361 362 if err == nil { 363 t.Error("Expected patch --validate to fail when not authenticated") 364 } 365 if !strings.Contains(err.Error(), "not authenticated") { 366 t.Errorf("Expected 'not authenticated' error, got: %v", err) 367 } 368 }) 369 }) 370 371 t.Run("Command Help", func(t *testing.T) { 372 t.Run("root help", func(t *testing.T) { 373 handler, cleanup := createTestPublicationHandler(t) 374 defer cleanup() 375 376 cmd := NewPublicationCommand(handler).Create() 377 cmd.SetArgs([]string{"help"}) 378 err := cmd.Execute() 379 380 if err != nil { 381 t.Errorf("help command failed: %v", err) 382 } 383 }) 384 385 t.Run("auth help", func(t *testing.T) { 386 handler, cleanup := createTestPublicationHandler(t) 387 defer cleanup() 388 389 cmd := NewPublicationCommand(handler).Create() 390 cmd.SetArgs([]string{"auth", "--help"}) 391 err := cmd.Execute() 392 393 if err != nil { 394 t.Errorf("auth help failed: %v", err) 395 } 396 }) 397 }) 398 399 t.Run("Command Aliases", func(t *testing.T) { 400 t.Run("list alias ls works", func(t *testing.T) { 401 handler, cleanup := createTestPublicationHandler(t) 402 defer cleanup() 403 404 cmd := NewPublicationCommand(handler).Create() 405 cmd.SetArgs([]string{"ls"}) 406 err := cmd.Execute() 407 408 if err != nil { 409 t.Errorf("list alias 'ls' failed: %v", err) 410 } 411 }) 412 }) 413 414 t.Run("Handler Validation", func(t *testing.T) { 415 t.Run("auth validates empty handle", func(t *testing.T) { 416 handler, cleanup := createTestPublicationHandler(t) 417 defer cleanup() 418 419 ctx := context.Background() 420 err := handler.Auth(ctx, "", "password") 421 422 if err == nil { 423 t.Error("Expected error for empty handle") 424 } 425 if !strings.Contains(err.Error(), "handle is required") { 426 t.Errorf("Expected 'handle is required' error, got: %v", err) 427 } 428 }) 429 430 t.Run("auth validates empty password", func(t *testing.T) { 431 handler, cleanup := createTestPublicationHandler(t) 432 defer cleanup() 433 434 ctx := context.Background() 435 err := handler.Auth(ctx, "test.bsky.social", "") 436 437 if err == nil { 438 t.Error("Expected error for empty password") 439 } 440 if !strings.Contains(err.Error(), "password is required") { 441 t.Errorf("Expected 'password is required' error, got: %v", err) 442 } 443 }) 444 }) 445}