Skip to content

Commit 214e7a2

Browse files
skip Orkes-only tests when running against OSS, via custom gated describe wrappers configured by env vars
1 parent 39540d5 commit 214e7a2

14 files changed

Lines changed: 236 additions & 84 deletions

.github/workflows/pull_request.yml

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,11 @@ jobs:
206206
name: codecov-integration-v4-sm-node-${{ matrix.node-version }}-shard-${{ matrix.shard }}
207207
fail_ci_if_error: false
208208

209-
# Integration tests (OSS): spins up Conductor OSS + Postgres via an inline
210-
# docker-compose and runs the integration suite unauthenticated, with Orkes-only
211-
# tests gated out via CONDUCTOR_SERVER_TYPE=oss (see test:integration:oss).
209+
# Integration tests (OSS): spins up Conductor OSS + Postgres via
210+
# scripts/docker-compose-oss.yaml and runs the integration suite unauthenticated,
211+
# with Orkes-only tests gated out via CONDUCTOR_SERVER_TYPE=oss (see
212+
# test:integration:oss). The same stack can be run locally with
213+
# scripts/run-integration-oss.sh.
212214
integration-tests-oss:
213215
runs-on: ubuntu-latest
214216
timeout-minutes: 30
@@ -238,43 +240,8 @@ jobs:
238240
- name: Install Dependencies
239241
if: steps.cache.outputs.cache-hit != 'true'
240242
run: npm ci
241-
- name: Write docker-compose file
242-
run: |
243-
cat <<'EOF' > docker-compose-oss.yaml
244-
services:
245-
conductor-server:
246-
image: conductoross/conductor:latest
247-
environment:
248-
- CONFIG_PROP=config-postgres.properties
249-
ports:
250-
- "8080:8080"
251-
healthcheck:
252-
test: ["CMD", "curl", "-I", "-XGET", "http://localhost:8080/health"]
253-
interval: 10s
254-
timeout: 10s
255-
retries: 20
256-
links:
257-
- conductor-postgres:postgresdb
258-
depends_on:
259-
conductor-postgres:
260-
condition: service_healthy
261-
conductor-postgres:
262-
image: postgres:16
263-
environment:
264-
- POSTGRES_USER=conductor
265-
- POSTGRES_PASSWORD=conductor
266-
healthcheck:
267-
test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432'
268-
interval: 5s
269-
timeout: 5s
270-
retries: 12
271-
httpbin:
272-
image: ghcr.io/orkes-io/test-utils/httpbin:latest
273-
expose:
274-
- "8081"
275-
EOF
276243
- name: Start Conductor OSS stack
277-
run: docker compose -f docker-compose-oss.yaml up -d
244+
run: docker compose -f scripts/docker-compose-oss.yaml up -d
278245
- name: Wait for Conductor to be healthy
279246
run: timeout 120 bash -c 'until curl -sf http://localhost:8080/health; do sleep 5; done'
280247
- name: Run integration tests (OSS)
@@ -283,7 +250,7 @@ jobs:
283250
JEST_JUNIT_OUTPUT_NAME: integration-oss-test-results.xml
284251
- name: Dump Conductor logs
285252
if: failure()
286-
run: docker compose -f docker-compose-oss.yaml logs conductor-server
253+
run: docker compose -f scripts/docker-compose-oss.yaml logs conductor-server
287254
- name: Publish Test Results
288255
uses: dorny/test-reporter@v2
289256
if: ${{ !cancelled() }}

scripts/docker-compose-oss.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Conductor OSS stack used to run the SDK integration tests against open-source
2+
# Conductor. Shared by scripts/run-integration-oss.sh and the
3+
# integration-tests-oss job in .github/workflows/pull_request.yml.
4+
#
5+
# The Conductor server reaches httpbin over the compose network at
6+
# http://httpbin:8081, which matches HTTPBIN_SERVICE_HOSTNAME=httpbin.
7+
services:
8+
conductor-server:
9+
image: conductoross/conductor:latest
10+
environment:
11+
- CONFIG_PROP=config-postgres.properties
12+
ports:
13+
- "8080:8080"
14+
healthcheck:
15+
test: ["CMD", "curl", "-I", "-XGET", "http://localhost:8080/health"]
16+
interval: 10s
17+
timeout: 10s
18+
retries: 20
19+
links:
20+
- conductor-postgres:postgresdb
21+
depends_on:
22+
conductor-postgres:
23+
condition: service_healthy
24+
conductor-postgres:
25+
image: postgres:16
26+
environment:
27+
- POSTGRES_USER=conductor
28+
- POSTGRES_PASSWORD=conductor
29+
healthcheck:
30+
test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432'
31+
interval: 5s
32+
timeout: 5s
33+
retries: 12
34+
httpbin:
35+
image: ghcr.io/orkes-io/test-utils/httpbin:latest
36+
expose:
37+
- "8081"

scripts/run-integration-oss.sh

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Spin up a local Conductor OSS stack and run the SDK integration suite against
4+
# it, mirroring the `integration-tests-oss` job in
5+
# .github/workflows/pull_request.yml. Orkes-only tests are gated out via
6+
# CONDUCTOR_SERVER_TYPE=oss (see the test:integration:oss npm script).
7+
#
8+
# The stack (Conductor OSS + Postgres + httpbin) is defined in
9+
# scripts/docker-compose-oss.yaml and is torn down automatically on exit.
10+
#
11+
# Test output is written to both stdout and a log file (default:
12+
# scripts/oss-test-run.log, override with -l|--log) so it can be shared later.
13+
#
14+
# Usage:
15+
# scripts/run-integration-oss.sh [-t|--test <path|pattern>] [-l|--log <file>] [--keep-up] [-- jest args]
16+
# Examples:
17+
# scripts/run-integration-oss.sh # full OSS-gated suite
18+
# scripts/run-integration-oss.sh --test WorkflowExecutor
19+
# scripts/run-integration-oss.sh --log /tmp/oss.log # custom log path
20+
# scripts/run-integration-oss.sh --keep-up # leave the stack running afterwards
21+
# scripts/run-integration-oss.sh -- --testPathPatterns="EventClient"
22+
set -euo pipefail
23+
24+
TEST_PATTERN=""
25+
KEEP_UP=0
26+
LOG_FILE=""
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+
-t|--test) TEST_PATTERN="${2:?--test needs a path or pattern}"; shift 2 ;;
36+
-l|--log) LOG_FILE="${2:?--log needs a file path}"; shift 2 ;;
37+
--keep-up) KEEP_UP=1; shift ;;
38+
-h|--help) usage; exit 0 ;;
39+
--) shift; extra=("$@"); break ;;
40+
*) echo "Unknown argument: $1" >&2; usage; exit 1 ;;
41+
esac
42+
done
43+
44+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
45+
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
46+
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose-oss.yaml"
47+
LOG_FILE="${LOG_FILE:-${SCRIPT_DIR}/oss-test-run.log}"
48+
cd "${REPO_ROOT}"
49+
50+
# Guard: this script spins up and tests against a *local* OSS stack. If enterprise
51+
# / remote server vars are already in the environment they would silently redirect
52+
# the suite at a deployed server (e.g. sdkdev) and make Orkes-only tests pass,
53+
# defeating the purpose of the OSS run. Refuse to run unless explicitly overridden.
54+
if [[ "${ALLOW_REMOTE_SERVER:-0}" != "1" ]]; then
55+
offending=()
56+
[[ -n "${CONDUCTOR_AUTH_KEY:-}" ]] && offending+=("CONDUCTOR_AUTH_KEY")
57+
[[ -n "${CONDUCTOR_AUTH_SECRET:-}" ]] && offending+=("CONDUCTOR_AUTH_SECRET")
58+
if [[ -n "${CONDUCTOR_SERVER_URL:-}" \
59+
&& ! "${CONDUCTOR_SERVER_URL}" =~ ^https?://(localhost|127\.0\.0\.1)(:|/|$) ]]; then
60+
offending+=("CONDUCTOR_SERVER_URL=${CONDUCTOR_SERVER_URL}")
61+
fi
62+
if (( ${#offending[@]} > 0 )); then
63+
echo "Error: refusing to run the OSS suite — remote/enterprise server vars are set:" >&2
64+
printf ' - %s\n' "${offending[@]}" >&2
65+
echo >&2
66+
echo "This script runs against a LOCAL OSS stack. Unset these first:" >&2
67+
echo " unset CONDUCTOR_SERVER_URL CONDUCTOR_AUTH_KEY CONDUCTOR_AUTH_SECRET" >&2
68+
echo "Or set ALLOW_REMOTE_SERVER=1 to bypass this check intentionally." >&2
69+
exit 1
70+
fi
71+
fi
72+
73+
CONDUCTOR_SERVER_URL="${CONDUCTOR_SERVER_URL:-http://localhost:8080/api}"
74+
HEALTH_URL="${CONDUCTOR_SERVER_URL%/api}/health"
75+
76+
compose() { docker compose -f "${COMPOSE_FILE}" "$@"; }
77+
78+
cleanup() {
79+
if [[ "${KEEP_UP}" == "1" ]]; then
80+
echo "--keep-up set: leaving the OSS stack running. Tear down with:"
81+
echo " docker compose -f ${COMPOSE_FILE} down -v"
82+
return
83+
fi
84+
echo "Tearing down Conductor OSS stack..."
85+
compose down -v || true
86+
}
87+
trap cleanup EXIT
88+
89+
echo "Starting Conductor OSS stack (${COMPOSE_FILE})..."
90+
compose up -d
91+
92+
echo "Waiting for Conductor to be healthy at ${HEALTH_URL} ..."
93+
# Portable wait loop using bash's built-in SECONDS (macOS has no `timeout`).
94+
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-180}"
95+
deadline=$(( SECONDS + HEALTH_TIMEOUT ))
96+
until curl -sf "${HEALTH_URL}" >/dev/null 2>&1; do
97+
if (( SECONDS >= deadline )); then
98+
echo "Error: Conductor did not become healthy within ${HEALTH_TIMEOUT}s." >&2
99+
compose logs conductor-server || true
100+
exit 1
101+
fi
102+
sleep 5
103+
done
104+
echo "Conductor is up."
105+
106+
export CONDUCTOR_SERVER_URL
107+
export CONDUCTOR_SERVER_TYPE=oss
108+
export HTTPBIN_SERVICE_HOSTNAME="${HTTPBIN_SERVICE_HOSTNAME:-httpbin}"
109+
export CONDUCTOR_REQUEST_TIMEOUT_MS="${CONDUCTOR_REQUEST_TIMEOUT_MS:-120000}"
110+
export CONDUCTOR_RETRY_SERVER_ERRORS="${CONDUCTOR_RETRY_SERVER_ERRORS:-true}"
111+
112+
test_cmd=(npm run test:integration:oss -- --ci --runInBand --testTimeout=120000)
113+
if [[ -n "${TEST_PATTERN}" ]]; then
114+
# Targeting a specific test: pass it straight through as a path pattern.
115+
test_cmd+=("--testPathPatterns=${TEST_PATTERN}")
116+
fi
117+
if (( ${#extra[@]} > 0 )); then
118+
test_cmd+=("${extra[@]}")
119+
fi
120+
121+
if [[ -n "${TEST_PATTERN}" ]]; then
122+
echo "Running OSS integration tests | test '${TEST_PATTERN}' | server ${CONDUCTOR_SERVER_URL}"
123+
else
124+
echo "Running OSS integration tests | full OSS-gated suite | server ${CONDUCTOR_SERVER_URL}"
125+
fi
126+
echo "Writing output to ${LOG_FILE}"
127+
128+
# Tee to a log file for later sharing. pipefail ensures the test command's exit
129+
# status (not tee's) is what propagates.
130+
"${test_cmd[@]}" 2>&1 | tee "${LOG_FILE}"

src/integration-tests/ApplicationClient.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import {
99
import { ApplicationClient } from "../sdk";
1010
import { createClientWithRetry } from "./utils/createClientWithRetry";
1111
import type { Tag } from "../open-api";
12-
import { describeForOrkesV4 } from "./utils/customJestDescribe";
12+
import { describeForOrkesOnlyV4 } from "./utils/customJestDescribe";
1313

1414
describe("ApplicationClient", () => {
15-
describeForOrkesV4("ApplicationClient V4+", () => {
15+
describeForOrkesOnlyV4("ApplicationClient V4+", () => {
1616
jest.setTimeout(60000);
1717

1818
let applicationClient: ApplicationClient;
@@ -578,5 +578,5 @@ describe("ApplicationClient", () => {
578578
// ).rejects.toThrow();
579579
// });
580580
});
581-
}); // end describeForOrkesV4
581+
}); // end describeForOrkesOnlyV4
582582
});

src/integration-tests/AuthorizationClient.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
MetadataClient,
66
} from "../sdk";
77
import { createClientWithRetry } from "./utils/createClientWithRetry";
8-
import { describeForOrkesV4 } from "./utils/customJestDescribe";
8+
import { describeForOrkesOnlyV4 } from "./utils/customJestDescribe";
99
import { registerWorkflowDefWithRetry } from "./utils/registerWorkflowWithRetry";
1010

1111
/**
@@ -15,7 +15,7 @@ import { registerWorkflowDefWithRetry } from "./utils/registerWorkflowWithRetry"
1515
* in a lifecycle order: create → read → update → delete.
1616
*/
1717
describe("AuthorizationClient", () => {
18-
describeForOrkesV4("AuthorizationClient V4+", () => {
18+
describeForOrkesOnlyV4("AuthorizationClient V4+", () => {
1919
jest.setTimeout(60000);
2020

2121
const suffix = Date.now();
@@ -355,5 +355,5 @@ describe("AuthorizationClient", () => {
355355
).rejects.toThrow();
356356
});
357357
});
358-
}); // end describeForOrkesV4
358+
}); // end describeForOrkesOnlyV4
359359
});

src/integration-tests/EventClient.test.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import type {
1515
import { EventClient } from "../sdk";
1616
import { createClientWithRetry } from "./utils/createClientWithRetry";
1717
import {
18-
describeForOrkesV4,
19-
describeForOrkesV5,
2018
describeForOrkesOnlyV4,
19+
describeForOrkesOnlyV5,
2120
} from "./utils/customJestDescribe";
2221
import { pollUntil } from "./utils/pollUntil";
2322

@@ -83,7 +82,7 @@ describe("EventClient", () => {
8382
description: `Test event handler: ${name}`,
8483
});
8584

86-
describeForOrkesV4("Event Handler Management", () => {
85+
describeForOrkesOnlyV4("Event Handler Management", () => {
8786
test("Should add a single event handler", async () => {
8887
const handlerName = createUniqueName("event-handler");
8988
const eventName = createUniqueName("event");
@@ -487,7 +486,7 @@ describe("EventClient", () => {
487486
});
488487
});
489488

490-
describeForOrkesV4("Test Endpoint", () => {
489+
describeForOrkesOnlyV4("Test Endpoint", () => {
491490
test("Should call test endpoint", async () => {
492491

493492
const result = await eventClient.test();
@@ -579,7 +578,7 @@ describe("EventClient", () => {
579578
});
580579
});
581580

582-
describeForOrkesV5("Event Processing", () => {
581+
describeForOrkesOnlyV5("Event Processing", () => {
583582
test("Should handle incoming event", async () => {
584583
const handlerName = createUniqueName("event-handler");
585584
const eventName = createUniqueName("event");
@@ -670,7 +669,7 @@ describe("EventClient", () => {
670669
});
671670
});
672671

673-
describeForOrkesV5("Event Executions and Statistics", () => {
672+
describeForOrkesOnlyV5("Event Executions and Statistics", () => {
674673
test("Should get all active event handlers (execution view)", async () => {
675674

676675
const handlerName = createUniqueName("event-handler");

src/integration-tests/MetadataClient.complete.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from "../sdk";
1717
import { createClientWithRetry } from "./utils/createClientWithRetry";
1818
import {
19+
describeForOrkesOnly,
1920
describeForOrkesOnlyV4,
2021
describeForOssSchedulerWip,
2122
} from "./utils/customJestDescribe";
@@ -279,7 +280,7 @@ describe("MetadataClient Complete Coverage", () => {
279280
// ==================== Rate Limits ====================
280281
// Rate limit API may not be available on all server versions
281282

282-
describe("Rate Limits", () => {
283+
describeForOrkesOnly("Rate Limits", () => {
283284
let rateLimitSupported = true;
284285

285286
test("setWorkflowRateLimit should configure rate limiting", async () => {

src/integration-tests/PromptClient.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ import {
44
PromptClient,
55
} from "../sdk";
66
import type { Tag } from "../open-api";
7-
import { describeForOrkesV5 } from "./utils/customJestDescribe";
7+
import { describeForOrkesOnlyV5 } from "./utils/customJestDescribe";
88
import { createClientWithRetry } from "./utils/createClientWithRetry";
99

1010
/**
11-
* E2E Integration Tests for PromptClient (v5 only).
11+
* E2E Integration Tests for PromptClient.
1212
*
1313
* Tests prompt CRUD, tag management, and (optionally) prompt testing
14-
* against a configured LLM integration.
14+
* against a configured LLM integration. Orkes-only: the prompt registry
15+
* (/api/prompts) does not exist on Conductor OSS.
1516
*/
16-
describeForOrkesV5("PromptClient", () => {
17+
describeForOrkesOnlyV5("PromptClient", () => {
1718
jest.setTimeout(60000);
1819

1920
const clientPromise = createClientWithRetry();

src/integration-tests/SchemaClient.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ import {
44
SchemaClient,
55
} from "../sdk";
66
import { createClientWithRetry } from "./utils/createClientWithRetry";
7-
import { describeForOrkesV5 } from "./utils/customJestDescribe";
7+
import { describeForOrkesOnlyV5 } from "./utils/customJestDescribe";
88

99
/**
1010
* E2E Integration Tests for SchemaClient
1111
*
1212
* Tests schema registration, retrieval (by name, by name+version), listing,
13-
* version creation, and deletion. Gated to v5: GET /api/schema/{name} is not
14-
* supported on older backends (returns "Method 'GET' is not supported").
13+
* version creation, and deletion. Orkes-only: the schema registry
14+
* (/api/schema) does not exist on Conductor OSS (returns "No static resource").
1515
*/
16-
describeForOrkesV5("SchemaClient", () => {
16+
describeForOrkesOnlyV5("SchemaClient", () => {
1717
jest.setTimeout(60000);
1818

1919
const clientPromise = createClientWithRetry();

0 commit comments

Comments
 (0)