//go:build test // +build test package admin import ( "net/http" "testing" "crussell/testutils" ) func intPtr(i int) *int { return &i } func float64Ptr(f float64) *float64 { return &f } func boolPtr(b bool) *bool { return &b } // TestGetBusinessSettings verifies that GET /api/admin/settings returns the // current business settings row. func TestGetBusinessSettings(t *testing.T) { ctx, tx := testutils.SetupTestTx(t) _, err := tx.Exec(ctx, `UPDATE business_settings SET business_name = 'Test Salon', business_address = '123 Test St', currency_code = 'GBP', gift_card_expiry_months = 12, voucher_type = 'SPV'`) if err != nil { t.Fatalf("failed to seed business settings: %v", err) } handler := http.HandlerFunc(GetBusinessSettings) w := makeAdminRequest(handler, "GET", "/api/admin/settings", nil, ctx) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var s BusinessSettings if err := parseResponseBody(w, &s); err != nil { t.Fatalf("failed to parse response: %v", err) } if s.BusinessName != "Test Salon" { t.Errorf("expected BusinessName 'Test Salon', got '%s'", s.BusinessName) } if s.BusinessAddress != "123 Test St" { t.Errorf("expected BusinessAddress '123 Test St', got '%s'", s.BusinessAddress) } if s.CurrencyCode != "GBP" { t.Errorf("expected CurrencyCode 'GBP', got '%s'", s.CurrencyCode) } if s.GiftCardExpiryMonths != 12 { t.Errorf("expected GiftCardExpiryMonths 12, got %d", s.GiftCardExpiryMonths) } if s.VoucherType != "SPV" { t.Errorf("expected VoucherType 'SPV', got '%s'", s.VoucherType) } } // TestUpdateBusinessSettings verifies that updating a single field via // PUT /api/admin/settings returns 200 with the updated settings. func TestUpdateBusinessSettings(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ BusinessName: stringPtr("Updated Salon Name"), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var s BusinessSettings if err := parseResponseBody(w, &s); err != nil { t.Fatalf("failed to parse response: %v", err) } if s.BusinessName != "Updated Salon Name" { t.Errorf("expected BusinessName 'Updated Salon Name', got '%s'", s.BusinessName) } } // TestUpdateBusinessSettings_MultipleFields verifies that updating several // fields at once works correctly. func TestUpdateBusinessSettings_MultipleFields(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ BusinessName: stringPtr("Multi Update Salon"), BusinessAddress: stringPtr("456 New St"), GiftCardExpiryMonths: intPtr(24), VoucherType: stringPtr("MPV"), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var s BusinessSettings if err := parseResponseBody(w, &s); err != nil { t.Fatalf("failed to parse response: %v", err) } if s.BusinessName != "Multi Update Salon" { t.Errorf("expected BusinessName 'Multi Update Salon', got '%s'", s.BusinessName) } if s.BusinessAddress != "456 New St" { t.Errorf("expected BusinessAddress '456 New St', got '%s'", s.BusinessAddress) } if s.GiftCardExpiryMonths != 24 { t.Errorf("expected GiftCardExpiryMonths 24, got %d", s.GiftCardExpiryMonths) } if s.VoucherType != "MPV" { t.Errorf("expected VoucherType 'MPV', got '%s'", s.VoucherType) } } // TestUpdateBusinessSettings_InvalidVoucherType verifies that an invalid // voucher_type value returns 400. func TestUpdateBusinessSettings_InvalidVoucherType(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ VoucherType: stringPtr("INVALID"), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusBadRequest { t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String()) } if w.Body.String() != "voucher_type must be 'SPV' or 'MPV'\n" { t.Errorf("unexpected error message: %s", w.Body.String()) } } // TestUpdateBusinessSettings_NegativeExpiryMonths verifies that a // gift_card_expiry_months value less than 1 returns 400. func TestUpdateBusinessSettings_NegativeExpiryMonths(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ GiftCardExpiryMonths: intPtr(0), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusBadRequest { t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String()) } if w.Body.String() != "gift_card_expiry_months must be at least 1\n" { t.Errorf("unexpected error message: %s", w.Body.String()) } } // TestUpdateBusinessSettings_InvalidVATRate verifies that a default_vat_rate // outside the 0-100 range returns 400. func TestUpdateBusinessSettings_InvalidVATRate(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ DefaultVATRate: float64Ptr(-1), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusBadRequest { t.Errorf("expected status 400 for negative rate, got %d. body: %s", w.Code, w.Body.String()) } if w.Body.String() != "default_vat_rate must be between 0 and 100\n" { t.Errorf("unexpected error message: %s", w.Body.String()) } body2 := UpdateBusinessSettingsRequest{ DefaultVATRate: float64Ptr(101), } w2 := makeAdminRequest(handler, "PUT", "/api/admin/settings", body2, ctx) if w2.Code != http.StatusBadRequest { t.Errorf("expected status 400 for rate > 100, got %d. body: %s", w2.Code, w2.Body.String()) } if w2.Body.String() != "default_vat_rate must be between 0 and 100\n" { t.Errorf("unexpected error message: %s", w2.Body.String()) } } // TestUpdateBusinessSettings_NoFields verifies that an empty request body // (no fields to update) returns 400. func TestUpdateBusinessSettings_NoFields(t *testing.T) { ctx, _ := testutils.SetupTestTx(t) handler := http.HandlerFunc(UpdateBusinessSettings) w := makeAdminRequest(handler, "PUT", "/api/admin/settings", UpdateBusinessSettingsRequest{}, ctx) if w.Code != http.StatusBadRequest { t.Errorf("expected status 400, got %d. body: %s", w.Code, w.Body.String()) } if w.Body.String() != "No fields to update\n" { t.Errorf("unexpected error message: %s", w.Body.String()) } } // TestUpdateBusinessSettings_PartialUpdate verifies that updating a single field // leaves other fields unchanged. func TestUpdateBusinessSettings_PartialUpdate(t *testing.T) { ctx, tx := testutils.SetupTestTx(t) _, err := tx.Exec(ctx, `UPDATE business_settings SET business_name = 'Test Salon', business_address = '123 Test St', currency_code = 'GBP', gift_card_expiry_months = 12, voucher_type = 'SPV'`) if err != nil { t.Fatalf("failed to seed business settings: %v", err) } handler := http.HandlerFunc(UpdateBusinessSettings) body := UpdateBusinessSettingsRequest{ GiftCardExpiryMonths: intPtr(36), } w := makeAdminRequest(handler, "PUT", "/api/admin/settings", body, ctx) if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d. body: %s", w.Code, w.Body.String()) } var s BusinessSettings if err := parseResponseBody(w, &s); err != nil { t.Fatalf("failed to parse response: %v", err) } if s.GiftCardExpiryMonths != 36 { t.Errorf("expected GiftCardExpiryMonths 36, got %d", s.GiftCardExpiryMonths) } if s.BusinessName != "Test Salon" { t.Errorf("expected BusinessName 'Test Salon' (unchanged), got '%s'", s.BusinessName) } if s.BusinessAddress != "123 Test St" { t.Errorf("expected BusinessAddress '123 Test St' (unchanged), got '%s'", s.BusinessAddress) } if s.VoucherType != "SPV" { t.Errorf("expected VoucherType 'SPV' (unchanged), got '%s'", s.VoucherType) } if s.CurrencyCode != "GBP" { t.Errorf("expected CurrencyCode 'GBP' (unchanged), got '%s'", s.CurrencyCode) } }