# Mock Netbay gateway (send testing)

A stand-in for the customs gateway that the SPN send path talks to, so the send
can be exercised without the UAT gateway -- whose latency is worse than prod and
cannot be told to fail in a chosen shape.

It replaces the gateway **by configuration, not by code**. Nothing in
`gen_decl_imp_xml.inc`, `USERFUNC.inc` or `MAINVAR.inc` changes.

## Why not a fake-send flag

A `FAKE_SEND_TO_GW`-style flag (the pattern used on the PRE side) short-circuits
at the `sendToGateway()` call boundary, so it never exercises curl, the client
timeout, the SOAP parsing or `genXmlResponse()` -- exactly the layers the
duplicate-submission work has to prove. It also means editing
`gen_decl_imp_xml.inc`, which is TIS-620 and is the one file every declaration
passes through.

## Files

| file | what it is |
|---|---|
| `gateway.php` | the mock endpoint (serves `logIn` + `sendMessage`) |
| `mock_gw.json.example` | copy to `mock_gw.json`; behaviour knobs, re-read per request |
| `seed_conf_gw_endpoint.php` | writes the `conf_gw_endpoint` row that points a profile at the mock |
| `contract_test.php` | selftest -- replays the real envelopes through the real parsers' logic |
| `mock_gw.on` | **you create this**; without it the endpoint returns 403 |
| `mock_gw_requests.log` | one line per call -- this is the test evidence |
| `mock_gw_state.json` | per-reference call counter, only used by `sequence` |

## Setup

1. Put this folder where Apache serves the SPN app and confirm the URL answers:

       curl -i http://127.0.0.1/<path>/IE5DEV.shippingnet/script/mock_gw/gateway.php
       -> 403 while mock_gw.on is absent

2. Arm it on the test node only:

       cp mock_gw.json.example mock_gw.json
       touch mock_gw.on

3. Point a **test profile** at it (dry run first -- the script never reads
   MAINVAR, every connection detail is an argument):

       php seed_conf_gw_endpoint.php --host=10.5.209.191:43006 --db=mdhspn_clone \
           --user=... --pass=... --id=90 \
           --url=http://127.0.0.1/<path>/IE5DEV.shippingnet/script/mock_gw/gateway.php \
           --profile=<profilecode>
       # add --apply to write

4. Delete any stale `digitalsign/config_gw/configGW90.txt` on each node.
   `getConfigGateWayEndpoint()` rewrites that file only when it is missing or the
   row's `last_update` is today.

5. **Verify the directory is not readable** -- `.htaccess` only works where
   `AllowOverride` is on, so check rather than assume:

       curl -o /dev/null -w '%{http_code}
' http://<host>/mock_gw/README.md
       curl -o /dev/null -w '%{http_code}
' http://<host>/mock_gw/contract_test.php

   Both must be 403. Observed on 10.6.208.109 on 2026-09-16 before the guards
   were deployed: the log, the config and the arming flag were all fetchable at
   200, and `contract_test.php` EXECUTED when fetched -- it armed the mock,
   overwrote `mock_gw.json` and deleted `mock_gw_requests.log`. If either check
   returns 200, deny those files in the vhost config instead.

## Selftest

    php -S 127.0.0.1:8899 -t .        # in this folder, another shell
    php contract_test.php             # 14 checks, expects PASS 14 FAIL 0

It arms the mock, exercises every mode, and disarms it again. Pass a base URL to
check a real deployment instead: `php contract_test.php http://node/.../gateway.php`

## Behaviour knobs

`mock_gw.json` -- see `mock_gw.json.example` for the full list. Per-request
overrides `?mode=...&delay_ms=...` beat the file, for ad-hoc probes.

Send modes: `ok`, `error`, `expired`, `null`, `fault`, `garbage`, `hang`, `empty`.
Authen modes: `ok`, `error`, `fault`, `hang`, `empty`.

## The test Phase 0 exists to pass

Make one reference slow past the consumer's `SPN_SEND_HTTP_TIMEOUT` (100s), then
fast:

```json
"refs": {
  "BBGD200293924": {
    "sequence": [
      { "mode": "ok", "delay_ms": 150000 },
      { "mode": "ok", "delay_ms": 0 }
    ]
  }
}
```

Enqueue that one declaration and watch `mock_gw_requests.log`.

- **Two `sendMessage RECV` lines for that referenceNo = the declaration was
  submitted twice.** That is the bug, reproduced.
- One `RECV` line = the fix holds.

(Each request logs a `RECV` line on arrival and a `REPLY` line on the way out,
so count `RECV` only.)

The log is the assertion; nothing else in the stack records a duplicate, because
the gateway is a pass-through and Customs' duplicate verdict comes back later on
a separate download-response path.

## Safety

- Inert without `mock_gw.on`. **Never create that file on prod.**
- `download.endpoint.cusReceive` in the seeded config points at the mock too, so
  a test profile cannot pull real customs responses; the mock answers it 400 so a
  download task fails loudly rather than quietly reaching the real gateway.
- `seed_conf_gw_endpoint.php` is dry-run by default and refuses a blocklist of
  known production hosts and database names.
- `garbage` mode deliberately crashes the caller (`xpath()` on the `false` that
  `simplexml_load_string()` returns, a PHP 5.6 fatal). That crash is the point of
  the mode -- it reproduces a known unguarded parse in the send path.
