package commands import ( "context" "database/sql" "errors" "testing" "git.tjp.lol/punchcard/internal/queries" ) func TestOutCommand(t *testing.T) { tests := []struct { name string setupTimeEntry bool args []string expectError bool expectedOutput string }{ { name: "stop active timer", setupTimeEntry: true, args: []string{"out"}, expectError: false, expectedOutput: "Timer stopped. Session duration:", }, { name: "no active timer", setupTimeEntry: false, args: []string{"out"}, expectError: true, }, { name: "out command with arguments should fail", setupTimeEntry: false, args: []string{"out", "extra"}, expectError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Setup fresh database for each test q, cleanup := setupTestDB(t) defer cleanup() // Setup time entry if needed if tt.setupTimeEntry { // Create a test client first client, err := q.CreateClient(context.Background(), queries.CreateClientParams{ Name: "TestClient", Email: sql.NullString{String: "test@example.com", Valid: true}, }) if err != nil { t.Fatalf("Failed to setup test client: %v", err) } // Create active time entry _, err = q.CreateTimeEntry(context.Background(), queries.CreateTimeEntryParams{ Description: sql.NullString{String: "Test work", Valid: true}, ClientID: client.ID, ProjectID: sql.NullInt64{}, }) if err != nil { t.Fatalf("Failed to setup test time entry: %v", err) } } // Execute command output, err := executeCommandWithDB(t, q, tt.args...) // Check error expectation if tt.expectError { if err == nil { t.Errorf("Expected error but got none") } return } if err != nil { t.Errorf("Unexpected error: %v", err) return } // Check output contains expected text if tt.expectedOutput != "" { if len(output) == 0 || output[:len(tt.expectedOutput)] != tt.expectedOutput { t.Errorf("Expected output to start with %q, got %q", tt.expectedOutput, output) } } // If we set up a time entry, verify it was stopped if tt.setupTimeEntry { // Try to get active time entry - should return no rows _, err := q.GetActiveTimeEntry(context.Background()) if !errors.Is(err, sql.ErrNoRows) { t.Errorf("Expected no active time entry after stopping, but got: %v", err) } // Verify the time entry was updated with an end_time // We can't directly query by ID with the current queries, but we can check that no active entries exist } }) } } func TestOutCommandErrorType(t *testing.T) { // Test that the specific ErrNoActiveTimer error is returned q, cleanup := setupTestDB(t) defer cleanup() // Execute out command with no active timer _, err := executeCommandWithDB(t, q, "out") if err == nil { t.Fatal("Expected error but got none") } // Check that it's specifically the ErrNoActiveTimer error if !errors.Is(err, ErrNoActiveTimer) { t.Errorf("Expected ErrNoActiveTimer, got: %v", err) } }