Files
orion/docs/proposals/test-api-deps-auth-dependencies.md
Samir Boulahtit 93731b7173 test: add 73 unit tests for app/api/deps.py auth dependencies
Cover all core authentication paths: helpers (_get_token_from_request,
_validate_user_token, _get_user_model, _validate_customer_token),
admin/store/merchant/customer auth (cookie + header + API variants),
optional auth, store permission factories, and store ownership checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:38:31 +01:00

11 KiB
Raw Blame History

Test Plan: app/api/deps.py — Authentication Dependencies

Date: 2026-02-19 Status: Planned Priority: P0 — Security-critical, zero test coverage File under test: app/api/deps.py (1,668 lines, 31 functions)

Why This File Is Critical

deps.py is the single entry point for all authentication and authorization in the application. Every protected route depends on it. It enforces:

  • Role isolation (admins can't access store routes, stores can't access admin routes)
  • Cookie path restrictions (prevent cross-context token leakage)
  • Token priority (Authorization header > cookie)
  • Platform access control for platform admins
  • Store permission checks (owner, team member, specific permissions)
  • Merchant ownership verification
  • Customer token validation with store-matching
  • Module and menu-based access control

A bug here is a security vulnerability. Zero test coverage is unacceptable.

Current State

  • Existing test coverage: None for deps.py directly
  • Related tests: tests/integration/security/test_authentication.py (3 tests — basic endpoint protection only)
  • Related tests: tests/unit/models/schema/test_auth.py (tests UserContext schema)

Test Structure

Tests will live at: tests/unit/api/test_deps.py

This follows the project convention of matching source layout (app/api/deps.pytests/unit/api/test_deps.py).

Functions To Test (31 total)

Phase 1: Helper Functions (4 functions)

These are pure/near-pure functions — easiest to test, highest value as foundation.

Function Lines What it does Test count
_get_token_from_request() 76105 Extract token from header or cookie, header takes priority 4
_validate_user_token() 108123 Validate JWT, return User model 3
_get_user_model() 126155 Load User from DB by UserContext.id, copy token attrs 3
_validate_customer_token() 10261114 Decode customer JWT, verify type/expiry/store match 7

Tests for _get_token_from_request:

  • Returns (token, "header") when Authorization header present
  • Returns (token, "cookie") when only cookie present
  • Header takes priority over cookie when both present
  • Returns (None, None) when neither present

Tests for _validate_user_token:

  • Returns User for valid token
  • Raises InvalidTokenException for invalid/expired token
  • Raises InvalidTokenException for token with non-existent user

Tests for _get_user_model:

  • Returns User model with token attributes copied
  • Raises InvalidTokenException when user not in DB
  • Copies token_store_id, token_store_code, token_store_role from context

Tests for _validate_customer_token:

  • Returns CustomerContext for valid customer token
  • Rejects token with wrong type (not "customer")
  • Rejects token with missing "sub" claim
  • Rejects expired token
  • Rejects token for non-existent customer
  • Rejects token for inactive customer
  • Rejects token with store_id mismatch (cross-store attack)

Phase 2: Admin Authentication (6 functions)

Function Lines What it does Test count
get_current_admin_from_cookie_or_header() 163209 Admin auth via cookie or header 4
get_current_admin_api() 212242 Admin auth via header only (CSRF-safe) 3
get_current_super_admin() 250283 Require super admin role 3
get_current_super_admin_api() 286312 Super admin, header only 2
require_platform_access() 315357 Factory: platform-specific admin access 4
get_admin_with_platform_context() 360430 Admin with platform from JWT 4

Key tests:

  • Valid admin token accepted (cookie and header)
  • Non-admin role rejected with AdminRequiredException
  • No token raises InvalidTokenException
  • Super admin check: platform admin rejected
  • Platform access: super admin bypasses, platform admin checked against accessible_platform_ids
  • Platform context: platform_id extracted from token, stored in request.state

Phase 3: Store Authentication (2 functions)

Function Lines What it does Test count
get_current_store_from_cookie_or_header() 665720 Store auth via cookie or header 5
get_current_store_api() 723780 Store API auth, validates store context 5

Key tests:

  • Valid store token accepted
  • Admin blocked from store routes (critical security boundary)
  • Customer blocked from store routes
  • No token raises InvalidTokenException
  • Store API requires token_store_id claim
  • Store API verifies user still member of store (revocation check)

Phase 4: Merchant Authentication (5 functions)

Function Lines What it does Test count
get_current_merchant_from_cookie_or_header() 788848 Merchant auth, verifies ownership 4
get_current_merchant_api() 851896 Merchant API auth 3
get_current_merchant_optional() 899940 Returns None if not authenticated 3
get_merchant_for_current_user() 943979 Load Merchant object for API user 3
get_merchant_for_current_user_page() 9821018 Load Merchant object for page user 2

Key tests:

  • Valid merchant owner accepted
  • User without active merchants rejected
  • Optional variant returns None on failure (no exception)
  • Merchant object stored in request.state

Phase 5: Customer Authentication (2 functions)

Function Lines What it does Test count
get_current_customer_from_cookie_or_header() 11171156 Customer auth via cookie or header 3
get_current_customer_api() 11591185 Customer API auth 2

Key tests:

  • Valid customer token accepted
  • No token raises InvalidTokenException
  • API variant requires Authorization header

Phase 6: Access Control (6 functions)

Function Lines What it does Test count
require_module_access() 438538 Factory: module enablement check 4
require_menu_access() 546657 Factory: menu visibility check 4
require_store_permission() 12781324 Factory: specific permission check 4
require_store_owner() 13271369 Require store owner role 3
require_any_store_permission() 13721425 Factory: ANY of N permissions 3
require_all_store_permissions() 14281483 Factory: ALL of N permissions 3

Key tests:

  • Module disabled → InsufficientPermissionsException
  • Super admin bypasses module check
  • Store owner has all permissions
  • Team member checked against specific permissions
  • Missing permission raises InsufficientStorePermissionsException

Phase 7: Optional Auth & Utilities (4 functions)

Function Lines What it does Test count
get_current_admin_optional() 15351579 Returns None on failure 3
get_current_store_optional() 15821626 Returns None on failure 3
get_current_customer_optional() 16291667 Returns None on failure 3
get_user_permissions() 14861527 List all permissions for user in store 3
get_user_store() 12251270 Verify store ownership/membership 3

Key tests:

  • Optional variants return None (not raise) when token invalid
  • Optional variants return context when token valid
  • get_user_permissions returns all permissions for owner, specific for team member

Total Test Count

Phase Functions Tests
1. Helpers 4 17
2. Admin auth 6 20
3. Store auth 2 10
4. Merchant auth 5 15
5. Customer auth 2 5
6. Access control 6 21
7. Optional & utils 5 15
Total 30 ~103

Test Approach

Unit vs Integration

These will be unit tests with mocked dependencies:

  • db → SQLAlchemy session with test data (using existing db fixture)
  • auth_manager → Use the real AuthManager but with test users/tokens (existing auth_fixtures.py)
  • request → Mock FastAPI Request with request.state, request.url.path
  • FastAPI Depends() → Call functions directly, passing dependencies explicitly

This avoids the overhead of full HTTP request cycles and tests the logic in isolation.

Fixtures Needed

Most already exist in tests/fixtures/auth_fixtures.py:

  • test_admin — admin user (is_super_admin=True)
  • test_platform_admin — platform admin (is_super_admin=False)
  • test_store_user — store-role user
  • test_user — regular user

New fixtures to add in the test file:

  • mock_request — Mock Request with configurable state and url.path
  • test_customer — Customer model for customer auth tests
  • test_merchant — Merchant model for merchant auth tests
  • test_store — Store with platform association
  • admin_token — JWT token for admin user
  • store_token — JWT token with store context claims
  • customer_token — Customer JWT token
  • merchant_token — JWT token for merchant owner

Execution Order

  1. Phase 1 first — helper functions have no dependencies on other deps.py functions
  2. Phase 25 next — auth functions build on helpers
  3. Phase 67 last — access control builds on auth functions

Phases 25 can be done in parallel. Phase 6 depends on 23 being done.

Verification

# Run just the deps tests
python -m pytest tests/unit/api/test_deps.py -v --timeout=60

# Run with coverage
python -m pytest tests/unit/api/test_deps.py -v --cov=app.api.deps --cov-report=term-missing

Corrected Coverage Summary

The earlier 360 analysis incorrectly reported module service coverage at 10%. The actual numbers (after finding tests in app/modules/*/tests/):

Module Services Unit Tests Integration Tests Coverage
billing 7 8 4 100%+
catalog 4 4 0 100%
checkout 1 1 0 100%
cms 4 4 0 100%
core 6 5 0 83%
customers 3 3 0 100%
dev_tools 2 2 0 100%
inventory 3 3 0 100%
loyalty 8 8 1 100%
marketplace 5 8 0 100%+
messaging 6 5 0 83%
monitoring 6 6 0 100%
orders 6 5 0 83%
payments 2 2 0 100%
tenancy 11 17 6 100%+
cart 1 1 0 100%
analytics 1 0 0 0%

Remaining gaps (non-service):

  • Models: 12 modules have no model tests
  • Schemas: 12 modules have no schema tests
  • Routes: 14 modules have no route/integration tests
  • Tasks: 0 modules have task tests (5 modules have tasks)
  • Framework: app/api/deps.py, middleware/auth.py, app/exceptions/, app/handlers/stripe_webhook.py have no tests