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) } } }) } }