๐ Debugging & Testing
Manual debug workflows for verifying auth context, order queries, and data consistency across the Ngwenya federation.
๐ Overview
When troubleshooting data visibility, auth propagation, or order consistency issues, you need to query multiple layers of the stack independently:
- Auth Service (Rust, port
3008) โ Session validation, user identity - Gateway (NestJS Apollo, port
30000) โ Request routing, context injection - Subgraphs (NestJS, various ports) โ Business logic, DB queries
- MongoDB โ Raw data verification
This guide documents the exact curl commands and patterns used during debugging, with real examples from Bug #56 (Murchase History Mismatch).
๐ Prerequisites
Get Your Session Token
Grab session_token from browser DevTools โ Application โ Cookies โ session_token.
Or extract from the auth store in the browser console:
JSON.parse(localStorage.getItem('ngwenya_auth_user'));
// โ { id, email, username, session_token, ... }
Service Ports
| Service | Port | URL |
|---|---|---|
| Auth (Rust) | 3008 |
http://localhost:3008 |
| Gateway | 30000 |
http://localhost:30000/graphql |
| Murchases | 3013 |
http://localhost:3013/graphql |
| Products | 3003 |
http://localhost:3003/graphql |
| Malets | 3011 |
http://localhost:3011/graphql |
| uCart | 3014 |
http://localhost:3014/graphql |
| uChat GraphQL | 3017 |
http://localhost:3017/graphql |
| uChat WS | 3018 |
ws://localhost:3018/ws |
๐งช Auth Pipeline Debugging
1. Validate Session Token
Verify what the auth service returns for a session. This is what the gateway calls on every request.
curl -s http://localhost:3008/auth/validate \
-H "Cookie: session_token=YOUR_TOKEN" | python3 -m json.tool
Expected response (after Bug #56 fix):
{
"user_id": "2e6f0c3a-...",
"device_id": "117b2901-...",
"email": "user@example.com",
"username": "meekdenzo"
}
โ ๏ธ If
cargo buildinapps/auth.
2. Check `/me` Endpoint (Full Profile)
Returns the complete user profile from the auth DB:
curl -s http://localhost:3008/me \
-H "Cookie: session_token=YOUR_TOKEN" | python3 -m json.tool
Expected response:
{
"id": "2e6f0c3a-...",
"username": "meekdenzo",
"email": "user@example.com",
"email_verified": true,
"is_privileged": false,
"session_token": "cYvijcn..."
}
3. Check Account Existence (Identity Status)
Verify if an account exists for a specific email or phone number. Used for fail-fast guest lookups.
# Check Email
curl -s "http://localhost:3008/auth/email/check?email=user@example.com" | python3 -m json.tool
# Check Phone (E.164 format)
curl -s "http://localhost:3008/auth/phone/check?phone=+1234567890" | python3 -m json.tool
Expected response:
{
"exists": true,
"userId": "2e6f0c3a-..."
}
4. Verify Gateway Context Propagation
The gateway should forward the full user object (including email) as a JSON header to subgraphs. Test by querying a guarded resolver:
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-H "Cookie: session_token=YOUR_TOKEN" \
-d '{"query": "{ orders { id buyerId guestEmail } }"}' | python3 -m json.tool
If orders with guestEmail matching your email don't appear, the gateway isn't forwarding email in the user context.
๐ Murchases Debugging
4. Query Authenticated Orders (via Gateway)
This is what the frontend calls on /murchases:
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-H "Cookie: session_token=YOUR_TOKEN" \
-d '{"query": "{ orders { id status buyerId guestEmail totalAmount { formatted } items { name quantity } createdAt } }"}' \
| python3 -c "
import json, sys
data = json.load(sys.stdin)
orders = data['data']['orders']
print(f'Total: {len(orders)} orders')
for o in orders:
sid = o['id'][-8:].upper()
print(f' #{sid} | buyer={o[\"buyerId\"][:16]}... | guest={o[\"guestEmail\"]} | {o[\"totalAmount\"][\"formatted\"]}')
"
5. Query Subgraph Directly (Bypass Gateway)
Bypass the gateway entirely to test the subgraph resolver in isolation. Pass the user header manually to simulate what the gateway sends:
curl -s http://localhost:3013/graphql \
-H "Content-Type: application/json" \
-H 'user: {"id":"YOUR_USER_ID","email":"your@email.com"}' \
-d '{"query": "{ orders { id buyerId guestEmail totalAmount { formatted } createdAt } }"}' \
| python3 -m json.tool
๐ก This is the most valuable debug tool โ it lets you test what happens when the gateway provides different user contexts. Try:
- With
- Without
buyerId-matched orders- With a different
id: should return that user's orders
6. Check Individual Order Ownership
Query a specific order to inspect its buyerId and guestEmail:
ORDER_ID="69c99debf608483ad49f1068"
curl -s http://localhost:3013/graphql \
-H "Content-Type: application/json" \
-H 'user: {"id":"YOUR_USER_ID","email":"your@email.com"}' \
-d "{\"query\": \"{ order(id: \\\"$ORDER_ID\\\") { id buyerId guestEmail status totalAmount { formatted } } }\"}" \
| python3 -m json.tool
7. Batch-Check Order Ownership
Loop through multiple order IDs to quickly see which account each belongs to:
for oid in ORDER_ID_1 ORDER_ID_2 ORDER_ID_3; do
result=$(curl -s http://localhost:3013/graphql \
-H "Content-Type: application/json" \
-H 'user: {"id":"YOUR_USER_ID","email":"your@email.com"}' \
-d "{\"query\": \"{ order(id: \\\"$oid\\\") { id buyerId guestEmail totalAmount { formatted } } }\"}" 2>/dev/null)
echo "$oid: $(echo $result | python3 -c "
import json,sys
d=json.load(sys.stdin)
o=d.get('data',{}).get('order')
print(f'buyer={o[\"buyerId\"]} guest={o[\"guestEmail\"]}' if o else 'NOT FOUND')
" 2>/dev/null)"
done
8. Compare Guest vs Authenticated Views
The core mismatch test โ these two should return the same orders for the same email:
echo "=== AUTHENTICATED VIEW ==="
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-H "Cookie: session_token=YOUR_TOKEN" \
-d '{"query": "{ orders { id totalAmount { formatted } } }"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(f'{len(d[\"data\"][\"orders\"])} orders')"
echo "=== GUEST VIEW (requires valid OTP) ==="
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ ordersByEmail(email: \"your@email.com\", code: \"123456\") { id totalAmount { formatted } } }"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(f'{len(d[\"data\"][\"ordersByEmail\"])} orders') if d.get('data') else print(d['errors'][0]['message'])"
echo "=== PHONE VIEW (requires valid OTP) ==="
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ ordersByPhone(phone: \"+1234567890\", code: \"123456\") { id totalAmount { formatted } } }"}' \
| python3 -c "import json,sys; d=json.load(sys.stdin); print(f'{len(d[\"data\"][\"ordersByPhone\"])} orders') if d.get('data') else print(d['errors'][0]['message'])"
๐๏ธ Database Queries
9. Query MongoDB Directly
When you need to see raw data without going through GraphQL resolvers:
# All orders for an email (regardless of buyerId)
mongosh mongodb://localhost:27017/ngwenya --quiet --eval '
db.orders.find({ guestEmail: /your@email.com/i })
.sort({ createdAt: -1 })
.forEach(o => print(
`${o._id} | buyer=${o.buyerId} | guest_email=${o.guestEmail} | guest_phone=${o.guestPhone} | $${o.totalAmount?.formatted}`
))
'
# Count unreassigned guest orders
mongosh mongodb://localhost:27017/ngwenya --quiet --eval '
print("Unreassigned:", db.orders.countDocuments({
guestEmail: /your@email.com/i,
buyerId: "guest"
}))
'
# Count orders by buyerId for an email
mongosh mongodb://localhost:27017/ngwenya --quiet --eval '
db.orders.aggregate([
{ $match: { guestEmail: /your@email.com/i } },
{ $group: { _id: "$buyerId", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
]).forEach(g => print(` ${g._id}: ${g.count} orders`))
'
10. Check Guest OTP Tokens
Inspect pending OTP tokens in the database:
mongosh mongodb://localhost:27017/ngwenya --quiet --eval '
db.guestordertokens.find({ $or: [{ email: "your@email.com" }, { phone: "+1234567890" }] })
.sort({ createdAt: -1 })
.limit(3)
.forEach(t => print(
`dest=${t.email || t.phone} | code=${t.code} | used=${t.used} | expires=${t.expiresAt}`
))
'
๐ Order Reassignment
11. Trigger Manual Reassignment
Reassign guest orders to the authenticated user (requires matching email):
curl -s http://localhost:30000/graphql \
-H "Content-Type: application/json" \
-H "Cookie: session_token=YOUR_TOKEN" \
-d '{"query": "mutation { reassignGuestOrders(guestEmail: \"your@email.com\") }"}' \
| python3 -m json.tool
Returns the count of reassigned orders. Only works when the authenticated user's email matches the guestEmail argument.
๐ Common Issues & Root Causes
Orders Missing in Authenticated View
| Symptom | Root Cause | Debug Step |
|---|---|---|
| Guest view shows more orders than auth view | findByBuyer query too restrictive |
Steps 4-5: compare with/without email |
| Auth view shows 0 orders | user.email not in gateway context |
Step 1: check /auth/validate response |
Orders belong to different buyerId UUIDs |
Multiple accounts created for same email | Step 7: batch-check ownership |
| Guest OTP always fails | Token expired or already used | Step 10: check OTP tokens |
Auth Pipeline Breakdowns
| Symptom | Root Cause | Fix |
|---|---|---|
/auth/validate missing email |
Auth service running old binary | cargo build + restart in apps/auth |
| Gateway not forwarding email | auth.context.ts not including data.email |
Check line 69/89 in auth.context.ts |
Subgraph user.email is undefined |
Gateway willSendRequest not setting header |
Check user header in app.module.ts |
๐ File Reference
| Category | File Path |
|---|---|
| Auth Route | ngwenya-federation/apps/auth/src/routes/validate.rs |
| Gateway | ngwenya-federation/apps/ngwenya-gateway/src/auth.context.ts |
| Gateway Boot | ngwenya-federation/apps/ngwenya-gateway/src/app.module.ts |
| Order Service | ngwenya-federation/apps/murchases/src/order/order.service.ts |
| Order Resolver | ngwenya-federation/apps/murchases/src/order/order.resolver.ts |
| Frontend Store | ngwenya-front/src/stores/orderStore.ts |
| Murchases Page | ngwenya-front/src/routes/murchases/+page.svelte |
| Order Queries | ngwenya-front/src/lib/queries/orders.ts |
๐ฌ uChat Debugging
Verify uChat Service is Running
# Check if uChat ports are listening
lsof -i :3017 -i :3018 -sTCP:LISTEN
# Restart uChat (rebuilds from source)
make restart-uchat
Test WebSocket Delivery
The uChat benchmark script measures E2E latency across auth, HTTP, and WS paths:
npx tsx scripts/bench-uchat-latency.ts
Requires at least two distinct user sessions in logs/main.log. The script auto-extracts tokens and deduplicates by user ID.
Verify Redis Pub/Sub
Subscribe to a user's global WS channel and send a message to verify the fan-out:
# In terminal 1: subscribe to User B's channel
podman exec -it ngwenya-redis redis-cli SUBSCRIBE "uchat:user:<USER_B_ID>"
# In terminal 2: send a message as User A
curl -s http://localhost:3017/graphql \
-H "Content-Type: application/json" \
-H "x-user-id: <USER_A_ID>" \
-d '{"query": "mutation { sendMessage(input: { conversationId: \"<CONV_ID>\", encryptedBody: \"test\" }) { id } }"}'
If the message appears in terminal 1, Redis pub/sub fan-out is working correctly.
Common uChat Issues
| Symptom | Root Cause | Fix |
|---|---|---|
request to localhost:3017 failed |
uChat service is down | make restart-uchat |
x-user-id header missing |
Gateway auth timed out | Check auth service; gateway retries automatically |
| WS connects but no messages arrive | Sender-filter bug (fixed) or wrong channel | Verify with Redis CLI subscriber |
| Raw JSON error in chat UI | Error not sanitized | Fixed in store.svelte.ts |
Related
- Gateway Rate Limiting โ Dual-window throttling that may affect debugging requests at high frequency
- Gateway Tracing & Observability โ Per-operation metrics and response caching useful during debugging
- MFA & Passkeys โ Auth pipeline details for debugging WebAuthn and TOTP flows
- Local Development Environment โ Podman setup, Make targets, and service architecture for running the full stack
- uChat Integration Guide โ Backend architecture, WS modes, and Redis pub/sub details
- uCart Concurrency & Resilience โ Optimistic concurrency and retry patterns for cart mutation debugging
- Svelte 5 AST Shadows โ Fixing silent rendering crashes caused by $state variable collisions
- Frontend Debugging Playbook โ Console-log-driven methodology for diagnosing silent Svelte 5 UI failures