oxedyne/fe2o3/fe2o3_steel/tests/rig/README.md
4.1 KiB, 4 runs
created by r1870400018:14547, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | # The rig |
| 2 | |
| 3 | Starts a real Steel -- its own wallet, its own Ozone, its own certificates -- and |
| 4 | drives it over HTTPS with a real dashboard session. Then throws it all away. |
| 5 | |
| 6 | `cargo test` does not run this. It is a shell script, it wants a free port and |
| 7 | half a minute, and it is here to be run by hand: |
| 8 | |
| 9 | ``` |
| 10 | fe2o3_steel/tests/rig/run.sh |
| 11 | ``` |
| 12 | |
| 13 | Nothing else is needed. It builds the binary, makes a wallet, starts a server in |
| 14 | a temporary directory, runs the checks, and stops. |
| 15 | |
| 16 | ## Why it exists |
| 17 | |
| 18 | An in-crate test asks the code whether it works. The code says yes. That is worth |
| 19 | something, and it is not worth what it looks like: a DKIM signer in this workspace |
| 20 | sat broken for three months behind tests that agreed with it. |
| 21 | |
| 22 | Some things cannot be asked from inside at all. Whether the dashboard's cookie |
| 23 | reaches a handler is a fact about `Path=/admin` and a browser, not about a |
| 24 | function. The composer's whole design rests on it, and the route it replaced was |
| 25 | gated on a session that could never arrive -- a gate that refused its own author |
| 26 | exactly as it refused a stranger, and therefore looked correct from every angle |
| 27 | except this one. |
| 28 | |
| 29 | So the checks here are the ones only an outside caller can make: that an |
| 30 | anonymous write is refused, that a draft is served to nobody while its author can |
| 31 | still read it, that a renamed post takes its old key with it, that a deleted post |
| 32 | stays deleted through an import, and that a slug with a path in it writes nothing. |
| 33 | |
| 34 | ## What it is not |
| 35 | |
| 36 | It is not a substitute for the unit tests, and it does not run in CI. It is slow, |
| 37 | it binds a port, and it will fail on a machine where port 9443 is busy |
| 38 | (`RIG_PORT=nnnn` moves it). |
| 39 | |
| 40 | It knows some things that cost an afternoon to learn, and they are in `run.sh` |
| 41 | where they bite: |
| 42 | |
| 43 | - **Wallet creation needs a terminal.** It reads the passphrase through |
| 44 | crossterm's raw mode, so a pipe will not do -- hence the pty in |
| 45 | `make_wallet.py`. In raw mode Enter is a carriage return, not a newline. |
| 46 | - **`server -d` or nothing.** Without `-d` a first run refuses to start in |
| 47 | production mode and exits 0, silently, having said nothing about why. |
| 48 | - **stdin must stay open.** The server keeps a shell beside the listener, and an |
| 49 | EOF on stdin ends the process. Hence the fifo. |
| 50 | - **`dev_cfg` is a required config block.** Every `FromDatMap` field is, unless it |
| 51 | says otherwise. |
| 52 | - **Run one rig at a time.** A second run while another is held (`RIG_HOLD=1`) |
| 53 | produces false failures in `console_rig`, which authenticates over a WebSocket |
| 54 | and will find the wrong server. It looks exactly like a gate regression — a |
| 55 | non-admin getting 200 instead of 403 — and is not one. Kill the held rig first. |
| 56 | |
| 57 | ## The passphrase |
| 58 | |
| 59 | `rig-test-passphrase-not-a-secret`, in the clear, on purpose. It protects a |
| 60 | wallet that exists for thirty seconds and holds nothing. |
| 61 | |
| 62 | ## The byte range rig |
| 63 | |
| 64 | `range.sh` is the second script here, and it answers a different question: does a |
| 65 | window of a file, asked for over the wire, arrive as the bytes that are actually |
| 66 | in the file at that offset? |
| 67 | |
| 68 | ``` |
| 69 | fe2o3_steel/tests/rig/range.sh |
| 70 | RIG_TRANSCRIPT=1 fe2o3_steel/tests/rig/range.sh # print the exchanges too |
| 71 | ``` |
| 72 | |
| 73 | It serves a ten megabyte file of a counting pattern and drives it with curl: |
| 74 | `-r 0-99`, `-r 100-`, `-r -50`, a window from the middle, a start past the end, a |
| 75 | suffix longer than the file, a unit that is not `bytes`, several ranges at once, |
| 76 | an empty file, a one byte file, and a `HEAD`. Every body is compared with `cmp` |
| 77 | against the same window cut out of the file with `dd` -- because a response of the |
| 78 | right length carrying the wrong bytes is exactly the failure a length check |
| 79 | misses, and exactly what a viewer sees as a video that will not play. |
| 80 | |
| 81 | The whole sweep runs twice, once as curl chooses and once under `--http2`. Steel |
| 82 | offers only `http/1.1` over ALPN today, so `--http2` negotiates 1.1 and falls |
| 83 | back; the second pass is there so that the day ALPN offers `h2`, the sweep already |
| 84 | covers it. |
| 85 | |
| 86 | The last check asks for two windows down one connection. A body that is shorter or |
| 87 | longer than the `Content-Length` promised desynchronises every message after it, |
| 88 | and that can only be seen when a second request follows the first on the same |
| 89 | connection. |