Skip to content

Commit 9347c3b

Browse files
CCOR-13194 - Switch integration tests to run against sm v4 server and sdkdev for v5 (#127)
* testing integration tests against alternate servers * adjust notes around integration testing. add another test server - now we have all 5 to see what is the least flaky * make http bin url dynamic b/c different clusters have it on different host name, make timeouts more sane in one test, add some missing task cleanup logic in tests * add new constants file that can grab from env before setting constant * add some retry on things that seem to be particularly painfully flaky * add missing file * additional retry and diagnostic within parts of the integration testing. * add missing helper util file * cancel in progress * rather aggressive retry fixes for test flakiness * add missing file * further retry and timeout tuning * reduce pr integration test runs to sm-v4 and sdkdev-v5 * trying to sort out inscrutible and inexplicable issues. without a whole lot of luck * helper script to make running test shards locally easy * require v4 integration tests to pass * account for difference in how asyncComplete http task status works when http task is system task (v4 - no api orch) or worker task (v5 with api orch) in the way a test detects waiting http task * exclude integration-tests/** from code coverage requirement * restrict the asyncComplete test to v5 so it doesn't introduce the only weird v4-vs-v5-branching-inside-one-test in the entire test suite
1 parent 45e2418 commit 9347c3b

36 files changed

Lines changed: 773 additions & 247 deletions

.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ CONDUCTOR_AUTH_SECRET=""
66
CONDUCTOR_MAX_HTTP2_CONNECTIONS=
77

88
CONDUCTOR_TLS_INSECURE=
9-
CONDUCTOR_DISABLE_HTTP2=
9+
CONDUCTOR_DISABLE_HTTP2=
10+
11+
# Hostname of the httpbin service reachable from the Conductor server (default: httpbin-server)
12+
HTTPBIN_SERVICE_HOSTNAME=

.github/workflows/pull_request.yml

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ on:
77
pull_request:
88
workflow_dispatch:
99

10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
1014
jobs:
1115
linter:
1216
runs-on: ubuntu-latest
@@ -79,19 +83,17 @@ jobs:
7983
name: codecov-unit-node-${{ matrix.node-version }}
8084
fail_ci_if_error: false
8185

82-
# Integration tests (v5): lower max-parallel reduces 502/503 from the shared Conductor server
83-
# but makes CI slower without eliminating flakes entirely — feel free to experiment.
84-
# Sharding (--shard i/N) splits the suite so each job runs ~1/N of tests.
85-
integration-tests:
86+
# Integration tests (v5 sdkdev): mirrors integration-tests but targets the sdkdev environment.
87+
integration-tests-v5-sdkdev:
8688
runs-on: ubuntu-latest
8789
timeout-minutes: 25
8890
strategy:
8991
fail-fast: false
90-
max-parallel: 2
92+
max-parallel: 3
9193
matrix:
9294
node-version: [20, 22, 24]
9395
shard: [1, 2, 3]
94-
name: Node.js v${{ matrix.node-version }} - integration v5 (shard ${{ matrix.shard }}/3)
96+
name: Node.js v${{ matrix.node-version }} - integration v5 sdkdev (shard ${{ matrix.shard }}/3)
9597
steps:
9698
- name: Checkout
9799
uses: actions/checkout@v4
@@ -111,45 +113,45 @@ jobs:
111113
- name: Install Dependencies
112114
if: steps.cache.outputs.cache-hit != 'true'
113115
run: npm ci
114-
- name: Run integration tests (v5) shard ${{ matrix.shard }}/3
116+
- name: Run integration tests (v5 sdkdev) shard ${{ matrix.shard }}/3
115117
run: npm run test:integration:v5 -- --ci --coverage --runInBand --testTimeout=120000 --shard=${{ matrix.shard }}/3 --reporters=default --reporters=github-actions --reporters=jest-junit
116118
env:
117119
ORKES_BACKEND_VERSION: "5"
118-
CONDUCTOR_SERVER_URL: ${{ vars.SERVER_URL }}
119-
CONDUCTOR_AUTH_KEY: ${{ secrets.AUTH_KEY }}
120-
CONDUCTOR_AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
120+
CONDUCTOR_SERVER_URL: ${{ vars.SDKDEV_V5_SERVER_URL }}
121+
CONDUCTOR_AUTH_KEY: ${{ vars.SDKDEV_V5_AUTH_KEY }}
122+
CONDUCTOR_AUTH_SECRET: ${{ secrets.SDKDEV_V5_AUTH_SECRET }}
121123
CONDUCTOR_REQUEST_TIMEOUT_MS: "120000"
122124
CONDUCTOR_RETRY_SERVER_ERRORS: "true"
123-
JEST_JUNIT_OUTPUT_NAME: integration-v5-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
125+
HTTPBIN_SERVICE_HOSTNAME: "httpbin"
126+
JEST_JUNIT_OUTPUT_NAME: integration-v5-sdkdev-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
124127
- name: Publish Test Results
125128
uses: dorny/test-reporter@v2
126129
if: ${{ !cancelled() }}
127130
with:
128-
name: integration v5 Node ${{ matrix.node-version }} shard ${{ matrix.shard }}/3
129-
path: reports/integration-v5-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
131+
name: integration v5 sdkdev Node ${{ matrix.node-version }} shard ${{ matrix.shard }}/3
132+
path: reports/integration-v5-sdkdev-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
130133
reporter: jest-junit
131134
- name: Upload coverage to Codecov
132135
if: always()
133136
uses: codecov/codecov-action@v4
134137
with:
135138
token: ${{ secrets.CODECOV_TOKEN }}
136139
files: ./coverage/lcov.info
137-
flags: integration-v5
138-
name: codecov-integration-v5-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}
140+
flags: integration-v5-sdkdev
141+
name: codecov-integration-v5-sdkdev-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}
139142
fail_ci_if_error: false
140143

141-
# Integration tests (v4): same sharding as v5. v4 fails in CI (passes locally); do not block PRs.
142-
integration-tests-v4:
144+
# Integration tests (v4 sm): mirrors integration-tests-v4 but targets the sm environment.
145+
integration-tests-v4-sm:
143146
runs-on: ubuntu-latest
144147
timeout-minutes: 25
145-
continue-on-error: true
146148
strategy:
147149
fail-fast: false
148150
max-parallel: 1
149151
matrix:
150152
node-version: [20, 22, 24]
151153
shard: [1, 2, 3]
152-
name: Node.js v${{ matrix.node-version }} - integration v4 (shard ${{ matrix.shard }}/3)
154+
name: Node.js v${{ matrix.node-version }} - integration v4 sm (shard ${{ matrix.shard }}/3)
153155
steps:
154156
- name: Checkout
155157
uses: actions/checkout@v4
@@ -169,29 +171,29 @@ jobs:
169171
- name: Install Dependencies
170172
if: steps.cache.outputs.cache-hit != 'true'
171173
run: npm ci
172-
- name: Run integration tests (v4) shard ${{ matrix.shard }}/3
174+
- name: Run integration tests (v4 sm) shard ${{ matrix.shard }}/3
173175
run: npm run test:integration:v4 -- --ci --coverage --runInBand --testTimeout=120000 --shard=${{ matrix.shard }}/3 --reporters=default --reporters=github-actions --reporters=jest-junit
174176
env:
175177
ORKES_BACKEND_VERSION: "4"
176-
CONDUCTOR_SERVER_URL: ${{ vars.SERVER_URL_V4 }}
177-
CONDUCTOR_AUTH_KEY: ${{ secrets.AUTH_KEY_V4 }}
178-
CONDUCTOR_AUTH_SECRET: ${{ secrets.AUTH_SECRET_V4 }}
178+
CONDUCTOR_SERVER_URL: ${{ vars.SM_V4_SERVER_URL }}
179+
CONDUCTOR_AUTH_KEY: ${{ vars.SM_V4_AUTH_KEY }}
180+
CONDUCTOR_AUTH_SECRET: ${{ secrets.SM_V4_AUTH_SECRET }}
179181
CONDUCTOR_REQUEST_TIMEOUT_MS: "120000"
180182
CONDUCTOR_RETRY_SERVER_ERRORS: "true"
181-
JEST_JUNIT_OUTPUT_NAME: integration-v4-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
183+
JEST_JUNIT_OUTPUT_NAME: integration-v4-sm-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
182184
- name: Publish Test Results
183185
uses: dorny/test-reporter@v2
184186
if: ${{ !cancelled() }}
185187
with:
186-
name: integration v4 Node ${{ matrix.node-version }} shard ${{ matrix.shard }}/3
187-
path: reports/integration-v4-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
188+
name: integration v4 sm Node ${{ matrix.node-version }} shard ${{ matrix.shard }}/3
189+
path: reports/integration-v4-sm-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}-test-results.xml
188190
reporter: jest-junit
189191
- name: Upload coverage to Codecov
190192
if: always()
191193
uses: codecov/codecov-action@v4
192194
with:
193195
token: ${{ secrets.CODECOV_TOKEN }}
194196
files: ./coverage/lcov.info
195-
flags: integration-v4
196-
name: codecov-integration-v4-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}
197+
flags: integration-v4-sm
198+
name: codecov-integration-v4-sm-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}
197199
fail_ci_if_error: false

SDK_DEVELOPMENT.md

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -416,19 +416,89 @@ npm run test:unit # Unit tests only
416416

417417
### Integration tests (E2E)
418418

419-
Against a real Conductor server. Location: `src/integration-tests/`.
419+
Against a real Orkes Conductor (enterprise) server. Location: `src/integration-tests/`.
420420

421421
```bash
422422
# Full suite
423-
CONDUCTOR_SERVER_URL=http://localhost:8080 \
424-
CONDUCTOR_AUTH_KEY=key CONDUCTOR_AUTH_SECRET=secret \
425-
ORKES_BACKEND_VERSION=5 \
426-
npm run test:integration:orkes-v5
423+
npm run test:integration:v5
427424

428425
# Single file
429-
npx jest --force-exit --testPathPatterns="AuthorizationClient"
426+
npx jest --force-exit --detectOpenHandles --testPathPatterns="AuthorizationClient"
430427
```
431428

429+
Server credentials are read from environment variables. You can export them in your
430+
shell or put them in a `.env` file in the repo root (loaded automatically by
431+
`jest.config.mjs` via `dotenv`). To use a `.env` file, copy the example:
432+
433+
```bash
434+
cp .env.example .env
435+
```
436+
437+
Required variables plus two that CI sets for flake resilience:
438+
439+
```bash
440+
CONDUCTOR_SERVER_URL="https://your-cluster.orkesconductor.io/api"
441+
CONDUCTOR_AUTH_KEY="your-key"
442+
CONDUCTOR_AUTH_SECRET="your-secret"
443+
CONDUCTOR_REQUEST_TIMEOUT_MS=120000
444+
CONDUCTOR_RETRY_SERVER_ERRORS=true
445+
```
446+
447+
### Replicating CI matrix shards locally
448+
449+
CI runs integration tests as a matrix of **3 Node versions (20, 22, 24) x 3 shards**,
450+
using Jest's built-in `--shard=i/N` flag. Each shard runs ~1/3 of the test files
451+
deterministically, so shard 1/3 always contains the same files for a given test suite.
452+
453+
To reproduce a specific CI cell locally you need two things: the right Node version
454+
and the right shard flag.
455+
456+
#### Node version switching with fnm
457+
458+
Install [fnm](https://github.com/Schniz/fnm) (Fast Node Manager) if you don't have it.
459+
If you currently have Node installed directly via Homebrew, remove it first so the two
460+
don't conflict:
461+
462+
```bash
463+
brew remove node # remove Homebrew-managed Node if present
464+
brew install fnm
465+
eval "$(fnm env --use-on-cd)" # add this line to ~/.zshrc for persistence
466+
```
467+
468+
Then install the versions CI uses:
469+
470+
```bash
471+
fnm install 20 && fnm install 22 && fnm install 24
472+
```
473+
474+
#### Running a shard
475+
476+
Example: reproduce **"Node.js v22 — integration v5 (shard 1/3)"**:
477+
478+
```bash
479+
fnm use 22
480+
node --version # confirm v22.x.x
481+
482+
npm run test:integration:v5 -- --runInBand --testTimeout=120000 --shard=1/3
483+
```
484+
485+
Run all three shards sequentially to cover the full suite:
486+
487+
```bash
488+
for shard in 1 2 3; do
489+
npm run test:integration:v5 -- --runInBand --testTimeout=120000 --shard=$shard/3
490+
done
491+
```
492+
493+
Switch back to your default Node when done:
494+
495+
```bash
496+
fnm use default # or: fnm use system
497+
node --version # confirm you're back to your usual version
498+
```
499+
500+
The same pattern works for v4 — swap `test:integration:v5` with `test:integration:v4`.
501+
432502
### Integration test template
433503

434504
```typescript

jest.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export default {
1010
"!src/**/generated/**",
1111
"!src/**/spec/**",
1212
"!src/**/*.test.{ts,tsx}",
13+
"!src/integration-tests/**",
1314
"!src/**/index.ts",
1415
"!src/**/types.ts",
1516
"!src/**/*.types.ts",

scripts/run-integration-v4.sh

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Run one cell of the v4 integration-test matrix, mirroring the
4+
# `integration-tests-v4-sm` job in .github/workflows/pull_request.yml.
5+
#
6+
# Required (shell env or .env at repo root):
7+
# CONDUCTOR_SERVER_URL, CONDUCTOR_AUTH_KEY, CONDUCTOR_AUTH_SECRET
8+
#
9+
# Usage:
10+
# scripts/run-integration-v4.sh [-n|--node 20|22|24] [-s|--shard 1|2|3]
11+
# [-t|--test <path|pattern>] [-c|--coverage] [-- jest args]
12+
# Examples:
13+
# scripts/run-integration-v4.sh # node 24, shard 1/3
14+
# scripts/run-integration-v4.sh --node 22 --shard 2 # node 22, shard 2/3
15+
# scripts/run-integration-v4.sh --coverage # include the coverage report (off by default)
16+
# # Run a single test file (--test disables sharding so the file isn't filtered out):
17+
# scripts/run-integration-v4.sh --test src/integration-tests/WorkflowExecutor.test.ts
18+
# scripts/run-integration-v4.sh --node 20 --test WorkflowExecutor
19+
# scripts/run-integration-v4.sh -- --testPathPatterns="WorkflowExecutor"
20+
set -euo pipefail
21+
22+
NODE_VERSION=24
23+
SHARD=1
24+
TOTAL_SHARDS=3
25+
TEST_PATTERN=""
26+
COVERAGE=0
27+
28+
# Print the leading comment block (everything after the shebang up to the first
29+
# non-comment line) as help text, so it stays in sync with the header above.
30+
usage() { awk 'NR>1 && /^#/ {sub(/^# ?/, ""); print; next} NR>1 {exit}' "$0"; }
31+
32+
extra=()
33+
while [[ $# -gt 0 ]]; do
34+
case "$1" in
35+
-n|--node) NODE_VERSION="${2:?--node needs a value}"; shift 2 ;;
36+
-s|--shard) SHARD="${2:?--shard needs a value}"; shift 2 ;;
37+
-t|--test) TEST_PATTERN="${2:?--test needs a path or pattern}"; shift 2 ;;
38+
-c|--coverage) COVERAGE=1; shift ;;
39+
-h|--help) usage; exit 0 ;;
40+
--) shift; extra=("$@"); break ;;
41+
*) echo "Unknown argument: $1" >&2; usage; exit 1 ;;
42+
esac
43+
done
44+
45+
case "$NODE_VERSION" in 20|22|24) ;; *) echo "Error: --node must be 20, 22, or 24 (got '$NODE_VERSION')" >&2; exit 1 ;; esac
46+
case "$SHARD" in 1|2|3) ;; *) echo "Error: --shard must be 1, 2, or 3 (got '$SHARD')" >&2; exit 1 ;; esac
47+
48+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
49+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
50+
cd "${REPO_ROOT}"
51+
52+
if [[ -f .env ]]; then
53+
set -a
54+
# shellcheck disable=SC1091
55+
source .env
56+
set +a
57+
fi
58+
59+
missing=()
60+
for var in CONDUCTOR_SERVER_URL CONDUCTOR_AUTH_KEY CONDUCTOR_AUTH_SECRET; do
61+
if [[ -z "${!var:-}" ]]; then
62+
missing+=("$var")
63+
fi
64+
done
65+
if (( ${#missing[@]} > 0 )); then
66+
echo "Error: missing required environment variable(s): ${missing[*]}" >&2
67+
echo "Set them in your shell or in a .env file at the repo root (see .env.example)." >&2
68+
exit 1
69+
fi
70+
71+
export ORKES_BACKEND_VERSION=4
72+
export CONDUCTOR_REQUEST_TIMEOUT_MS="${CONDUCTOR_REQUEST_TIMEOUT_MS:-120000}"
73+
export CONDUCTOR_RETRY_SERVER_ERRORS="${CONDUCTOR_RETRY_SERVER_ERRORS:-true}"
74+
75+
test_cmd=(npm run test:integration:v4 -- --ci --runInBand --testTimeout=120000)
76+
if [[ "${COVERAGE}" == "1" ]]; then
77+
test_cmd+=(--coverage)
78+
fi
79+
if [[ -n "${TEST_PATTERN}" ]]; then
80+
# Targeting a specific test: don't shard, or the file may be filtered out.
81+
test_cmd+=("--testPathPatterns=${TEST_PATTERN}")
82+
else
83+
test_cmd+=("--shard=${SHARD}/${TOTAL_SHARDS}")
84+
fi
85+
if (( ${#extra[@]} > 0 )); then
86+
test_cmd+=("${extra[@]}")
87+
fi
88+
89+
if [[ -n "${TEST_PATTERN}" ]]; then
90+
echo "Running v4 integration tests | node ${NODE_VERSION} | test '${TEST_PATTERN}' | server ${CONDUCTOR_SERVER_URL}"
91+
else
92+
echo "Running v4 integration tests | node ${NODE_VERSION} | shard ${SHARD}/${TOTAL_SHARDS} | server ${CONDUCTOR_SERVER_URL}"
93+
fi
94+
95+
if command -v fnm >/dev/null 2>&1; then
96+
fnm exec --using="${NODE_VERSION}" -- "${test_cmd[@]}"
97+
else
98+
current_major="$(node -v 2>/dev/null | sed -E 's/^v([0-9]+).*/\1/')"
99+
if [[ "${current_major}" != "${NODE_VERSION}" ]]; then
100+
echo "Error: requested Node ${NODE_VERSION} but current Node is v${current_major:-unknown} and fnm is not installed." >&2
101+
echo "Install fnm (see SDK_DEVELOPMENT.md) or switch your Node version manually before running." >&2
102+
exit 1
103+
fi
104+
"${test_cmd[@]}"
105+
fi

0 commit comments

Comments
 (0)