08.04 — Test strategy

08.04 — Test strategy#

Six test types, six distinct roles. Documented in CURA_TEST_STRATEGY.md §3.

Test types#

TypeExpected statusAuthoritative source
Happy pathPASSNominal flows described from the FRDs
Unhappy pathPASS (the test passes when the app correctly refuses)Existing validations work
Edge case / boundaryPASS or FAILDepending on the test, often accompanied by a note added to the FRDs
Business logic vulnerability / gap test (functional tests trying to “break” the product and identify gaps)Intentional FAILBehaviors a system should prevent but CURA doesn’t
Exploratory / observed-behaviourPASSDocuments current behavior when the spec doesn’t cover it
Permanent security regression fixturePASS (= security holds)Verifies a security fix holds (fixes that stay permanently in the test heritage)

Happy path#

Exercises the nominal flow with valid inputs and an authenticated user. Verifies that the system produces the correct output (confirmation, history entry, etc.).

06.07 — OTP coordination flow + deliberate imprecision

06.07 — OTP coordination flow + deliberate imprecision#

The backend’s reason to exist: let N parallel Ocarina workers retrieve the right OTP for their user, even when several OTPs are generated.

Flow#

   ┌───────────────────────────────────────────────────────────────────┐
   │               Worker (among --workers 3 of Ocarina)               │
   └───────────────────────────────────────────────────────────────────┘
                                     │
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Selenium opens the Dashboard login page                           │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Acquire the distributed Redis lock (OTP_SEND_LOCK_KEY)            │
   │   → only this worker can click "Send OTP" during ACQ              │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ min_utc_date = datetime.now(UTC)                                  │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ L1 cache: record min_utc_date + username in the cache             │
   │   (keys reserved by reserve_free_cache_key)                       │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Selenium types username + password + ticks OTP + click "REQ OTP"  │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ IGORISTAN UI: fetch /api/otp?_user=<username>                     │
   │   (x-api-key typed by Selenium into the UI)                       │
   └─────────────────────────────────┬─────────────────────────────────┘
                                     ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ TESTS-WORKERS /api/otp:                                           │
   │   generate(secret) → otpCode                                      │
   │   createdAt = floor(now/1000)*1000  ← ms stripped                 │
   │   event = { _user, otpCode, createdAt, expiresAt, ... }           │
   │   redis.set("otp:<now>:<uuid>", JSON, EX 360)                     │
   │   return event                                                    │
   └──────────────────────────────────┬────────────────────────────────┘
                                      ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ IGORISTAN UI: displays OTP screen                                 │
   └──────────────────────────────────┬────────────────────────────────┘
                                      ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Release the OTP_SEND_LOCK_KEY Redis lock                          │
   └──────────────────────────────────┬────────────────────────────────┘
                                      ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Selenium: retrieve_dashboard_otp_code(min_utc_date, _user)        │
   │   ↓                                                               │
   │   GET /api/otp-history  (x-api-key = IGOR_API_KEY)                │
   │   ↓                                                               │
   │   TESTS-WORKERS: SCAN otp:* + MGET → all events                   │
   │   ↓                                                               │
   │   client-side filter by _user                                     │
   │   filter createdAt >= min_utc_date - 1s                           │
   │   sort ASC by createdAt                                           │
   │   return first.otpCode                                            │
   └──────────────────────────────────┬────────────────────────────────┘
                                      ▼
   ┌───────────────────────────────────────────────────────────────────┐
   │ Selenium types the OTP into the Igoristan UI                      │
   │   → AUTHENTICATED_WITH_MFA                                        │
   └───────────────────────────────────────────────────────────────────┘

Race conditions#

  1. Worker A: min_utc_date_A = 13:27:53.123, generates OTP_A at 13:27:53.250.
  2. Worker B: min_utc_date_B = 13:27:53.130, generates OTP_B at 13:27:53.470.

Without coordination, A could pick up OTP_B instead of OTP_A — the timestamps are close and truncated to the second.