|
| 1 | +# JS examples against the local runner |
| 2 | + |
| 3 | +The JS SDK repository ([aws/aws-durable-execution-sdk-js]) has about 125 |
| 4 | +example tests. Each test runs a durable function and checks its result and |
| 5 | +history. The JS repository runs them against real Lambda. This directory runs |
| 6 | +the same tests against the Python local runner in |
| 7 | +`packages/aws-durable-execution-sdk-python-testing`. Everything runs on your |
| 8 | +machine. No AWS account or credentials are needed. |
| 9 | + |
| 10 | +Run it after you change the testing package. The same script runs in CI |
| 11 | +(`.github/workflows/js-examples.yml`) on pull requests that touch the testing |
| 12 | +package. |
| 13 | + |
| 14 | +[aws/aws-durable-execution-sdk-js]: https://github.com/aws/aws-durable-execution-sdk-js |
| 15 | + |
| 16 | +## Quick start |
| 17 | + |
| 18 | +You need `git`, Python 3.11 or newer, and Node.js 22 or newer with `npm`. |
| 19 | + |
| 20 | +```sh |
| 21 | +scripts/js-examples/run.sh # every example, about 2.5 minutes |
| 22 | +scripts/js-examples/run.sh invoke # only tests whose path matches "invoke" |
| 23 | +``` |
| 24 | + |
| 25 | +The first run takes a few more minutes. It does two setup steps: |
| 26 | + |
| 27 | +1. It creates `scripts/js-examples/.venv` and installs the SDK and the testing |
| 28 | + package from your checkout. The install is editable, so later runs use |
| 29 | + your current source without reinstalling. |
| 30 | +2. It clones the JS SDK into `~/.cache/dex-js-examples/js-sdk` at the commit |
| 31 | + in `js-sdk.ref`, then runs `npm ci` and builds the example bundles. Later |
| 32 | + runs reuse the build until the commit changes. |
| 33 | + |
| 34 | +The last lines of the output give the result: |
| 35 | + |
| 36 | +```text |
| 37 | +[js-examples] selected 125 test files: 121 passed (0 on retry), 0 failed, 4 have no cloud tests, 0 not run |
| 38 | +``` |
| 39 | + |
| 40 | +The script exits 0 when every selected test passes and 1 otherwise. It exits |
| 41 | +2 when setup fails. |
| 42 | + |
| 43 | +## Common tasks |
| 44 | + |
| 45 | +| Task | Command | |
| 46 | +| --- | --- | |
| 47 | +| Run tests whose path matches a regex | `run.sh step/ 'wait-for-callback/.*heartbeat'` | |
| 48 | +| Test against another JS SDK branch, tag or commit | `run.sh --js-ref main` | |
| 49 | +| Use your own JS SDK checkout | `run.sh --js-dir ~/src/aws-durable-execution-sdk-js` | |
| 50 | +| Skip the JS build for that checkout | `run.sh --js-dir ~/src/aws-durable-execution-sdk-js --no-build` | |
| 51 | +| Force a JS rebuild | `run.sh --rebuild` | |
| 52 | +| See runner debug logs | `run.sh --log-level DEBUG hello-world` | |
| 53 | +| Pass options to jest | `run.sh invoke -- --verbose` | |
| 54 | +| All options | `run.sh --help` | |
| 55 | + |
| 56 | +With `--js-dir`, the harness builds your checkout in place and changes nothing |
| 57 | +else in it. Its lock and build stamp live in `~/.cache/dex-js-examples`. |
| 58 | + |
| 59 | +## When a test fails |
| 60 | + |
| 61 | +Each run writes its files to `scripts/js-examples/.out/`, or to the directory |
| 62 | +given with `--out`: |
| 63 | + |
| 64 | +| File | Contents | |
| 65 | +| --- | --- | |
| 66 | +| `report.json` | Passed, failed, flaky and skipped test files | |
| 67 | +| `runner.log` | The Python local runner, which is the code under test | |
| 68 | +| `shim.log` | One line per handler invocation: request ID, function, duration, outcome | |
| 69 | +| `proxy.log` | The invoke proxy | |
| 70 | +| `jest-*.json` | Raw jest results | |
| 71 | +| `maps/` | The function maps generated from the examples' `template.yml` | |
| 72 | + |
| 73 | +To investigate one example, run it alone with debug logs: |
| 74 | + |
| 75 | +```sh |
| 76 | +scripts/js-examples/run.sh --log-level DEBUG --retries 0 step/interrupted-no-retry -- --verbose |
| 77 | +``` |
| 78 | + |
| 79 | +Set `PROXY_DUMP_DIR=/tmp/dump` to also save every history, state and |
| 80 | +checkpoint response the tests read. |
| 81 | + |
| 82 | +A test file that fails is run once more on fresh servers. If it then passes, |
| 83 | +the run succeeds and the file is reported as `FLAKY`. A few examples race real |
| 84 | +timers, for example parallel branches that must checkpoint within |
| 85 | +milliseconds of each other, and they can fail on a loaded machine. Use |
| 86 | +`--retries 0` to see every failure. |
| 87 | + |
| 88 | +## How it works |
| 89 | + |
| 90 | +Four processes run on the loopback, on free ports chosen for each run: |
| 91 | + |
| 92 | +```text |
| 93 | +jest (CloudDurableTestRunner) |
| 94 | + -> invoke_proxy.py turns Lambda Invoke into POST /start-durable-execution; |
| 95 | + forwards every other request unchanged |
| 96 | + -> local runner from this checkout: the code under test |
| 97 | + -> lambda-shim.cjs answers the runner's Invoke calls by running the |
| 98 | + built example bundles |
| 99 | +``` |
| 100 | + |
| 101 | +1. jest runs the examples in integration mode (`NODE_ENV=integration`), the |
| 102 | + mode the JS repository uses against real Lambda. The JS test runner starts |
| 103 | + each execution with Lambda `Invoke` and reads the execution ARN from the |
| 104 | + response. `invoke_proxy.py` gives the local runner that endpoint. |
| 105 | +2. The local runner invokes handlers at its `--lambda-endpoint`, which is the |
| 106 | + shim. It resolves chained-invoke targets with `--function-configs`. |
| 107 | +3. `lambda-shim.cjs` implements Lambda `Invoke` for the example bundles. It |
| 108 | + runs each function on worker threads. A worker handles one invocation at a |
| 109 | + time and is reused afterwards, like a warm Lambda sandbox. When an |
| 110 | + invocation runs past the function's `Timeout`, the shim returns Lambda's |
| 111 | + `Sandbox.Timedout` error and terminates the worker. Some examples depend |
| 112 | + on this, for example `step/interrupted-no-retry`. |
| 113 | +4. `run.py` reads the examples' `template.yml` and writes the three maps the |
| 114 | + other processes need: test file to function name (for jest), function |
| 115 | + name to bundle, timeout and environment (for the shim), and function name |
| 116 | + to `DurableConfig` (for the runner). |
| 117 | + |
| 118 | +The runner emulates region `us-west-2` and account `123456789012`. The proxy |
| 119 | +and the shim use the same values. If they differ, the runner rejects chained |
| 120 | +invokes whose target ARN names another region or account. |
| 121 | + |
| 122 | +## Which tests run |
| 123 | + |
| 124 | +Every `*.test.ts` under the examples' `src/examples` is selected, except for |
| 125 | +three groups: |
| 126 | + |
| 127 | +- `otel/` examples. They export spans to an OpenTelemetry collector, which the |
| 128 | + harness does not run. |
| 129 | +- Examples marked `localOnly` in the JS catalog. `template.yml` omits them, |
| 130 | + and the JS repository's integration run skips them too. The output reports |
| 131 | + how many were skipped. |
| 132 | +- Tests without "cloud" in their name. The integration jest config runs only |
| 133 | + those. A file with no such test is reported as "no cloud tests". |
| 134 | + |
| 135 | +## Known failures |
| 136 | + |
| 137 | +`known-failures.txt` lists test files that are expected to fail, one per line, |
| 138 | +with the reason after `#`. It is empty today. A listed file that fails does |
| 139 | +not fail the run. A listed file that passes does fail the run, so that the |
| 140 | +list stays accurate. |
| 141 | + |
| 142 | +## Updating the pinned JS SDK |
| 143 | + |
| 144 | +Pull requests test against the commit in `js-sdk.ref`. So a failure on a pull |
| 145 | +request points at the Python change, not at a new JS commit. The weekly CI |
| 146 | +run tests against JS `main` instead. When it is green, move the pin: |
| 147 | + |
| 148 | +```sh |
| 149 | +git ls-remote https://github.com/aws/aws-durable-execution-sdk-js.git refs/heads/main |
| 150 | +# write that commit into scripts/js-examples/js-sdk.ref, then: |
| 151 | +scripts/js-examples/run.sh |
| 152 | +``` |
| 153 | + |
| 154 | +## Harness tests |
| 155 | + |
| 156 | +`test_run.py` tests the template parsing, test selection and result handling |
| 157 | +in `run.py`. It needs no JS SDK: |
| 158 | + |
| 159 | +```sh |
| 160 | +scripts/js-examples/.venv/bin/python -m pytest scripts/js-examples |
| 161 | +``` |
0 commit comments