Skip to main content

SDLC 04: Quality Assurance Protocols and Test Cases

Revision history: Updated June 2026 — 19-microservice architecture; Go/Python service testing; Meilisearch; BullMQ; Playwright E2E; k6 performance tests; OWASP ZAP security scanning; dedicated test environment.


1. Testing Levels

LevelToolScopeRun by
UnitJest (Node), pytest (Python), Go testIndividual functions, utilities, service logicLocal + CI
IntegrationJest + Supertest (Node), httpx (Python), Go test + httptestAPI route handlers + DB operationsCI
ComponentReact Testing LibraryFrontend components (checkout, overlay, tracking map, etc.)CI
MicroserviceJest (Node), pytest (Python), Go testService-to-service contracts, queue processingCI
End-to-EndPlaywrightCritical user journeys on stagingPre-release
Performancek6Search, checkout, tracking endpointsWeekly on staging
SecurityOWASP ZAP, npm auditVulnerability scanning, dependency auditCI + weekly
Payment SandboxPawaPay / Flutterwave sandboxGateway integration against real sandbox APIsManual QA

2. Required Test Coverage

LayerMinimum coverage
Backend utilities (utils/)90%
Service layer (services/)80%
Route handlers (routes/)100% happy path + key error paths
Payment service (services/payment/)100% (all provider branches, all webhook events)
Fraud detection (services/fraud/)100% (all rule branches, scoring logic)
Frontend components70%
Go services (search, analytics)80%
Python services (moderation, recommendations)80%

3. Testing Strategy by Service Type

3.1 Node.js Services (Jest + Supertest)

All Node.js services follow the same testing pattern:

// services/backend/src/__tests__/payments.integration.test.js
describe('POST /api/v1/payments/initiate', () => {
it('returns ussd response for MTN_MONEY', async () => {
process.env.PAYMENT_PROVIDER = 'MOCK';
const { body } = await request(app)
.post('/api/v1/payments/initiate')
.set('Authorization', `Bearer ${testUserToken}`)
.set('x-pakashop-key', process.env.PAKASHOP_CLIENT_KEY)
.send({
orderId: testOrderId,
paymentMethod: 'MTN',
phoneNumber: '0977123456'
})
.expect(200);

expect(body.success).toBe(true);
expect(body.data.transactionId).toBeDefined();
expect(body.data.message).toMatch(/payment request/i);
});

it('returns redirect response for CARD', async () => {
const { body } = await request(app)
.post('/api/v1/payments/initiate')
.set('Authorization', `Bearer ${testUserToken}`)
.set('x-pakashop-key', process.env.PAKASHOP_CLIENT_KEY)
.send({ orderId: testOrderId, paymentMethod: 'CARD' })
.expect(200);

expect(body.data.redirectUrl).toMatch(/^https?:\/\//);
});

it('blocks payment when fraud score exceeds threshold', async () => {
// Simulate high-risk transaction
const { body } = await request(app)
.post('/api/v1/payments/initiate')
.set('Authorization', `Bearer ${testUserToken}`)
.send({ orderId: suspiciousOrderId, paymentMethod: 'MTN' })
.expect(403);

expect(body.code).toBe('FRAUD_001');
});
});

3.2 Go Services (Go test)

// services/search/search_test.go
func TestSearchProducts(t *testing.T) {
req := httptest.NewRequest("GET", "/search?q=phone", nil)
rr := httptest.NewRecorder()

handler := http.HandlerFunc(SearchHandler)
handler.ServeHTTP(rr, req)

assert.Equal(t, http.StatusOK, rr.Code)

var results SearchResults
json.Unmarshal(rr.Body.Bytes(), &results)
assert.True(t, len(results.Hits) > 0)
assert.Less(t, results.ProcessingTimeMs, 100)
}

3.3 Python Services (pytest)

# services/moderation/tests/test_moderation.py
import pytest
from moderation import process_image

@pytest.mark.asyncio
async def test_nsfw_detection():
result = await process_image(
"https://example.com/test-image.jpg",
asset_id="test-uuid",
asset_type="PRODUCT_IMAGE"
)
assert "approved" in result
assert "scores" in result
assert 0 <= result["scores"]["nudity_safe"] <= 1

3.4 Payment Testing Strategy

Because real gateway calls must be avoided in CI, the payment system supports three testing modes:

MockAdapter (Local and CI)

# .env.local / CI environment
PAYMENT_PROVIDER=MOCK

MockAdapter simulates all gateway responses:

  • initiateDeposit() → returns ACCEPTED after a 500 ms delay
  • simulateWebhook() → fires a deposit.completed event after 2 s
  • createPaymentLink() → returns a local /mock-checkout URL

Use MockAdapter for all unit and integration tests.

Sandbox Mode (Manual QA)

# staging .env
PAYMENT_PROVIDER=AUTO
PAWAPAY_BASE_URL=https://api.sandbox.pawapay.io
FLUTTERWAVE_BASE_URL=https://api.flutterwave.com/v3

Test card (Flutterwave): 4187427415564246 / any future expiry / CVV 828.

PawaPay sandbox: trigger callbacks from the PawaPay Sandbox Simulator dashboard.


4. Critical E2E Test Cases (Playwright)

4.1 Authentication Journeys

  1. User registers with email + password → email verification → login.
  2. User requests password reset → receives Resend email → resets successfully.
  3. Google OAuth flow → user created → session established.
  4. MFA enrollment (TOTP) → login with TOTP code → session established.

4.2 B2C Consumer Journeys

  1. User browses products → adds to cart → selects MTN Mobile Money → completes USSD checkout → order confirmed.
  2. User selects Visa/Mastercard → redirected to Flutterwave hosted page → returns to /payment/callback → order confirmed.
  3. User views order history → correct payment method label shown (e.g., "MTN Mobile Money").
  4. User views order details → "Delivery Address" shown (not "Shipping Address").
  5. User applies coupon at checkout → discount reflected in total.
  6. User earns loyalty points → redeems points on next order.

4.3 B2B Vendor Journeys

  1. Vendor completes seller application (all 5 phases) → uploads KYC documents → submits.
  2. Admin approves application → shop created → user role updated to SHOP_OWNER.
  3. Vendor creates product with images → Sightengine moderation runs → product appears in catalogue.
  4. Vendor views their order items → correct vendorAmount displayed.
  5. Vendor manages inventory → stock adjustment → low-stock alert triggered.

4.4 Payment Lifecycle Journeys

  1. Mobile money USSD push → payment confirmed by webhook → order status → CONFIRMED.
  2. Webhook duplicate (same reference) → idempotency check → second event silently ignored.
  3. Payment times out (3 min polling) → timeout message shown → link to order page.
  4. Admin releases funds → settlementStatus transitions HELD → RELEASABLE → PAID_OUT.
  5. Fraud detection blocks suspicious payment → admin review queue entry created.

4.5 Delivery Journeys

  1. Agent picks up order → GPS tracking starts → buyer sees live location on map.
  2. Agent within 400m of destination → buyer receives "arriving soon" notification.
  3. Agent delivers → customer provides PIN → digital signature captured → order DELIVERED.
  4. Courier company admin adds sub-agent → sub-agent appears in fleet dashboard.

4.6 Admin Journeys

  1. Admin views all seller applications → filters by status → approves / rejects.
  2. Admin sends broadcast notification → all active users receive it.
  3. Admin manually releases vendor funds for a confirmed delivery.
  4. Admin reviews flagged content → approves / rejects / escalates.
  5. Admin views fraud review queue → approves blocked payment.
  6. Admin toggles feature flag → change takes effect without redeployment.

5. Performance Tests (k6)

5.1 Search Performance

// tests/performance/search.js
import http from 'k6/http';
import { check } from 'k6';

export const options = {
stages: [
{ duration: '1m', target: 100 },
{ duration: '3m', target: 100 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<100'], // 95% under 100ms
},
};

export default function () {
const res = http.get('https://staging.pakashop.store/api/v1/products?search=phone');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 100ms': (r) => r.timings.duration < 100,
});
}

5.2 Checkout Performance

// tests/performance/checkout.js
export const options = {
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<200'],
},
};

export default function () {
// Simulate checkout flow
http.post('https://staging.pakashop.store/api/v1/orders', orderPayload);
http.post('https://staging.pakashop.store/api/v1/payments/initiate', paymentPayload);
}

6. Security Tests

6.1 OWASP ZAP

Run ZAP baseline scan against staging before every production deploy:

# .github/workflows/security-scan.yml
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://staging.pakashop.store \
-r zap-report.html

6.2 Dependency Audit

npm audit --audit-level=moderate
# Block PRs with high/critical vulnerabilities

6.3 SAST

Static analysis via SonarQube or GitHub CodeQL on every PR.


7. Test Environment

EnvironmentDatabaseGatewayRedisMeilisearchPurpose
LocalLocal PostgreSQL (Docker)MOCKLocal RedisLocal MeilisearchDeveloper testing
CI (GitHub Actions)PostgreSQL service containerMOCKRedis service containerMeilisearch service containerAutomated tests per PR
StagingStaging PostgreSQLPawaPay/Flutterwave sandboxStaging RedisStaging MeilisearchPre-release QA
ProductionProduction PostgreSQLPawaPay/Flutterwave liveProduction RedisProduction MeilisearchLive platform

CI service containers (see ci.yml):

services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: test, POSTGRES_DB: pakashop_test }
ports: ['5432:5432']
redis:
image: redis:7
ports: ['6379:6379']
meilisearch:
image: getmeili/meilisearch:v1.7
env: { MEILI_MASTER_KEY: test_key }
ports: ['7700:7700']

8. Continuous Testing

  • All unit and integration tests run automatically on every pull request (GitHub Actions).
  • Merges to production require the full test suite to pass.
  • E2E Playwright suite runs weekly on the staging environment.
  • Performance tests (k6) run weekly on staging.
  • Security scans (OWASP ZAP, npm audit) run on every PR and weekly.
  • npm audit runs in CI; PRs with high/critical vulnerabilities are blocked.

9. Test Data Management

  • A prisma/seed.ts (or seed.js) script provides consistent test data (users, shops, products, orders).
  • The test database is reset before each integration test run using prisma migrate reset --force.
  • No production data is ever used in tests; all test records use synthetic Zambian data (e.g., 0977000001 as test phone numbers).
  • Test Redis uses DB 1 to avoid conflicts with development data.
  • Test services run on 4000-series ports to avoid conflicts with local development.

For internal use only. Do not distribute outside Pakashop engineering.