This commit is contained in:
2026-02-23 01:19:53 +00:00
parent df3439bd70
commit 89d848ee72
2 changed files with 202 additions and 9 deletions
+123 -4
View File
@@ -101,6 +101,121 @@ npm run dev # Development server
SabreDAV is bundled with PHPFPM and Composer. The Docker image installs dependencies automatically during the container startup. SabreDAV is bundled with PHPFPM and Composer. The Docker image installs dependencies automatically during the container startup.
## 🧪 Testing
### Test Infrastructure
Crussell has a comprehensive Go testing infrastructure located in `backend/testutils/`:
| Component | File | Description |
|-----------|------|-------------|
| **Database** | `testutils/testdb/testdb.go` | PostgreSQL test pool, migrations, table truncation |
| **HTTP Helpers** | `testutils/helpers.go` | Request builders, auth helpers, assertions |
| **JWT** | `testutils/jwt/jwt.go` | Test token generation for users/admins |
| **HTTP Client** | `testutils/httptest/client.go` | REST client wrapper with auth support |
| **Fixtures** | `testutils/fixtures/fixtures.go` | Factory functions for test data |
| **Validators** | `internal/validators/validators.go` | ID validation utilities |
### Test Files
The project includes 16 test files covering all major handlers:
```
backend/
├── bookings.test # Main booking integration tests
├── portfolio.test # Portfolio system tests
├── handlers/
│ ├── admin/
│ │ ├── bookings_test.go # Admin booking management
│ │ ├── today_test.go # Today's view
│ │ ├── users_test.go # User management
│ │ └── services_test.go # Service CRUD
│ ├── auth/
│ │ └── auth_test.go # Authentication
│ ├── bookings/
│ │ └── bookings_test.go # User booking flow
│ ├── portfolio/
│ │ └── images_test.go # Image upload/management
│ ├── scheduling/
│ │ └── scheduling_test.go # Availability logic
│ ├── services/
│ │ └── services_test.go # Service eligibility
│ ├── user/
│ │ └── profile_test.go # User profile
│ └── handlers_test.go # Common handler tests
```
### Running Tests
```bash
cd backend
# Run all tests
go test ./...
# Run with verbose output
go test -v ./...
# Run specific test file
go test -v ./handlers/bookings
# Run tests matching pattern
go test -v -run "TestBooking" ./...
```
### Test Database Setup
Tests use a dedicated PostgreSQL database. Set the connection string via:
```bash
export TEST_DB_DSN="postgres://user:pass@localhost:5432/crussell_test?sslmode=disable"
go test ./...
```
Default DSN: `postgres://myuser:mypassword@localhost:5432/crussell_test?sslmode=disable`
### Test Utilities Usage
```go
import (
"crussell/testutils"
"crussell/testutils/testdb"
"crussell/testutils/fixtures"
"crussell/testutils/jwt"
)
func TestMyHandler(t *testing.T) {
// Setup test database
cleanup := testutils.SetupTestDB(t)
defer cleanup()
// Create test data
adminID, _ := fixtures.CreateTestAdminUser(db.DB)
userID, _ := fixtures.CreateTestUser(db.DB)
serviceID, _ := fixtures.CreateTestService(db.DB)
// Generate tokens
adminToken := jwt.GenerateAdminToken()
userToken := jwt.GenerateUserToken(userID)
// Make authenticated requests
w := testutils.MakeAdminRequest(router, "GET", "/api/admin/bookings", nil, adminID)
w := testutils.MakeUserRequest(router, "GET", "/api/bookings", nil, userID)
// Assert results
testutils.AssertStatusCode(t, w, http.StatusOK)
testutils.AssertJSONResponse(t, w, &response)
}
```
### Test Conventions
- All test files use `//go:build test` build tag
- Database is migrated fresh per test run via `testdb.Migrate()`
- Tables are truncated between tests via `testdb.TruncateTables()`
- Test tokens use a fixed secret: `test-secret-key-for-testing-only`
- Fixtures auto-generate unique emails to avoid conflicts
## 📂 Environment Variables ## 📂 Environment Variables
| Variable | Purpose | Example | | Variable | Purpose | Example |
@@ -176,8 +291,11 @@ docker compose exec backend sh
| Image Metadata Stripping | ✅ | ❌ | EXIF/GPS stripped on upload via `imaging` library | | Image Metadata Stripping | ✅ | ❌ | EXIF/GPS stripped on upload via `imaging` library |
| Profile Pictures | ✅ | ✅ | Upload to separate bucket, cropper, circular display, CalDAV sync | | Profile Pictures | ✅ | ✅ | Upload to separate bucket, cropper, circular display, CalDAV sync |
| Auto-Booking Status | ✅ | ✅ | Auto-transition: confirmed → in_progress → completed based on time | | Auto-Booking Status | ✅ | ✅ | Auto-transition: confirmed → in_progress → completed based on time |
| Simplified Deposits | ✅ | | `deposits_required` INT on users table (3 default), reduces on payment | | Simplified Deposits | ✅ | ⚠️ | `deposits_required` INT on users table (3 default), 48h notice, reduces on payment |
| Contact Page | ✅ | ✅ | Dynamic data from first admin user via `/api/contact` endpoint | | Contact Page | ✅ | ✅ | Dynamic data from first admin user via `/api/contact` endpoint |
| Email Verification | ✅ | ❌ | Verification codes, generate/check endpoints |
| Calendar Export | ✅ | ⚠️ | ICS download endpoint, Add to Calendar button (backend complete) |
| Loyalty Stamps | ✅ | ✅ | Backend complete, displayed in account page |
### ⚠️ Partially Complete ### ⚠️ Partially Complete
@@ -352,17 +470,18 @@ grep -rn "console\.log" frontend/src/ --include="*.svelte" --include="*.ts"
|------|--------| |------|--------|
| `account_role` | `user`, `admin` | | `account_role` | `user`, `admin` |
| `account_type` | `standard`, `vip`, `guest` | | `account_type` | `standard`, `vip`, `guest` |
| `booking_status` | `pending`, `confirmed`, `in_progress`, `completed`, `cancelled`, `no_show` | | `booking_status` | `pending`, `confirmed`, `in_progress`, `completed`, `client_cancelled`, `we_cancelled`, `re-schedule`, `no_show`, `no_deposit` |
| `payment_type` | `deposit`, `full`, `refund` | | `payment_type` | `deposit`, `full`, `refund` |
| `payment_method` | `cash`, `card`, `bank_transfer`, `square` | | `payment_method` | `cash`, `card`, `bank_transfer`, `square` |
| `payment_status` | `pending`, `completed`, `failed`, `refunded` | | `payment_status` | `pending`, `completed`, `failed`, `refunded` |
| `admin_notification_reason` | `pending_booking`, `cancelled_booking`, `rescheduled_booking`, `1_week_no_pay`, `1_month_no_pay`, `affiliate_claim` | | `admin_notification_reason` | `pending_booking`, `cancelled_booking`, `rescheduled_booking`, `1_week_no_pay`, `1_month_no_pay`, `affiliate_claim`, `late_cancellation`, `no_deposit`, `deposit_paid` |
### Key Tables ### Key Tables
| Table | Purpose | | Table | Purpose |
|-------|---------| |-------|--------|
| `users` | Customer and admin accounts | | `users` | Customer and admin accounts |
| `verification_codes` | Email verification and password reset codes |
| `services` | Salon service catalog | | `services` | Salon service catalog |
| `bookings` | Appointment records | | `bookings` | Appointment records |
| `booking_services` | Services per booking (many-to-many) | | `booking_services` | Services per booking (many-to-many) |
+79 -5
View File
@@ -108,11 +108,10 @@
- [x] Book (`/book`) - Full wizard with service selection, date/time, customer details - [x] Book (`/book`) - Full wizard with service selection, date/time, customer details
- [x] Portfolio (`/portfolio`) - S3/R2 storage with tag filtering, category filters, pagination, ?img= featured image, admin upload - [x] Portfolio (`/portfolio`) - S3/R2 storage with tag filtering, category filters, pagination, ?img= featured image, admin upload
- [x] Today (`/today`) - Admin only, real-time schedule view with auto-status transitions - [x] Today (`/today`) - Admin only, real-time schedule view with auto-status transitions
- [x] Schedule (`/schedule`) - User's upcoming bookings with .ics export - [x] Schedule (`/schedule`) - User's upcoming bookings with .ics export and calendar download button
- [x] Account (`/account`) - Profile management, profile picture upload with cropper - [x] Account (`/account`) - Profile management, profile picture upload with cropper, loyalty stamps display
- [x] Login (`/login`) - [x] Login (`/login`)
- [x] Manage (`/manage`) - [x] Manage (`/manage`)
- [x] Manage (`/manage`)
#### Admin Dashboard (`/admin`) #### Admin Dashboard (`/admin`)
- [x] Auth guard with role check - [x] Auth guard with role check
@@ -171,6 +170,74 @@
--- ---
## Testing Infrastructure
Crussell has a comprehensive Go testing infrastructure located in `backend/testutils/`:
### Test Utilities
| Component | File | Description |
|-----------|------|-------------|
| **Database** | `testutils/testdb/testdb.go` | PostgreSQL test pool, migrations, table truncation |
| **HTTP Helpers** | `testutils/helpers.go` | Request builders, auth helpers, assertions |
| **JWT** | `testutils/jwt/jwt.go` | Test token generation for users/admins |
| **HTTP Client** | `testutils/httptest/client.go` | REST client wrapper with auth support |
| **Fixtures** | `testutils/fixtures/fixtures.go` | Factory functions for test data (users, services, bookings) |
| **Validators** | `internal/validators/validators.go` | ID validation utilities |
### Test Files
16 test files covering all major handlers:
```
backend/
├── bookings.test # Main booking integration tests
├── portfolio.test # Portfolio system tests
├── handlers/
│ ├── admin/
│ │ ├── bookings_test.go # Admin booking management
│ │ ├── today_test.go # Today's view
│ │ ├── users_test.go # User management
│ │ └── services_test.go # Service CRUD
│ ├── auth/
│ │ └── auth_test.go # Authentication
│ ├── bookings/
│ │ └── bookings_test.go # User booking flow
│ ├── portfolio/
│ │ └── images_test.go # Image upload/management
│ ├── scheduling/
│ │ └── scheduling_test.go # Availability logic
│ ├── services/
│ │ └── services_test.go # Service eligibility
│ ├── user/
│ │ └── profile_test.go # User profile
│ └── handlers_test.go # Common handler tests
```
### Running Tests
```bash
cd backend
go test ./...
go test -v ./handlers/bookings
go test -v -run "TestBooking" ./...
```
### Test Database
- DSN: `postgres://myuser:mypassword@localhost:5432/crussell_test?sslmode=disable`
- Override: `export TEST_DB_DSN="..."`
- Uses `testdb.Migrate()` to run init-script.sql
- Tables truncated between tests via `testdb.TruncateTables()`
### Conventions
- All test files use `//go:build test` build tag
- Test tokens use fixed secret: `test-secret-key-for-testing-only`
- Fixtures auto-generate unique emails to avoid conflicts
---
## Current Architecture ## Current Architecture
### Overview Diagram ### Overview Diagram
@@ -234,6 +301,9 @@ flowchart TD
| GET | `/api/services/eligible-for/{user_id}` | List services filtered by user's age and patch test status (admin only) | | GET | `/api/services/eligible-for/{user_id}` | List services filtered by user's age and patch test status (admin only) |
| POST | `/api/register` | Create new user account | | POST | `/api/register` | Create new user account |
| POST | `/api/login` | Authenticate and receive JWT | | POST | `/api/login` | Authenticate and receive JWT |
| POST | `/api/verify/generate` | Generate email verification or password reset code |
| POST | `/api/verify/check` | Verify code (email verification or password reset) |
| GET | `/api/contact` | Get business contact info (from first admin user) |
| GET | `/api/scheduling/default-hours` | Get weekly default hours | | GET | `/api/scheduling/default-hours` | Get weekly default hours |
| GET | `/api/scheduling/exceptional-groups` | List holiday/special hour groups | | GET | `/api/scheduling/exceptional-groups` | List holiday/special hour groups |
| GET | `/api/scheduling/working-hours` | Get merged working hours for date range | | GET | `/api/scheduling/working-hours` | Get merged working hours for date range |
@@ -245,11 +315,13 @@ flowchart TD
|--------|------|-------------| |--------|------|-------------|
| GET | `/api/user/profile` | Get current user profile | | GET | `/api/user/profile` | Get current user profile |
| PUT | `/api/user/profile` | Update profile | | PUT | `/api/user/profile` | Update profile |
| POST | `/api/user/profile-picture` | Upload profile picture with cropper |
| DELETE | `/api/user/account` | Delete account (GDPR) | | DELETE | `/api/user/account` | Delete account (GDPR) |
| GET | `/api/user/loyalty` | Get loyalty stamp count | | GET | `/api/user/loyalty` | Get loyalty stamp count |
| GET | `/api/bookings` | List user's bookings | | GET | `/api/bookings` | List user's bookings |
| POST | `/api/bookings` | Create booking | | POST | `/api/bookings` | Create booking |
| GET | `/api/bookings/{id}` | Get specific booking | | GET | `/api/bookings/{id}` | Get specific booking |
| GET | `/api/bookings/{id}/calendar` | Download ICS calendar file |
| PUT | `/api/bookings/{id}` | Update booking | | PUT | `/api/bookings/{id}` | Update booking |
| DELETE | `/api/bookings/{id}` | Cancel booking | | DELETE | `/api/bookings/{id}` | Cancel booking |
@@ -310,11 +382,12 @@ flowchart TD
```sql ```sql
account_role: unverified_email | verified_email | admin | guest | affiliate account_role: unverified_email | verified_email | admin | guest | affiliate
account_type: email | google | microsoft | facebook | guest account_type: email | google | microsoft | facebook | guest
booking_status: pending | confirmed | in_progress | completed | client_cancelled | we_cancelled | re-schedule | no_show booking_status: pending | confirmed | in_progress | completed | client_cancelled | we_cancelled | re-schedule | no_show | no_deposit
verification_purpose: email_verify | password_reset
payment_type: deposit | full | tip | balance | partial payment_type: deposit | full | tip | balance | partial
payment_method: online_square | in_person_card | cash | giftcard | discount payment_method: online_square | in_person_card | cash | giftcard | discount
payment_status: pending | completed | failed | refunded payment_status: pending | completed | failed | refunded
admin_notification_reason: pending_booking | cancelled_booking | rescheduled_booking | 1_week_no_pay | 1_month_no_pay | affiliate_claim admin_notification_reason: pending_booking | cancelled_booking | rescheduled_booking | 1_week_no_pay | 1_month_no_pay | affiliate_claim | late_cancellation | no_deposit | deposit_paid
``` ```
**Suggested additional `admin_notification_reason` values:** **Suggested additional `admin_notification_reason` values:**
@@ -333,6 +406,7 @@ admin_notification_reason: pending_booking | cancelled_booking | rescheduled_boo
| Table | Purpose | | Table | Purpose |
|-------|---------| |-------|---------|
| `users` | User accounts with profile data | | `users` | User accounts with profile data |
| `verification_codes` | Email verification and password reset codes |
| `user_social_logins` | Social auth provider links | | `user_social_logins` | Social auth provider links |
| `services` | Service offerings | | `services` | Service offerings |
| `user_service_patch_tests` | Patch test tracking | | `user_service_patch_tests` | Patch test tracking |