CLOUDLAYER CACHE TESTS (branch fix/txn-beam-cache) =================================================== TEST DATA (checked against the database) MERCHANT ...... Frog8 Cafe (MMS user id 1362) MID ........... 123451234512345 TID 1 ......... 81748887 (name "TEST QR", MID 123451234512345) TID 2 ......... 12345673 (name "Frog8 Cafe-Device", MID 123451234512345) <- same MID as TID 1 OTHER MID ..... 271204640000000 (TID 20464409, "Frog8 Cafe-Device") <- used only in TR-6 UNKNOWN MID ... 000000000000000 Cache TTL ..... 5 minutes Server ........ http://demo.ctrmv.com:4085 Auth key ...... 9lBhDRezUjLvZ9L WHAT YOU MUST KNOW BEFORE STARTING (this is why earlier results looked wrong) - 99999991 and 20464409-with-this-MID are NOT valid TID 2 values. 99999991 does not exist in the DB and 20464409 belongs to MID 271204640000000. Use TID 2 = 12345673 everywhere in this file. - The rules that exist in MMS for this merchant today are MERCHANT rules named "min amt" (MIN_AMOUNT 100) and "max amt" (MAX_AMOUNT 500). The names TEST-CACHE-MIN and TEST-CACHE-MAX-TERM do not exist. This file uses the real name "min amt". The "rule_name" in the response must read "min amt". - A MERCHANT rule applies to every TID under the MID. A TERMINAL rule applies to one TID only. The only terminal rules in MMS today are for TID S1234567 - none exist for 81748887. You create one in TR-3. - MMS menu names below are approximate. What matters is the SCOPE (Merchant / Terminal / Global) written in each step. WHERE EACH CHANGE IS MADE IN MMS [M-RULE] MMS -> Transaction Rules -> rule with Scope = MERCHANT (merchant = Frog8 Cafe) [T-RULE] MMS -> Transaction Rules -> rule with Scope = TERMINAL (terminal_id = the TID) [M-CFG] MMS -> Frog8 Cafe -> Merchant Configuration (settings: TRANS_CONTACTLESS, DEVICES, receipt message) [GLOBAL] MMS -> Global Settings (global_table: TRANS_CONTACTLESS, POS_ENTRY_BLOCKED, MCC_BLOCKED_LIST, COUNTRY_BLOCKED_LIST) No DB edits by hand. Every data change is done in MMS. -------------------------------------------------------------- 0. SETUP (paste once, in a terminal ON THE SERVER, in the CloudLayer folder) -------------------------------------------------------------- LOG=$(ls -t logs/app_*.log | grep -v '\.gz$' | head -1) mark() { MARK=$(wc -l < "$LOG"); echo "marked at line $MARK"; } show() { tail -n +$((MARK+1)) "$LOG" | grep -E "TXN RULE CACHE|MERCHANT CONFIG|DEVICE|DB LOAD|SELECT" ; } Use: run mark, send the curl, then run show to see only the new log lines. NORMAL TRANSACTION (used by "normal request" in later tests - change TID / amount / entry_mode as stated) curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: normal" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"150.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"910001","rrn":"RRN000000101","timestamp":"2026-10-01T10:00:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' ============================================================== 1. TRANSACTION RULES ============================================================== TR-1 — First request = MISS, second request = HIT Before starting, in MMS check: [M-RULE] "min amt" MIN_AMOUNT = 100 -> enabled (No terminal rule needed for this test.) CURL 1 — Clear cache curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 Transaction rule cache cleared PASS / FAIL: PASS✅ CURL 2 — First transaction curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-01a" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900001","rrn":"RRN000000001","timestamp":"2026-10-01T10:01:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: DECLINE, code R_MIN_AMOUNT Amount 50.00 is below minimum 100 rule_name: min amt Merchant rules: MISS → DB LOAD → CACHE SAVE Terminal rules: MISS → DB LOAD → CACHE SAVE PASS / FAIL: PASS✅ CURL 3 — Same transaction again curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-01b" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900002","rrn":"RRN000000002","timestamp":"2026-10-01T10:02:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: Same DECLINE Merchant rules: HIT Terminal rules: HIT No DB LOAD PASS / FAIL: PASS✅ TR-2 — Change MMS value but DO NOT refresh Before CURL 1, in MMS: [M-RULE] "min amt" MIN_AMOUNT: 100 → 30 (save) Important: do NOT refresh yet. CURL 1 — Test without refresh curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-02a" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900003","rrn":"RRN000000003","timestamp":"2026-10-01T10:03:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: Still DECLINE (old cached value 100 is still used) Merchant rules: HIT No DB LOAD PASS / FAIL: PASS✅ CURL 2 — Refresh this MID curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Expected: HTTP 200 Merchant cache cleared (INVALIDATE) PASS / FAIL: PASS✅ CURL 3 — Test after refresh curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-02b" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900004","rrn":"RRN000000004","timestamp":"2026-10-01T10:04:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: ALLOW (50 >= 30) Merchant rules: MISS → DB LOAD → CACHE SAVE PASS / FAIL: PASS✅ Restore in MMS: [M-RULE] "min amt" MIN_AMOUNT: 30 → 100 (save) Then clear the cache: curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 / cache cleared. PASS / FAIL: PASS✅ TR-3 — Terminal MAX rule (rule belongs to ONE TID, not the whole MID) Before starting, in MMS: [M-RULE] DISABLE "max amt" (MAX_AMOUNT 500, scope Merchant). Reason: it is a merchant rule, so it would decline BOTH TIDs and hide what this test checks. [T-RULE] CREATE a new rule: name = TEST-CACHE-MAX-TERM, type = MAX_AMOUNT, max_amount = 500, currency = AED, Scope = TERMINAL, terminal_id = 81748887, enabled. [T-RULE] Make sure NO terminal rule exists for TID 12345673. Keep [M-RULE] "min amt" (MIN 100) enabled. CURL 1 — Clear cache curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 / cache cleared. PASS / FAIL: PASS✅ (re-run after the MMS change above) CURL 2 — TID 81748887 (TID 1), amount 600 curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-03a" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"600.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900005","rrn":"RRN000000005","timestamp":"2026-10-01T10:05:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: DECLINE, code R_MAX_AMOUNT Transaction amount 600.00 exceeds maximum 500.00 rule_name: TEST-CACHE-MAX-TERM (NOT "max amt") Merchant rules: MISS → DB LOAD; Terminal rules: MISS → DB LOAD PASS / FAIL: ______ CURL 3 — TID 12345673 (TID 2, same MID), amount 600 curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-03b" \ -d '{"merchant_id":"123451234512345","terminal_id":"12345673","bin":"450000","entry_mode":"071","amount":{"value":"600.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900006","rrn":"RRN000000006","timestamp":"2026-10-01T10:06:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: ALLOW (TEST-CACHE-MAX-TERM must NOT apply to this TID) Merchant rules: HIT Terminal rules: HIT (one shared entry for all TIDs) No DB LOAD PASS / FAIL: ______ This proves the rule is TID-specific, not MID-specific. Leave the TEST-CACHE-MAX-TERM terminal rule and the disabled "max amt" as they are for the rest of the file. (Restore at the very end: delete TEST-CACHE-MAX-TERM and re-enable "max amt" if you want the old state back.) TR-4 — Disable a rule in MMS Before CURL 1, in MMS: [M-RULE] DISABLE "min amt" (MIN_AMOUNT 100, scope Merchant). Do NOT refresh. CURL 1 — Before refresh curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-04a" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900007","rrn":"RRN000000007","timestamp":"2026-10-01T10:07:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: Still DECLINE (cached "min amt" is still active) Merchant rules: HIT PASS / FAIL: ______ CURL 2 — Refresh this MID curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ CURL 3 — Test again curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-04b" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900008","rrn":"RRN000000008","timestamp":"2026-10-01T10:08:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: ALLOW ("min amt" no longer applies) Merchant rules: MISS → DB LOAD PASS / FAIL: ______ Restore in MMS: [M-RULE] ENABLE "min amt" again, then run the MID refresh from CURL 2. (Do this before TR-5, which needs "min amt" active.) TR-5 — Same MID, different TID No MMS change. "min amt" must be enabled (restored at the end of TR-4). CURL 0 — Clear cache first curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" CURL 1 — TID 1 curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-05a" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900009","rrn":"RRN000000009","timestamp":"2026-10-01T10:09:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: DECLINE by merchant rule "min amt". Merchant rules: MISS → DB LOAD. PASS / FAIL: ______ CURL 2 — TID 2 (only terminal_id changed to 12345673) curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-05b" \ -d '{"merchant_id":"123451234512345","terminal_id":"12345673","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900010","rrn":"RRN000000010","timestamp":"2026-10-01T10:10:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: DECLINE by the same merchant rule "min amt" (merchant rule applies to both TIDs) Merchant rules: HIT Terminal rules: HIT No DB LOAD PASS / FAIL: ______ TR-6 — Different MID (run straight after TR-5, do NOT refresh in between) No MMS change. This MID has no "min amt" rule; it is a different merchant. CURL 1 curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-06a" \ -d '{"merchant_id":"271204640000000","terminal_id":"20464409","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900011","rrn":"RRN000000011","timestamp":"2026-10-01T10:11:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: "min amt" must NOT apply (it belongs to Frog8 Cafe MID 123451234512345) Merchant rules for this MID: MISS → DB LOAD → CACHE SAVE Terminal rules: HIT (shared entry already loaded in TR-5) PASS / FAIL: ______ (Other rules of that merchant may still decide ALLOW/DECLINE; the checks that matter are the log lines above.) TR-7 — Unknown MID No MMS change. CURL 1 curl -s -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-07a" \ -d '{"merchant_id":"000000000000000","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"900012","rrn":"RRN000000012","timestamp":"2026-10-01T10:12:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: Merchant not found Merchant rules: MISS → DB LOAD → CACHE SAVE (empty list is cached) Empty merchant rule list → ALLOW PASS / FAIL: ______ CURL 2 — send the same CURL again (X-Request-Id: tr-07b) Expected: Merchant rules: HIT No merchant-not-found DB load again PASS / FAIL: ______ TR-8 — Refresh authentication No MMS change. CURL 1 — Full refresh curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 / cache cleared. PASS / FAIL: ______ CURL 2 — Per-MID refresh curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Expected: HTTP 200 / merchant cache invalidated. PASS / FAIL: ______ CURL 3 — No authorization curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" Expected: HTTP 401 / UNAUTHORIZED. PASS / FAIL: ______ CURL 4 — Wrong authorization curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: wrong" Expected: HTTP 401 / UNAUTHORIZED. PASS / FAIL: ______ TR-9 — 20 simultaneous requests No MMS change. CURL 1 — Clear cache curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 / REFRESH ALL. PASS / FAIL: ______ Command 2 — Note the count BEFORE sending BEFORE=$(grep -c "DB LOAD" "$LOG"); echo $BEFORE Command 3 — Send 20 requests seq 1 20 | xargs -P 20 -I{} curl -s -o /dev/null \ -X POST "http://demo.ctrmv.com:4085/v1/transaction-rules/evaluate" \ -H "Content-Type: application/json" \ -H "X-Request-Id: tr-09-{}" \ -d '{"merchant_id":"123451234512345","terminal_id":"81748887","bin":"450000","entry_mode":"071","amount":{"value":"50.00","currency":"AED"},"transaction":{"type":"PURCHASE","stan":"120001","rrn":"TR0000000099","timestamp":"2026-10-01T10:13:00Z"},"context":{"channel":"POS","acquirer_id":"ACQ01"}}' Expected: No response output. PASS / FAIL: ______ Command 4 — Count DB LOAD again AFTER=$(grep -c "DB LOAD" "$LOG"); echo $((AFTER-BEFORE)) Expected: 2 (1 merchant load + 1 terminal load, NOT 40) PASS / FAIL: ______ TR-10 — 5-minute TTL No MMS change. CURL 1 — Clear cache (needed: TR-9 left a warm entry, and a leftover entry would show HIT with a shorter TTL) curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/transactionRules/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200 / cache cleared. PASS / FAIL: ______ Step 2 — Populate the cache with the normal transaction CURL (TID 81748887, amount 150). Do not refresh after this. Expected: Merchant rules: MISS → DB LOAD → CACHE SAVE; Terminal rules: MISS → DB LOAD → CACHE SAVE; ALLOW. PASS / FAIL: ______ Command 3 — Check TTL (run in the app's iex / remote console) Cachex.ttl(:transaction_rules_cache, "123451234512345") Expected: {:ok, n} with n <= 300000 ms Run it again a few seconds later: n is smaller PASS / FAIL: ______ WAIT — about 5 minutes. This is the only test where you intentionally wait. CURL 4 — Send the normal transaction CURL again Expected: Merchant rules: MISS → DB LOAD → CACHE SAVE PASS / FAIL: ______ ============================================================== 2. MERCHANT CONFIG ============================================================== MID: 123451234512345 (Frog8 Cafe) Cache: :merchant_config_cache TTL: 5 minutes Merchant refresh CURL (used throughout this section): curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/merchantConfig/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Note: the rules from section 1 must be in a state that lets amount 150 through (min 100 enabled, no max rule below 150) so that the transaction decision is not decided by a rule. "Normal request" = the NORMAL TRANSACTION in the setup (TID 81748887, amount 150, entry_mode 071). MC-1 — Config MISS → HIT No MMS change. CURL 1 — Clear merchant cache: run the merchant refresh CURL. Expected: HTTP 200 / merchant config cache cleared. PASS / FAIL: ______ CURL 2 — Normal request Expected (first request): [MERCHANT CONFIG] Not in cache, loading config from the database merchant_configuration SELECT MCC lookup Country lookup PASS / FAIL: ______ CURL 3 — Same request again Expected: No merchant config loading No merchant_configuration SELECT No MCC SELECT No country SELECT PASS / FAIL: ______ MC-2 — TRANS_CONTACTLESS (merchant setting) Before CURL 1, in MMS: [M-CFG] Frog8 Cafe -> TRANS_CONTACTLESS = 1 (currently "1") CURL 1 — Refresh merchant cache: run the merchant refresh CURL. Expected: HTTP 200 / cache cleared. PASS / FAIL: ______ CURL 2 — Normal request (entry_mode 071 = contactless) Expected: ALLOW. PASS / FAIL: ______ Before CURL 3, in MMS: [M-CFG] Frog8 Cafe -> TRANS_CONTACTLESS = 0 (save). DO NOT refresh. CURL 3 — Normal request again, without refresh Expected: Still ALLOW (cached value is still 1) No merchant config load PASS / FAIL: ______ CURL 4 — Refresh: run the merchant refresh CURL. Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ CURL 5 — Normal request after refresh Expected: DECLINE, code 57 Contactless disabled, TRANS_CONTACTLESS="0" PASS / FAIL: ______ Restore in MMS: [M-CFG] TRANS_CONTACTLESS = 1, then run the merchant refresh CURL again. MC-3 — Device blocked / unblocked (per TID, stored in the merchant's DEVICES setting) Prerequisite (important): the merchant's DEVICES list in MMS is currently EMPTY. A TID that is not listed is treated as NOT blocked and cannot be blocked. Before starting, in MMS: [M-CFG] Frog8 Cafe -> Devices: make sure TID 81748887 is listed with MID 123451234512345 and status = active (1). (In the settings this is a DEVICES entry {"mid":"123451234512345","tid":"81748887","status":"1"}; status "0" = blocked.) Do NOT add TID 12345673 as blocked. Before CURL 1, in MMS: [M-CFG] Set the device TID 81748887 to BLOCKED (status 0). CURL 1 — Refresh: run the merchant refresh CURL. Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ Device/CURL 2 — Normal request with TID 81748887 Expected: DECLINE, code 62 Terminal is inactive [DEVICE] Blocked? YES PASS / FAIL: ______ Before next test, in MMS: [M-CFG] Set the device TID 81748887 to ACTIVE (status 1). Do NOT refresh. Device/CURL 3 — Normal request with TID 81748887 again Expected: Still code 62 (cached status is still blocked) PASS / FAIL: ______ Device/CURL 4 — Normal request with TID 12345673 (same MID, change only terminal_id) Expected: NOT code 62 [DEVICE] Blocked? NO - terminal is not listed (the block belongs to one TID, not the MID) PASS / FAIL: ______ CURL 5 — Refresh: run the merchant refresh CURL. Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ Device/CURL 6 — Normal request with TID 81748887 Expected: NOT code 62 [DEVICE] Blocked? NO status = 1 PASS / FAIL: ______ MC-4 — Receipt message (shown in "footer.message" of the evaluate response) Before the test, in MMS: [M-CFG] Frog8 Cafe -> Receipt message = CACHE-TEST-MSG (currently empty, so the response shows "N/A") Do NOT refresh. Test 1 — Normal request, look at "footer":{"message":...} in the response Expected: OLD message ("N/A"), not CACHE-TEST-MSG. PASS / FAIL: ______ CURL 1 — Refresh: run the merchant refresh CURL. Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ Test 2 — Normal request again Expected: footer message = CACHE-TEST-MSG. PASS / FAIL: ______ Restore in MMS: [M-CFG] Receipt message back to empty, then run the merchant refresh CURL again. MC-5 — Unknown MID No MMS change. Test 1 — Normal request with "merchant_id":"000000000000000" (everything else unchanged) Expected: [MERCHANT CONFIG] No merchant found PASS / FAIL: ______ Test 2 — Send the same request again Expected: No "No merchant found" DB lookup again (cached nil result) PASS / FAIL: ______ Command 3 (app iex / remote console) Cachex.get(:merchant_config_cache, {:config, "000000000000000"}) Expected: {:ok, :__cached_nil__} PASS / FAIL: ______ MC-6 — Refresh validation No MMS change. Test 1 — No merchant_id curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/merchantConfig/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{}' Expected: HTTP 400 PASS / FAIL: ______ Test 2 — Empty merchant_id curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/merchantConfig/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" \ -H "Content-Type: application/json" \ -d '{"merchant_id":""}' Expected: HTTP 400 PASS / FAIL: ______ Test 3 — No Authorization curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/merchantConfig/refreshCache" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Expected: HTTP 401 PASS / FAIL: ______ Test 4 — Wrong Authorization curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/merchantConfig/refreshCache" \ -H "Authorization: wrong" \ -H "Content-Type: application/json" \ -d '{"merchant_id":"123451234512345"}' Expected: HTTP 401 PASS / FAIL: ______ MC-7 — TTL No MMS change. Step 0 — Run the merchant refresh CURL, then send the normal request once so the config is cached. Step 1 — Check TTL (app iex / remote console) Cachex.ttl(:merchant_config_cache, {:config, "123451234512345"}) Expected: {:ok, n} with n <= 300000 ms PASS / FAIL: ______ WAIT — about 5 minutes. Step 2 — Send the normal request Expected: [MERCHANT CONFIG] Not in cache, loading config from the database PASS / FAIL: ______ ============================================================== 3. GLOBAL FALLBACK ============================================================== Two different behaviours: Setting Behaviour TRANS_CONTACTLESS and similar settings Merged into the merchant cache, so can stay stale POS_ENTRY_BLOCKED Read live after a global refresh MCC_BLOCKED_LIST Read after a global refresh COUNTRY_BLOCKED_LIST Read after a global refresh Important: after every Global Settings refresh, wait about 2 seconds. Global refresh CURL: curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/globalSettings/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Note: the global table currently has NO rows for TRANS_CONTACTLESS, POS_ENTRY_BLOCKED, MCC_BLOCKED_LIST or COUNTRY_BLOCKED_LIST. Where a test says "Global X = value", create that key in [GLOBAL] if it is missing. GF-1 — Global value used when merchant has no value Before CURL 1, in MMS: [M-CFG] Frog8 Cafe -> TRANS_CONTACTLESS = empty / not set [GLOBAL] TRANS_CONTACTLESS = 1 CURL 1 — Global refresh (global refresh CURL). Wait 2 seconds. Expected: HTTP 200. CURL 2 — Merchant refresh (merchant refresh CURL) Expected: HTTP 200. PASS / FAIL: ______ Test transaction — Normal request (entry_mode 071) Expected: ALLOW Log says TRANS_CONTACTLESS came from the global defaults, value 1 Contactless enabled PASS / FAIL: ______ GF-2 — Merchant overrides global Before CURL 1, in MMS: [GLOBAL] TRANS_CONTACTLESS = 0 [M-CFG] Frog8 Cafe -> TRANS_CONTACTLESS = 1 CURL 1 — Global refresh. Wait 2 seconds. CURL 2 — Merchant refresh. Expected for both: HTTP 200. PASS / FAIL: ______ Test transaction — Normal request (entry_mode 071) Expected: ALLOW Merchant value 1 wins over global 0 Log must NOT say the value came from global defaults PASS / FAIL: ______ GF-3 — Known stale-cache gap Before CURL 1, in MMS: [M-CFG] Frog8 Cafe -> TRANS_CONTACTLESS = empty / not set [GLOBAL] TRANS_CONTACTLESS = 1 CURL 1 — Global refresh. Wait 2 seconds. CURL 2 — Merchant refresh. Expected: both HTTP 200. PASS / FAIL: ______ Test 1 — Normal request (entry_mode 071) Expected: ALLOW. PASS / FAIL: ______ Before CURL 3, in MMS: [GLOBAL] TRANS_CONTACTLESS: 1 → 0 (save). Do NOT refresh the merchant. CURL 3 — Global refresh ONLY. Wait 2 seconds. Expected: HTTP 200. PASS / FAIL: ______ Test 2 — Normal request Expected: Still ALLOW (old global value 1 is still inside the cached merchant config) - this is the known gap. PASS / FAIL: ______ CURL 4 — Merchant refresh. Expected: HTTP 200 / INVALIDATE. PASS / FAIL: ______ Test 3 — Normal request Expected: DECLINE, code 57 Contactless disabled, TRANS_CONTACTLESS="0" PASS / FAIL: ______ Restore in MMS: [M-CFG] TRANS_CONTACTLESS = 1, then run the merchant refresh CURL. GF-4 — POS_ENTRY_BLOCKED Before CURL 1, in MMS: [GLOBAL] POS_ENTRY_BLOCKED = 801 CURL 1 — Global refresh. Wait 2 seconds. Expected: HTTP 200. PASS / FAIL: ______ Test 2 — Normal request with "entry_mode":"801" Expected: DECLINE, code 57 POS entry blocked (POS_ENTRY_BLOCKED=801) No merchant refresh required PASS / FAIL: ______ Test 3 — Normal request with "entry_mode":"071" Expected: NOT blocked by POS_ENTRY_BLOCKED. PASS / FAIL: ______ Restore in MMS: [GLOBAL] POS_ENTRY_BLOCKED back to the original value (or remove it), then global refresh. GF-5 — MCC and country blocked lists Test 1 — Normal request, then read the merchant's MCC from the log (grep "MCC" in the log after the request) MCC = ______ Expected: the MCC is found from cache. PASS / FAIL: ______ Before CURL 1, in MMS: [GLOBAL] MCC_BLOCKED_LIST: add that MCC (same format as the list already uses). CURL 1 — Global refresh. Wait 2 seconds. Expected: HTTP 200. PASS / FAIL: ______ Test 2 — Normal request Expected: DECLINE, code 57, "Transaction not permitted to merchant" Log: MCC is BLOCKED MCC comes from cache, no extra MCC DB lookup PASS / FAIL: ______ Restore in MMS: [GLOBAL] MCC_BLOCKED_LIST back to the original, global refresh. Country test: repeat the same steps with the merchant's country code in [GLOBAL] COUNTRY_BLOCKED_LIST (Test 1 reads the country from the log). Expected log: country is BLOCKED, code 57. Then restore the original list in MMS and run the global refresh. GF-6 — Global refresh authentication No MMS change. CURL 1 — Correct key curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/globalSettings/refreshCache" \ -H "Authorization: 9lBhDRezUjLvZ9L" Expected: HTTP 200. PASS / FAIL: ______ CURL 2 — No key curl -i -X POST "http://demo.ctrmv.com:4085/api/admin/globalSettings/refreshCache" Expected: HTTP 401 / UNAUTHORIZED. PASS / FAIL: ______ ============================================================== 4. DEVICE TESTS (do these after the matching CURL tests above) ============================================================== Test MMS change (scope + where) Refresh? Device expected D-TR-1 [M-RULE] "min amt" MIN_AMOUNT = 100, enabled Yes, before 1st test AED 50 on TID 81748887 DECLINE D-TR-1 rpt No change No Same DECLINE, cache HIT D-TR-2 [M-RULE] "min amt" MIN 100 → 30 No AED 50 still DECLINE D-TR-2 b No change Yes (MID refresh) AED 50 APPROVED D-TR-3 [T-RULE] TEST-CACHE-MAX-TERM = 500 on TID 81748887, Yes TID 81748887 + AED 600 DECLINE [M-RULE] "max amt" disabled D-TR-3 b No change No TID 12345673 + AED 600 ALLOW (terminal rule does not apply to it) D-TR-4 [M-RULE] "min amt" disabled No AED 50 still DECLINE D-TR-4 b No change Yes (MID refresh) AED 50 APPROVED ============================================================== 5. SIMPLE REFRESH RULE — KEEP THIS BESIDE YOU ============================================================== What you do in MMS Refresh? What you are testing Set a value and want it immediately YES New value reaches cache Change a value and want to test stale cache NO Old cached value remains Want to test natural expiry NO, wait ~5 min TTL expiry Change a Global live setting Global refresh New global value Change a merged merchant/global setting Merchant refresh New value reaches merchant cache Global TRANS_CONTACTLESS changed after it was cached Global refresh alone is NOT Known stale-cache gap enough Device block / unblock Merchant refresh New device status Transaction rule (merchant or terminal scope) Transaction-rules refresh New rule Most important: for the stale-cache tests, do not wait 5 minutes. Test immediately after changing MMS so the old cached value is still present. Only the TTL tests (TR-10, MC-7) require you to actually wait around 5 minutes.