Skip to content

Test/consolidate test suite#59

Merged
ademboukabes merged 53 commits into
developfrom
test/consolidate-test-suite
Jul 4, 2026
Merged

Test/consolidate test suite#59
ademboukabes merged 53 commits into
developfrom
test/consolidate-test-suite

Conversation

@ademboukabes

Copy link
Copy Markdown
Collaborator

This PR introduces a robust testing infrastructure to ensure the reliability of our core services. It adds extensive Unit, Integration, and End-to-End (E2E) tests tailored to the new asynchronous OTP authentication flow and the AI photo processing pipeline.

Test Coverage & Scope

1. Unit Tests (tests/unit/)

  • Auth & Security: Validates the two-step mobile registration flow (OTP), login intent, JWT token validation, and rate-limiting limits.
  • Photo Approval Service: Tests the decision-making lifecycle for photos (Approve/Reject/Pending) and verifies audit logging behaviors.
  • AI Worker Messaging: Verifies that NATS messages for single faces and group photos are properly processed and that notifications are correctly dispatched.
  • Data Privacy: Ensures strict GDPR compliance by confirming no plaintext emails or sensitive data leak into the server logs.

2. Integration Tests (tests/integration/)

  • Database Interactions: Uses a real PostgreSQL database to verify that AI-generated face embeddings (vectors) are correctly persisted upon user enrollment without dimension errors.
  • Flow state: Ensures that OTP sessions are successfully stored and cleaned up in Redis during the registration cycle.

3. End-to-End (E2E) Tests (tests/e2e/)

  • Full Authentication Pipeline: Simulates a complete user journey: hitting the /register endpoint, retrieving the OTP directly from Redis, validating via /register/verify, and successfully logging in.
  • AI Load & Edge Cases: Simulates real-world traffic by uploading multiple photos (empty photos, group photos, and massive batches of 20+ photos) to ensure the API and AI Worker can handle concurrent load without crashing.

Verification

All tests are passing successfully (140/140) across the entire suite.

  • Unit tests passed locally
  • Integration tests passed locally
  • E2E tests passed locally

wailbentafat and others added 30 commits June 1, 2026 00:12
- Auto-approve group photos as public when no face matches any user
- Add ExpireStaleApprovals SQL + service method to unblock photos
  where users never respond (default 7-day timeout via config)
- Wire hourly expiry background task into FastAPI lifespan
This test validates the asynchronous workflow of the AI pipeline locally. It uploads a photo, triggers the NATS event, polls for the processing_job completion, and asserts that the photo status transitions to 'approved'.

Test output: 1 passed in ~1.5s
fix(photo-approval): resolve group photo lifecycle gaps

@wailbentafat wailbentafat left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: inline comments added on specific buggy lines.

await auth_service.mobile_register_resend_otp(redis=mock_redis, email=email)

assert exc_info.value.status_code == 404
assert "No pending registration found" in exc_info.value.detail

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Wrong mock patch path — this test will always pass even when NATS publish never fires.

The patch targets app.infra.nats.NatsClient.publish but users.py imports NatsClient with from app.infra.nats import NatsClient, so the name is already bound in app.service.users. Patching the source module after the import has no effect.

The test right above (test_mobile_register_sends_otp) does it correctly.

# ❌ wrong
with patch("app.infra.nats.NatsClient.publish", new_callable=AsyncMock) as mock_publish:

# ✅ correct
with patch("app.service.users.NatsClient.publish", new_callable=AsyncMock) as mock_publish:

Comment thread tests/unit/test_auth_email_otp.py Outdated
mock_redis.incr.return_value = 1

# Act
with patch("app.infra.nats.NatsClient.publish", new_callable=AsyncMock) as mock_publish:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Wrong mock patch path — this test will always pass silently even when NATS publish never fires.

users.py imports NatsClient as from app.infra.nats import NatsClient, so the name is already bound in app.service.users at import time. Patching app.infra.nats.NatsClient.publish after that has no effect on the already-bound reference.

The test right above this one (test_mobile_register_sends_otp) uses the correct path.

# ❌ current — patch has no effect
with patch("app.infra.nats.NatsClient.publish", new_callable=AsyncMock) as mock_publish:

# ✅ fix
with patch("app.service.users.NatsClient.publish", new_callable=AsyncMock) as mock_publish:

await auth_service.add_embbed_user(
user_id=user_id,
image_payloads=[payload],
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Wrong field name — this will raise a TypeError at runtime and the test will never run.

FaceImagePayload is defined with fields filename, content_type, and bytes — not image_bytes.

# ❌ current — TypeError: unexpected keyword argument 'image_bytes'
payload = FaceImagePayload(image_bytes=b"fake-image", filename="face.jpg")

# ✅ fix
payload = FaceImagePayload(bytes=b"fake-image", filename="face.jpg", content_type="image/jpeg")

from db.generated import photo_approvals as approval_queries


# ===========================================================================

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Typo in module name — causes ImportError, the entire file fails to collect.

stuff_user does not exist. This should be staff_user (or whatever the actual generated module name is).

# ❌ current — ImportError: No module named 'db.generated.stuff_user'
from db.generated import stuff_user as staff_queries

# ✅ fix
from db.generated import staff_user as staff_queries

@wailbentafat

Copy link
Copy Markdown
Collaborator

Remaining Bugs (couldn't pin inline)


tests/integration/test_enrollment_flow.py — lines 105–109: Broken cleanup

# ❌ rollback() already wipes the row — DELETE+commit after it is a no-op
finally:
    await db_conn.rollback()
    await db_conn.execute(text(f"DELETE FROM users WHERE id = '{user_id}'"))
    await db_conn.commit()

# ✅ fix — parameterized, no rollback
finally:
    await db_conn.execute(text("DELETE FROM users WHERE id = :uid"), {"uid": user_id})
    await db_conn.commit()

tests/e2e/conftest.py — lines 99–101: Deprecated asyncio.get_event_loop()

Using asyncio.get_event_loop() inside a running coroutine is deprecated since Python 3.10 and will raise an error in 3.12+.

# ❌
deadline = asyncio.get_event_loop().time() + timeout_s

# ✅
loop = asyncio.get_running_loop()
deadline = loop.time() + timeout_s

Same issue exists in tests/e2e/test_photo_ai_load.py.


tests/security/test_auth_security.pytest_blocked_user_access: Never tests blocking

The JWT contains a session_id that was never inserted into Redis or the DB. The auth middleware returns 401 (session not found) before it ever reaches the blocked-user check. The assert in (401, 403) passes for the wrong reason.

Fix: after creating and blocking the user, cache a valid session in Redis so the middleware finds it and actually reaches the block check.


tests/security/test_auth_security.pytest_rate_limiting: All requests return 401, not 429

Same root cause — the session doesn't exist, so all 25 requests return 401. assert 429 in responses will fail.

Fix: plant a real session in Redis before the loop so requests authenticate and the rate limiter can fire.

@ademboukabes ademboukabes merged commit e92fa0d into develop Jul 4, 2026
1 check passed
@ademboukabes ademboukabes deleted the test/consolidate-test-suite branch July 4, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants