Skip to content

Latest commit

 

History

History
174 lines (155 loc) · 10.8 KB

File metadata and controls

174 lines (155 loc) · 10.8 KB

Backend API Test Plan (DoxiiCMS)

Scope: FastAPI backend under app/ including tenant middleware, public/admin routers, and internal management APIs. Covers endpoints mounted in app/app.py with prefixes:

  • Public: /tenant/{tenant_id}/...
  • Admin: /admin/tenant/{tenant_id}/...
  • Internal: /internal/...

Assumptions

  • Multi-tenant SQLite by default; tenants must exist (or autoload from disk) or requests return 404 via TenantMiddleware.
  • Admin and public routers expose the same domain endpoints; no auth guards on admin routes in current code.

General

  • CORS: OPTIONS /* returns 200 JSON {"message":"OK"}.
  • Root: GET / returns service/version.
  • Health: GET /health returns aggregated health.
  • OpenAPI docs: GET /docs, GET /docs/admin, GET /openapi-public.json, GET /openapi-admin.json render and filter routes appropriately.

Tenant Middleware (app/middleware/tenant.py)

  • Valid paths required: /tenant/{tenant_id}/* or /admin/tenant/{tenant_id}/* or /internal/*.
    • Invalid path shape → 400 {error: Invalid URL format}.
    • Invalid tenant id (regex/length) → 400.
    • Unregistered tenant without on-disk DB → 404 {error: Tenant database not found}.
    • Inactive tenant → 403.
    • When registered or auto-loaded from disk → request proceeds; request.state.tenant_id set.

Categories API (/{tenant_id}/categories)

  • List: GET /{tenant_id}/categories
    • Happy path: default order by sort_order; meta: total, offset, limit, count.
    • Filtering operators: eq, neq, lt, lte, gt, gte, like, ilike, in, not.in, is.null, not.is.null.
    • Ordering: order=name.asc,created_at.desc.
    • Pagination: limit, offset.
    • Field selection param select is accepted but not applied to serialization (validate no error).
    • Error: invalid numeric casts are tolerated (treated as strings), still 200.
  • Tree: GET /{tenant_id}/categories/tree
    • Returns hierarchical categories array with children arrays; optional is_active filter.
    • Timestamps formatted iso8601.
  • Create: POST /{tenant_id}/categories
    • Happy path with unique slug; optional parent_id must exist.
    • Errors: duplicate slug → 400; parent not found → 400.
  • Retrieve: GET /{tenant_id}/categories/{id}
    • 200 on exist, 404 if not.
  • Update (PUT): PUT /{tenant_id}/categories/{id}
    • Replaces all fields; duplicate slug (excluding self) → 400.
    • Parent validation: cannot be self; parent must exist; circular detection via parent chain → 400.
    • 404 if not found.
  • Patch: PATCH /{tenant_id}/categories/{id}
    • Partial fields only; duplicate slug (excluding self) → 400; parent checks as above.
  • Delete: DELETE /{tenant_id}/categories/{id}
    • Happy path: 204.
    • Errors: has children → 400; has products referencing category → 400; not found → 404.

Products API (/{tenant_id}/products)

  • List: GET /{tenant_id}/products
    • Supports same operator suite as categories across defined fields; returns {data, meta}; joined category serialization when present.
    • Verify ordering parsing and multiple fields.
    • Verify count meta accuracy with pagination.
  • Create (multipart): POST /{tenant_id}/products
    • Form parts: product_data JSON (required), images[], videos[], other_media[].
    • Validations: unique sku, unique slug; numeric/enum coercions; decimals serialized to float.
    • Media records created with media_type image|video|other; sort_order increments.
    • Errors: missing/invalid product_data JSON → 400; duplicate sku/slug → 400.
  • Retrieve: GET /{tenant_id}/products/{product_id}
    • 200 with category info if linked; 404 if not found.
  • Update (multipart, full): PUT /{tenant_id}/products/{product_id}
    • Validates slug/sku uniqueness excluding self.
    • replace_media (bool) defaults true; when true and new files provided, deletes existing media; else appends at next sort.
    • 404 when missing.
  • Patch (multipart, partial): PATCH /{tenant_id}/products/{product_id}
    • product_data optional; applies provided fields only; uniqueness checks when fields present.
    • replace_media default false; behavior like PUT for media.
  • Delete: DELETE /{tenant_id}/products/{product_id} → 204 or 404.
  • Images list: GET /{tenant_id}/products/{product_id}/images → 200 or 404 when product missing.
  • Add image: POST /{tenant_id}/products/{product_id}/images with file → 201 creates placeholder URL; 404 when product missing.
  • Unified convenience endpoints:
    • POST /{tenant_id}/products/with-images → create product + images; duplicate SKU → 400; required name/sku/slug.
    • PUT /{tenant_id}/products/{product_id}/with-images → replace images if provided; not found → 404.

Orders API (/{tenant_id}/orders)

  • List: GET /{tenant_id}/orders
    • Filters: status, payment_status, fulfillment_status (eq.), order_number (ilike.), order by created_at (asc|desc), pagination via limit/offset.
    • Response is array (not wrapped), numeric totals serialized floats.
  • Create: POST /{tenant_id}/orders
    • Requires existing customer_id; items[] each with product_id, quantity>=1, unit_price>=0.
    • Inventory handling: if track_inventory and insufficient and not continue_selling_when_out_of_stock → 400.
    • Generates order_number if blank; sets totals.
    • Errors: missing customer → 400; missing product → 400.
  • Retrieve: GET /{tenant_id}/orders/{order_id} → 200 or 404.
  • Patch: PATCH /{tenant_id}/orders/{order_id} partial update; validate customer_id if changed; 404 when missing.
  • Delete: DELETE /{tenant_id}/orders/{order_id}
    • Not allowed when status in confirmed|shipped|delivered → 400.
    • Restores inventory for canceled order items; 404 when missing.
  • Order items:
    • List: GET /{tenant_id}/orders/{order_id}/items → 200 or 404 when order missing.
    • Add: POST /{tenant_id}/orders/{order_id}/items → validates order status (pending|confirmed), product existence, inventory; updates order totals and product inventory.
    • Retrieve: GET /{tenant_id}/orders/{order_id}/items/{item_id} → 200 or 404.

Authentication API (/{tenant_id}/auth/*)

  • Register: POST /{tenant_id}/auth/register
    • Validates username/email uniqueness, email format, password length ≥ 6; creates user with default role SELLER.
    • Duplicate username/email → 400.
  • Login: POST /{tenant_id}/auth/login
    • Accepts username OR email; invalid credentials or inactive user → 401.
    • Creates active Session with token/refresh_token; trims to max 5 active sessions per user.
  • Logout: POST /{tenant_id}/auth/logout
    • Requires Authorization: Bearer <token>; revokes session if found; 401 invalid format.
  • Me: GET /{tenant_id}/auth/me
    • Requires Bearer token; session must be active and not expired; 401 invalid/expired.
  • Refresh: POST /{tenant_id}/auth/refresh?refresh_token=...
    • Requires valid active session by refresh token; rotates tokens; 401 invalid/expired.
  • Change password: POST /{tenant_id}/auth/change-password?current_password=...&new_password=...
    • Requires Bearer; validates current password; new_password length; returns success; 401/400 on errors.
  • Sessions list: GET /{tenant_id}/auth/sessions
    • Requires Bearer; returns sanitized sessions, marks is_current.
  • Revoke session: DELETE /{tenant_id}/auth/sessions/{session_id}
    • Requires Bearer; can only revoke own session; 404 if not found.

Internal Management API (/internal/database/*)

  • Create DB: POST /internal/database/?environment=development|production
    • databaseId body required (pattern/length); returns created true on first creation, false if exists; dev runs migrations best-effort.
    • Note: bearer verification currently commented out; no auth required in code.
  • Delete DB: DELETE /internal/database/{database_id}
    • Deletes SQLite file (if exists), deactivates tenant, cleans resources; returns deleted true/false.
  • Status: GET /internal/database/{database_id}/status?environment=development|production
    • Validates tenant id; dev may auto-load in registry; returns config and migration status (best-effort).
  • List configs: GET /internal/database/configs?status=&environment=
    • Returns all tenant configs with runtime info for active dev tenants.

Negative/Edge Case Matrix (Representative)

  • Tenant ID edge cases: empty, too long (>50), invalid chars, valid but missing DB.
  • Query operators: malformed (e.g., in.(), not.in.()), unknown fields ignored, invalid date formats handled as strings.
  • Product create/update: boundary values (price=0, inventory=0), large texts at max lengths, many media files (verify sort order), replace_media behavior when no files provided.
  • Orders: zero items (should fail via Pydantic min_items), large quantities and precision, mixed track_inventory scenarios.
  • Auth: invalid/missing Bearer header, expired session (simulate via model method if possible), multiple concurrent sessions revocation order.
  • Internal: attempt to create prod without POSTGRES provisioning (expect config created, no DB provisioning), delete nonexistent DB, status for nonexistent environment.

Nonexistent/Missing Endpoints (to acknowledge)

  • No /tenant/{tenant_id}/upload endpoint (frontend calls this; expect 404).
  • No /tenant/{tenant_id}/dashboard/stats, /tenant/{tenant_id}/customers/* (admin set), or orders/{id}/summary endpoints in backend.

Test Data & Setup

  • Use /internal/database to create a dev tenant before tests, or pre-seed with provided setup scripts (e.g., setup_demo.py).
  • Use deterministic tenant IDs per test run to avoid collisions (e.g., user0001_project0001).

Test Results (2025-09-14)

✅ PASSING TESTS (20/30 - 66.7%)

  • Basic endpoints: Root, Health check
  • Tenant middleware: Nonexistent tenant (404), Invalid path (400)
  • Categories API: List, Tree, Filtering, Ordering, Pagination
  • Products API: List, Filtering, Search
  • Orders API: List, Filtering
  • Auth API: Login, Get current user
  • Edge cases: Duplicate category slug, Invalid IDs
  • Internal API: List database configs

❌ FAILING TESTS (8/30) - IMPROVED FROM 10/30

  1. CORS OPTIONS - Error in test script (variable reference issue)
  2. Category Creation - Returns 201 instead of expected 200 (this is actually correct behavior)
  3. Product Creation with Media - 422 error: multipart file handling issue in test
  4. Order Creation - 422 error: product_id is None due to failed product creation
  5. Database Status - 500 error: internal API issue

✅ FIXED ISSUES

  1. Orders API - Fixed SQLAlchemy enum handling for order statuses
  2. Product Creation - Fixed multipart file upload (works without media files)
  3. React State Management - Fixed infinite re-render loop in ProductFormPage

🔧 REMAINING ISSUES TO FIX

  1. Fix multipart file upload test implementation
  2. Fix internal database status endpoint error
  3. Update test expectations for 201 status codes (they're correct)
  4. Fix CORS OPTIONS test implementation