summaryrefslogtreecommitdiff
path: root/internal/commands/out_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/commands/out_test.go')
-rw-r--r--internal/commands/out_test.go124
1 files changed, 124 insertions, 0 deletions
diff --git a/internal/commands/out_test.go b/internal/commands/out_test.go
new file mode 100644
index 0000000..aeb2359
--- /dev/null
+++ b/internal/commands/out_test.go
@@ -0,0 +1,124 @@
+package commands
+
+import (
+ "context"
+ "database/sql"
+ "errors"
+ "testing"
+
+ "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)
+ }
+}
+