24 KiB
Sample Test Cases: Authentication & Credit System
Detailed Test Case Examples with Data and Expected Results Version: 1.0 Date: 2025-11-25
Authentication Test Cases
TC-AUTH-REG-001: Email/Password Registration with Valid Credentials
Priority: P0 (Critical) Component: Authentication Service Feature: User Registration
Preconditions:
- Application running in test environment
- Database accessible
- Email service configured (or mocked)
- Test email:
test+reg001@manacore.comnot already registered
Test Data:
{
"email": "test+reg001@manacore.com",
"password": "SecureP@ss123",
"deviceInfo": {
"deviceId": "test-device-001",
"deviceName": "iPhone 13 Pro",
"deviceType": "ios",
"appVersion": "1.0.0"
}
}
Test Steps:
- Open registration screen
- Enter email:
test+reg001@manacore.com - Enter password:
SecureP@ss123 - Tap "Register" button
- Wait for response
Expected Results:
API Response (200 OK):
{
"appToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "rt_abc123def456...",
"needsVerification": false
}
appToken Claims (Decoded):
{
"sub": "550e8400-e29b-41d4-a716-446655440000",
"email": "test+reg001@manacore.com",
"role": "authenticated",
"app_id": "memoro",
"iat": 1701000000,
"exp": 1701003600
}
Database Validation:
SELECT id, email, created_at, credits
FROM user_profiles
WHERE email = 'test+reg001@manacore.com';
-- Expected:
-- id: 550e8400-e29b-41d4-a716-446655440000
-- email: test+reg001@manacore.com
-- created_at: 2025-11-25 10:00:00
-- credits: 150 (default free credits)
Secure Storage Validation (Mobile):
// iOS Keychain / Android Keystore
const appToken = await SecureStore.getItemAsync('@auth/appToken');
const refreshToken = await SecureStore.getItemAsync('@auth/refreshToken');
const userEmail = await SecureStore.getItemAsync('@auth/userEmail');
expect(appToken).toBeTruthy();
expect(refreshToken).toBeTruthy();
expect(userEmail).toBe('test+reg001@manacore.com');
UI Validation:
- User redirected to home screen
- Credit balance shows "150 Mana"
- User name/email displayed in profile
Post-Conditions:
- User account created in database
- User authenticated
- Tokens stored securely
- User can access protected resources
TC-AUTH-LOGIN-002: Failed Login with Invalid Password
Priority: P0 (Critical) Component: Authentication Service Feature: User Login
Preconditions:
- User exists:
test+login002@manacore.comwith passwordCorrectP@ss123
Test Data:
{
"email": "test+login002@manacore.com",
"password": "WrongPassword",
"deviceInfo": {
"deviceId": "test-device-002",
"deviceName": "Pixel 6 Pro",
"deviceType": "android"
}
}
Test Steps:
- Open login screen
- Enter email:
test+login002@manacore.com - Enter incorrect password:
WrongPassword - Tap "Login" button
- Wait for response
Expected Results:
API Response (401 Unauthorized):
{
"success": false,
"error": "INVALID_CREDENTIALS"
}
UI Validation:
- Error message displayed: "Invalid email or password"
- Email field retains entered value
- Password field cleared
- No tokens stored
- User remains on login screen
Database Validation:
-- No new session created
SELECT * FROM auth_sessions
WHERE user_email = 'test+login002@manacore.com'
AND created_at > NOW() - INTERVAL '1 minute';
-- Expected: 0 rows
Security Validation:
- Password not returned in response
- Generic error message (no hint that email exists)
- Failed attempt logged for brute-force detection
Post-Conditions:
- User NOT authenticated
- No tokens in storage
- Failed login attempt recorded
TC-AUTH-REFRESH-001: Automatic Token Refresh on 401
Priority: P0 (Critical) Component: Token Manager Feature: Automatic Token Refresh
Preconditions:
- User logged in
- appToken expired (or manually expired)
- refreshToken valid
Test Data:
Initial State:
const expiredAppToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."; // exp: 1 hour ago
const validRefreshToken = "rt_valid123..."; // exp: 30 days from now
Test Steps:
- User is logged in with expired appToken
- User initiates API call:
GET /api/memos - TokenManager detects expired token
- TokenManager automatically calls
/auth/refresh - New tokens received
- Original API call retried with new token
- API call succeeds
Expected Results:
Token Refresh API Call:
POST /auth/refresh
Content-Type: application/json
{
"refreshToken": "rt_valid123...",
"deviceInfo": {
"deviceId": "test-device-003"
}
}
Token Refresh Response (200 OK):
{
"appToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.NEW_TOKEN",
"refreshToken": "rt_new456..."
}
Token Manager State Transitions:
VALID → EXPIRED → REFRESHING → VALID
Network Calls:
GET /api/memos→ 401 Unauthorized (expired token)POST /auth/refresh→ 200 OK (new tokens)GET /api/memos→ 200 OK (retry with new token)
Storage Updates:
// Old tokens replaced
await SecureStore.getItemAsync('@auth/appToken');
// Returns: NEW_TOKEN
await SecureStore.getItemAsync('@auth/refreshToken');
// Returns: rt_new456...
UI Validation:
- No user interaction required
- Loading indicator shown during refresh (< 2 seconds)
- Memos displayed successfully
- No error messages
Performance:
- Total time from initial API call to data displayed: < 3 seconds
- Refresh process: < 2 seconds
Post-Conditions:
- User remains authenticated
- New tokens stored
- Original API call succeeded
- TokenManager state: VALID
Credit System Test Cases
TC-CREDIT-PURCHASE-001: Successful Credit Purchase
Priority: P0 (Critical) Component: Payment Service, Credit Service Feature: Credit Purchase
Preconditions:
- User authenticated:
test+credit001@manacore.com - Current balance: 10 credits
- Mock payment gateway configured
Test Data:
{
"userId": "550e8400-e29b-41d4-a716-446655440001",
"packageId": "mana_100",
"credits": 100,
"price": 4.99,
"currency": "EUR"
}
Test Steps:
- User navigates to subscription page
- Select "100 Credits - €4.99" package
- Tap "Purchase" button
- Complete mock payment (simulated success)
- Payment gateway sends webhook to backend
Mock Webhook Payload:
{
"event": "purchase",
"transactionId": "txn_mock_12345",
"userId": "550e8400-e29b-41d4-a716-446655440001",
"productId": "mana_100",
"credits": 100,
"price": 4.99,
"currency": "EUR",
"timestamp": "2025-11-25T10:00:00Z",
"status": "completed"
}
Expected Results:
Webhook Processing:
- Backend receives webhook
- Validates transaction ID (not duplicate)
- Validates user exists
- Updates credit balance atomically
Database Updates:
user_profiles table:
UPDATE user_profiles
SET credits = credits + 100
WHERE id = '550e8400-e29b-41d4-a716-446655440001';
-- Before: 10 credits
-- After: 110 credits
credit_transactions table:
INSERT INTO credit_transactions (
id,
user_id,
transaction_type,
amount,
description,
transaction_id,
status,
created_at
) VALUES (
gen_random_uuid(),
'550e8400-e29b-41d4-a716-446655440001',
'purchase',
100,
'Credit purchase: 100 credits',
'txn_mock_12345',
'completed',
NOW()
);
API Response to Client:
{
"success": true,
"newBalance": 110,
"transaction": {
"id": "uuid-transaction",
"amount": 100,
"timestamp": "2025-11-25T10:00:00Z"
}
}
UI Updates:
- Credit balance updates to "110 Mana" (within 1 second)
- Success notification: "100 credits added successfully!"
- Transaction visible in history
Post-Conditions:
- User balance: 110 credits
- Transaction recorded
- User can use new credits
TC-CREDIT-CONSUME-003: Concurrent Credit Deduction
Priority: P0 (Critical) Component: Credit Service Feature: Credit Consumption
Preconditions:
- User authenticated:
test+credit003@manacore.com - Current balance: 100 credits
- 3 concurrent operations ready
Test Data:
User State:
{
"userId": "550e8400-e29b-41d4-a716-446655440003",
"currentBalance": 100,
"operations": [
{ "id": "op1", "type": "STORY_CREATE", "cost": 30 },
{ "id": "op2", "type": "STORY_CREATE", "cost": 30 },
{ "id": "op3", "type": "STORY_CREATE", "cost": 30 }
]
}
Test Steps:
- Trigger 3 story creation operations simultaneously
- Each operation validates credits BEFORE execution
- All 3 operations execute concurrently
- Each operation consumes credits AFTER success
- Verify final balance
Expected Results:
Validation Phase (Concurrent):
Operation 1:
POST /credits/validate
{
"userId": "550e8400-e29b-41d4-a716-446655440003",
"operationType": "STORY_CREATE",
"cost": 30
}
Response: { "hasCredits": true, "availableCredits": 100 }
Operation 2:
POST /credits/validate
{
"userId": "550e8400-e29b-41d4-a716-446655440003",
"operationType": "STORY_CREATE",
"cost": 30
}
Response: { "hasCredits": true, "availableCredits": 100 }
Operation 3:
POST /credits/validate
{
"userId": "550e8400-e29b-41d4-a716-446655440003",
"operationType": "STORY_CREATE",
"cost": 30
}
Response: { "hasCredits": true, "availableCredits": 100 }
Execution Phase:
- All 3 stories generated successfully
Consumption Phase (Atomic, Sequential due to DB locks):
Database Transaction Log:
-- Transaction 1 (Operation 1)
BEGIN;
SELECT credits, updated_at FROM user_profiles WHERE id = '...' FOR UPDATE;
-- credits: 100, updated_at: t1
UPDATE user_profiles SET credits = 70, updated_at = NOW() WHERE id = '...' AND updated_at = t1;
COMMIT;
-- Transaction 2 (Operation 2)
BEGIN;
SELECT credits, updated_at FROM user_profiles WHERE id = '...' FOR UPDATE;
-- credits: 70, updated_at: t2
UPDATE user_profiles SET credits = 40, updated_at = NOW() WHERE id = '...' AND updated_at = t2;
COMMIT;
-- Transaction 3 (Operation 3)
BEGIN;
SELECT credits, updated_at FROM user_profiles WHERE id = '...' FOR UPDATE;
-- credits: 40, updated_at: t3
UPDATE user_profiles SET credits = 10, updated_at = NOW() WHERE id = '...' AND updated_at = t3;
COMMIT;
Final State:
{
"userId": "550e8400-e29b-41d4-a716-446655440003",
"finalBalance": 10,
"transactionsCreated": 3,
"totalDeducted": 90
}
UI Validation:
- Credit balance updates to "10 Mana"
- All 3 stories visible in user's library
- Transaction history shows 3 separate deductions
Critical Validations:
- ✅ No over-deduction (balance not negative)
- ✅ No under-deduction (all 90 credits deducted)
- ✅ Database consistency maintained
- ✅ All operations succeeded
Post-Conditions:
- User balance: 10 credits
- 3 transactions recorded
- 3 stories created
TC-CREDIT-CONSUME-004: Insufficient Balance During Concurrent Operations
Priority: P0 (Critical) Component: Credit Service Feature: Credit Consumption Edge Case
Preconditions:
- User authenticated:
test+credit004@manacore.com - Current balance: 10 credits (LOW BALANCE)
- 2 concurrent operations ready
Test Data:
{
"userId": "550e8400-e29b-41d4-a716-446655440004",
"currentBalance": 10,
"operations": [
{ "id": "op1", "type": "STORY_CREATE", "cost": 8 },
{ "id": "op2", "type": "STORY_CREATE", "cost": 8 }
]
}
Test Steps:
- Trigger 2 story creation operations simultaneously (8 credits each)
- Both operations validate credits (both should pass with 10 credits available)
- Both operations execute concurrently
- First operation consumes 8 credits (balance → 2)
- Second operation attempts to consume 8 credits (insufficient)
- Verify error handling
Expected Results:
Validation Phase (Both Pass):
Operation 1: POST /credits/validate → { "hasCredits": true, "availableCredits": 10 }
Operation 2: POST /credits/validate → { "hasCredits": true, "availableCredits": 10 }
Execution Phase:
- Both stories generated successfully (AI service completes)
Consumption Phase:
Operation 1 (Succeeds):
BEGIN;
UPDATE user_profiles SET credits = 2 WHERE id = '...' AND credits >= 8;
-- 1 row affected
INSERT INTO credit_transactions (...) VALUES (...);
COMMIT;
Operation 2 (Fails):
BEGIN;
UPDATE user_profiles SET credits = credits - 8 WHERE id = '...' AND credits >= 8;
-- 0 rows affected (balance only 2, not enough)
ROLLBACK;
API Response for Operation 2 (400 Bad Request):
{
"error": "insufficient_credits",
"message": "Insufficient mana. Required: 8, Available: 2",
"requiredCredits": 8,
"availableCredits": 2
}
UI Behavior:
- Operation 1: Success notification, story saved
- Operation 2: Insufficient credits modal shown
- "You need 8 Mana but only have 2 Mana"
- "Get More Mana" button
- Cancel button
Final State:
{
"userId": "550e8400-e29b-41d4-a716-446655440004",
"finalBalance": 2,
"successfulOperations": 1,
"failedOperations": 1
}
Critical Validations:
- ✅ First operation succeeded and charged
- ✅ Second operation failed with clear error
- ✅ No negative balance
- ✅ User notified of failure
- ✅ Story from failed operation NOT saved
Post-Conditions:
- User balance: 2 credits
- 1 transaction recorded (successful operation)
- 1 story created (successful operation)
- User sees insufficient credits modal
Integration Test Cases
TC-INT-CROSS-002: Multi-App Credit Consumption
Priority: P0 (Critical) Component: Credit Service, Multi-App Integration Feature: Cross-App Credit Synchronization
Preconditions:
- User authenticated in both Memoro and Maerchenzauber
- User:
test+integration002@manacore.com - Initial balance: 100 credits
Test Data:
{
"userId": "550e8400-e29b-41d4-a716-446655440005",
"initialBalance": 100,
"memoroOperation": {
"type": "TRANSCRIPTION",
"cost": 30,
"duration": 15
},
"maerchenzauberOperation": {
"type": "STORY_CREATE",
"cost": 20
}
}
Test Steps:
- Open Memoro app
- Check credit balance → 100 Mana
- Record 15-minute audio memo
- Process transcription (costs 30 credits)
- Wait for balance update
- Immediately switch to Maerchenzauber app
- Check credit balance in Maerchenzauber
- Create story (costs 20 credits)
- Wait for balance update
- Switch back to Memoro app
- Check final balance
Expected Results:
Step 1-2 (Memoro - Initial):
UI: "100 Mana" displayed
Step 3-4 (Memoro - Transcription):
API: POST /memoro/transcribe
Response: { "success": true, "creditsUsed": 30 }
Database:
UPDATE user_profiles SET credits = 70 WHERE id = '...';
UI Update (< 1 second): "70 Mana" displayed
Step 6-7 (Maerchenzauber - Balance Check):
API: GET /auth/credits
Response: { "credits": 70, "userId": "..." }
UI: "70 Mana" displayed (synced from Memoro)
Validation: Balance consistent across apps within 1 second
Step 8-9 (Maerchenzauber - Story Creation):
API: POST /story/create
Response: { "success": true, "creditsUsed": 20 }
Database:
UPDATE user_profiles SET credits = 50 WHERE id = '...';
UI Update (< 1 second): "50 Mana" displayed
Step 10-11 (Memoro - Final Balance):
API: GET /auth/credits
Response: { "credits": 50, "userId": "..." }
UI: "50 Mana" displayed (synced from Maerchenzauber)
Validation: Balance consistent across apps
Timeline Validation:
T+0s: Memoro balance: 100
T+2s: Transcription complete, Memoro balance: 70
T+3s: Switch to Maerchenzauber, balance: 70 ✓
T+5s: Story created, Maerchenzauber balance: 50
T+6s: Switch to Memoro, balance: 50 ✓
Database Transaction Log:
-- Transaction 1 (Memoro)
INSERT INTO credit_transactions (user_id, app_id, type, amount, description)
VALUES ('...', 'memoro', 'consumption', -30, 'Transcription: 15 min audio');
-- Transaction 2 (Maerchenzauber)
INSERT INTO credit_transactions (user_id, app_id, type, amount, description)
VALUES ('...', 'maerchenzauber', 'consumption', -20, 'Story creation');
Critical Validations:
- ✅ Balance synced across apps within 1 second
- ✅ No lost credits (100 - 30 - 20 = 50)
- ✅ Both operations succeeded
- ✅ Transactions logged with correct app_id
Post-Conditions:
- User balance: 50 credits (consistent in both apps)
- 2 transactions recorded
- 1 transcribed memo (Memoro)
- 1 story created (Maerchenzauber)
Performance Test Case
TC-PERF-LOAD-002: Token Refresh Under Load
Priority: P1 (High) Component: Token Manager, Auth Service Feature: Concurrent Token Refresh
Test Configuration:
{
virtualUsers: 500,
duration: '2m',
scenario: 'all_tokens_expired_simultaneously'
}
Test Data:
// 500 virtual users with expired tokens
const users = Array.from({ length: 500 }, (_, i) => ({
userId: `load-test-user-${i}`,
appToken: generateExpiredToken(),
refreshToken: generateValidToken(),
deviceId: `device-${i}`
}));
Test Script (k6):
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 500,
duration: '2m',
thresholds: {
http_req_duration: ['p(95)<2000', 'p(99)<5000'],
http_req_failed: ['rate<0.01'],
},
};
export default function () {
const refreshToken = __ENV.REFRESH_TOKEN;
// Simulate API call with expired token
const apiRes = http.get('https://api.manacore.com/api/memos', {
headers: { Authorization: `Bearer ${expiredToken}` }
});
check(apiRes, {
'status is 401': (r) => r.status === 401,
});
// Automatic refresh triggered
const refreshRes = http.post('https://api.manacore.com/auth/refresh',
JSON.stringify({ refreshToken }),
{ headers: { 'Content-Type': 'application/json' } }
);
check(refreshRes, {
'refresh status is 200': (r) => r.status === 200,
'new tokens received': (r) => r.json('appToken') !== null,
'refresh time < 2s': (r) => r.timings.duration < 2000,
});
sleep(1);
}
Expected Results:
Performance Metrics:
Scenarios: (100.00%) 1 scenario, 500 max VUs, 2m30s max duration
✓ refresh status is 200............: 100.00% ✓ 60000 ✗ 0
✓ new tokens received...............: 100.00% ✓ 60000 ✗ 0
✓ refresh time < 2s.................: 99.95% ✓ 59970 ✗ 30
http_req_duration...................: avg=850ms min=200ms med=750ms max=4.2s p(95)=1.8s p(99)=2.9s
http_req_failed.....................: 0.01% ✓ 6 ✗ 59994
http_reqs...........................: 60000 500/s
vus.................................: 500 min=500 max=500
vus_max.............................: 500 min=500 max=500
Success Criteria:
- ✅ P95 response time < 2 seconds
- ✅ P99 response time < 5 seconds
- ✅ Error rate < 1%
- ✅ All 500 users successfully refreshed
- ✅ Server CPU < 80%
- ✅ Server memory stable (no leaks)
Server Resource Monitoring:
CPU Usage: 65% average, 78% peak
Memory Usage: 4.2 GB (stable)
Database Connections: 45/100 (under limit)
Response Time: 850ms average
Post-Conditions:
- All 500 users have valid tokens
- No service degradation
- No errors or crashes
Security Test Case
TC-SEC-CREDIT-001: Credit Balance Tampering Attempt
Priority: P0 (Critical) Component: Credit Service Feature: Security
Preconditions:
- User authenticated:
test+security001@manacore.com - Current balance: 10 credits
- Attacker has access to user's device/browser
Test Data:
{
"userId": "550e8400-e29b-41d4-a716-446655440006",
"currentBalance": 10,
"attemptedBalance": 10000
}
Attack Scenarios:
Attack 1: Modify JWT Claims
// Attacker obtains JWT token
const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
// Decode token
const payload = {
"sub": "550e8400-e29b-41d4-a716-446655440006",
"credits": 10, // Original
"role": "authenticated"
};
// Attacker modifies credits
payload.credits = 10000;
// Attacker re-encodes token (without proper signature)
const tamperedToken = encodeJWT(payload, "wrong-secret");
// Attacker sends API request with tampered token
fetch('/api/memos', {
headers: { Authorization: `Bearer ${tamperedToken}` }
});
Expected Defense:
API Response: 401 Unauthorized
{
"error": "Invalid token signature"
}
Server Log:
[AUTH] Token signature verification failed for user attempt
[SECURITY] Potential token tampering detected
Attack 2: Modify Local Storage
// Web app - attacker opens browser console
localStorage.setItem('creditBalance', '10000');
location.reload();
Expected Defense:
// On app load
const localBalance = localStorage.getItem('creditBalance'); // "10000"
const serverBalance = await fetchCredits(); // API call: 10
// App uses server-authoritative balance
UI displays: "10 Mana" (server value)
localStorage.setItem('creditBalance', '10'); // Overwritten
Attack 3: Direct API Manipulation
// Attacker crafts malicious API request
fetch('/credits/add', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + validToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: "550e8400-e29b-41d4-a716-446655440006",
amount: 10000
})
});
Expected Defense:
API Response: 403 Forbidden
{
"error": "Unauthorized endpoint"
}
Server Log:
[SECURITY] Attempt to access admin-only endpoint: /credits/add
[SECURITY] User: 550e8400-e29b-41d4-a716-446655440006
[SECURITY] IP: 192.168.1.100
Attack 4: SQL Injection
// Attacker tries SQL injection in credit consumption
fetch('/credits/consume', {
method: 'POST',
body: JSON.stringify({
userId: "'; UPDATE user_profiles SET credits = 10000; --",
amount: 10
})
});
Expected Defense:
// Parameterized query prevents injection
const query = `
UPDATE user_profiles
SET credits = credits - $1
WHERE id = $2
`;
db.query(query, [amount, userId]);
API Response: 400 Bad Request
{
"error": "Invalid user ID format"
}
Final Validation:
-- Verify balance unchanged
SELECT credits FROM user_profiles
WHERE id = '550e8400-e29b-41d4-a716-446655440006';
-- Expected: 10 (unchanged)
Critical Validations:
- ✅ JWT signature verification prevents token tampering
- ✅ Server-authoritative balance (client can't override)
- ✅ Admin endpoints protected (role-based access control)
- ✅ SQL injection prevented (parameterized queries)
- ✅ All attacks logged for security monitoring
Post-Conditions:
- User balance: 10 credits (unchanged)
- Attack attempts logged
- Security team notified (if threshold exceeded)
Test Data Repository
Test User Accounts
test_users:
- email: test+reg001@manacore.com
password: SecureP@ss123
credits: 0
purpose: Registration testing
- email: test+login002@manacore.com
password: CorrectP@ss123
credits: 50
purpose: Login testing
- email: test+credit001@manacore.com
password: Test123!@#
credits: 10
purpose: Credit purchase testing
- email: test+credit003@manacore.com
password: Test123!@#
credits: 100
purpose: Concurrent deduction testing
- email: test+credit004@manacore.com
password: Test123!@#
credits: 10
purpose: Insufficient balance testing
- email: test+integration002@manacore.com
password: Test123!@#
credits: 100
purpose: Multi-app integration testing
- email: test+security001@manacore.com
password: Test123!@#
credits: 10
purpose: Security testing
- email: test+b2b@manacore.com
password: Test123!@#
credits: 10000
b2b: true
purpose: B2B account testing
Mock Webhook Payloads
{
"purchase_success": {
"event": "purchase",
"transactionId": "txn_mock_12345",
"userId": "550e8400-e29b-41d4-a716-446655440001",
"productId": "mana_100",
"credits": 100,
"price": 4.99,
"currency": "EUR",
"timestamp": "2025-11-25T10:00:00Z",
"status": "completed"
},
"purchase_failed": {
"event": "purchase_failed",
"transactionId": "txn_mock_67890",
"userId": "550e8400-e29b-41d4-a716-446655440001",
"productId": "mana_100",
"error": "payment_declined",
"timestamp": "2025-11-25T10:05:00Z"
},
"purchase_duplicate": {
"event": "purchase",
"transactionId": "txn_mock_12345",
"userId": "550e8400-e29b-41d4-a716-446655440001",
"productId": "mana_100",
"credits": 100,
"price": 4.99,
"currency": "EUR",
"timestamp": "2025-11-25T10:00:00Z",
"status": "completed"
}
}
END OF SAMPLE TEST CASES
For full test strategy, see /TESTING_STRATEGY_AUTH_CREDITS.md