diff options
Diffstat (limited to 'internal/commands/add_project_test.go')
-rw-r--r-- | internal/commands/add_project_test.go | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/internal/commands/add_project_test.go b/internal/commands/add_project_test.go new file mode 100644 index 0000000..c41bd5c --- /dev/null +++ b/internal/commands/add_project_test.go @@ -0,0 +1,198 @@ +package commands + +import ( + "context" + "database/sql" + "testing" + + "punchcard/internal/queries" +) + +func TestAddProjectCommand(t *testing.T) { + tests := []struct { + name string + setupClient bool + clientName string + clientEmail string + args []string + expectedOutput string + expectError bool + }{ + { + name: "add project with client name", + setupClient: true, + clientName: "TestCorp", + clientEmail: "test@testcorp.com", + args: []string{"add", "project", "Website Redesign", "-c", "TestCorp"}, + expectedOutput: "Created project: Website Redesign for client TestCorp (ID: 1)\n", + expectError: false, + }, + { + name: "add project with client ID", + setupClient: true, + clientName: "TechSolutions", + clientEmail: "contact@techsolutions.com", + args: []string{"add", "project", "Mobile App", "-c", "1"}, + expectedOutput: "Created project: Mobile App for client TechSolutions (ID: 1)\n", + expectError: false, + }, + { + name: "add project with nonexistent client name", + setupClient: false, + args: []string{"add", "project", "Test Project", "-c", "NonexistentClient"}, + expectError: true, + }, + { + name: "add project with nonexistent client ID", + setupClient: false, + args: []string{"add", "project", "Test Project", "-c", "999"}, + expectError: true, + }, + { + name: "add project with no arguments", + setupClient: false, + args: []string{"add", "project"}, + expectError: true, + }, + { + name: "add project with only name", + setupClient: false, + args: []string{"add", "project", "Test Project"}, + expectError: true, + }, + { + name: "add project with too many arguments", + setupClient: true, + clientName: "TestCorp", + args: []string{"add", "project", "name", "extra", "-c", "TestCorp"}, + expectError: true, + }, + { + name: "add project with billable rate", + setupClient: true, + clientName: "BillableClient", + clientEmail: "billing@client.com", + args: []string{"add", "project", "Premium Project", "-c", "BillableClient", "-r", "175.25"}, + expectedOutput: "Created project: Premium Project for client BillableClient (ID: 1)\n", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q, cleanup := setupTestDB(t) + defer cleanup() + + if tt.setupClient { + _, err := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: tt.clientName, + Email: sql.NullString{String: tt.clientEmail, Valid: tt.clientEmail != ""}, + BillableRate: sql.NullInt64{}, + }) + if err != nil { + t.Fatalf("Failed to setup test client: %v", err) + } + } + + output, err := executeCommandWithDB(t, q, tt.args...) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error but got none") + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + if output != tt.expectedOutput { + t.Errorf("Expected output %q, got %q", tt.expectedOutput, output) + } + }) + } +} + +func TestAddProjectBillableRateStorage(t *testing.T) { + tests := []struct { + name string + args []string + expectedRate *int64 // nil means NULL in database, values in cents + expectError bool + }{ + { + name: "project without billable rate stores NULL", + args: []string{"add", "project", "NoRateProject", "-c", "testclient"}, + expectedRate: nil, + expectError: false, + }, + { + name: "project with zero billable rate stores NULL", + args: []string{"add", "project", "ZeroRateProject", "-c", "testclient", "--hourly-rate", "0"}, + expectedRate: nil, + expectError: false, + }, + { + name: "project with billable rate stores value", + args: []string{"add", "project", "RateProject", "-c", "testclient", "--hourly-rate", "225.50"}, + expectedRate: func() *int64 { f := int64(22550); return &f }(), // $225.50 = 22550 cents + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q, cleanup := setupTestDB(t) + defer cleanup() + + _, err := q.CreateClient(context.Background(), queries.CreateClientParams{ + Name: "testclient", + Email: sql.NullString{}, + BillableRate: sql.NullInt64{}, + }) + if err != nil { + t.Fatalf("Failed to setup test client: %v", err) + } + + _, err = executeCommandWithDB(t, q, tt.args...) + + if tt.expectError { + if err == nil { + t.Errorf("Expected error but got none") + } + return + } + + if err != nil { + t.Errorf("Unexpected error: %v", err) + return + } + + projects, err := q.FindProject(context.Background(), queries.FindProjectParams{ + ID: 1, + Name: "", + }) + if err != nil { + t.Fatalf("Failed to query created project: %v", err) + } + if len(projects) != 1 { + t.Fatalf("Expected 1 project, got %d", len(projects)) + } + + project := projects[0] + if tt.expectedRate == nil { + if project.BillableRate.Valid { + t.Errorf("Expected NULL billable_rate, got %d", project.BillableRate.Int64) + } + } else { + if !project.BillableRate.Valid { + t.Errorf("Expected billable_rate %d, got NULL", *tt.expectedRate) + } else if project.BillableRate.Int64 != *tt.expectedRate { + t.Errorf("Expected billable_rate %d, got %d", *tt.expectedRate, project.BillableRate.Int64) + } + } + }) + } +} |