Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ on:
branches:
- main
workflow_dispatch:
inputs:
oss_conductor_version:
description: 'OSS Conductor image tag (falls back to E2E_TEST_OSS_CONDUCTOR_VERSION org var)'
required: false
type: string

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
Expand Down Expand Up @@ -133,3 +138,65 @@ jobs:
bash scripts/run_integration_tests.sh --bucket=${{ matrix.bucket }}
-s --log-cli-level=INFO
--log-cli-format='%(asctime)s %(levelname)s %(name)s: %(message)s'

# Integration tests (OSS): spins up Conductor OSS + Postgres via
# scripts/docker-compose-oss.yaml and runs the integration suite
# unauthenticated, with Orkes-only tests gated out via
# CONDUCTOR_SERVER_TYPE=oss (see the individual test files for the
# empirically-confirmed gaps). The same stack can be run locally with
# scripts/run-integration-oss.sh.
#
# Unlike the authenticated integration-test job above, this runs the whole
# suite in one job (--bucket=all) instead of matrix-splitting it: the full
# OSS run finishes in under 5 minutes, so splitting it would just cause
# extra runner usage for no performance benefit.
integration-tests-oss:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
CONDUCTOR_SERVER_URL: http://localhost:8080/api
CONDUCTOR_SERVER_TYPE: oss
# See the comment on CONDUCTOR_HTTP2_ENABLED in the integration-test job
# above; kept consistent here even though the local OSS stack doesn't
# have the same proxy/LB in front of it.
CONDUCTOR_HTTP2_ENABLED: "false"
OSS_CONDUCTOR_VERSION: ${{ inputs.oss_conductor_version || vars.E2E_TEST_OSS_CONDUCTOR_VERSION }}
steps:
- name: Verify OSS Conductor version is set
run: |
if [ -z "$OSS_CONDUCTOR_VERSION" ]; then
echo "::error::No Conductor OSS image tag resolved. Set the E2E_TEST_OSS_CONDUCTOR_VERSION organization variable (and ensure its repository access policy includes this repo), or pass the oss_conductor_version input via workflow_dispatch."
exit 1
fi
echo "Using conductoross/conductor:$OSS_CONDUCTOR_VERSION"

- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pytest

- name: Start Conductor OSS stack
run: docker compose -f scripts/docker-compose-oss.yaml up -d

- name: Wait for Conductor to be healthy
run: timeout 120 bash -c 'until curl -sf http://localhost:8080/health; do sleep 5; done'

- name: Run integration tests (OSS)
run: >-
bash scripts/run_integration_tests.sh --bucket=all
-s --log-cli-level=INFO
--log-cli-format='%(asctime)s %(levelname)s %(name)s: %(message)s'

- name: Dump Conductor logs
if: failure()
run: docker compose -f scripts/docker-compose-oss.yaml logs conductor-server
41 changes: 41 additions & 0 deletions scripts/docker-compose-oss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Conductor OSS stack used to run the SDK integration tests against open-source
# Conductor. Shared by scripts/run-integration-oss.sh and the
# integration-tests-oss job in .github/workflows/pull_request.yml.
#
# The Conductor server reaches httpbin over the compose network at
# http://httpbin:8081 (see e.g. tests/integration/client/orkes/test_orkes_service_registry_client.py
# and the complex_wf_signal_test*.json fixtures).
#
# OSS_CONDUCTOR_VERSION defaults to `latest` for local runs; CI pins it via the
# E2E_TEST_OSS_CONDUCTOR_VERSION org variable (or a workflow_dispatch input).
services:
conductor-server:
image: conductoross/conductor:${OSS_CONDUCTOR_VERSION:-latest}
environment:
- CONFIG_PROP=config-postgres.properties
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "-I", "-XGET", "http://localhost:8080/health"]
interval: 10s
timeout: 10s
retries: 20
links:
- conductor-postgres:postgresdb
depends_on:
conductor-postgres:
condition: service_healthy
conductor-postgres:
image: postgres:16
environment:
- POSTGRES_USER=conductor
- POSTGRES_PASSWORD=conductor
healthcheck:
test: timeout 5 bash -c 'cat < /dev/null > /dev/tcp/localhost/5432'
interval: 5s
timeout: 5s
retries: 12
httpbin:
image: ghcr.io/conductor-oss/httpbin:v1.0.1
expose:
- "8081"
100 changes: 100 additions & 0 deletions scripts/run-integration-oss.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
#
# Spin up a local Conductor OSS stack and run the SDK integration suite
# against it. To reproduce the `integration-tests-oss` job in
# .github/workflows/pull_request.yml you need `-- --bucket=all`: that job runs
# the full suite, whereas this script defaults to the faster `core` bucket,
# which excludes tests/integration/test_workflow_client_intg.py -- the only
# entry point to the workflow-execution and Signal API scenarios.
#
# Orkes-Enterprise-only tests/classes/
# modules (Authorization, Secrets, Schema, Service Registry,
# metadata/scheduler tags) check `os.environ.get('CONDUCTOR_SERVER_TYPE')`
# directly and skip themselves when it's "oss" (confirmed empirically not
# implemented by plain OSS Conductor -- see the individual test files for
# details on each gap). The Signal API tests run on OSS too, using a
# WAIT-task-based fixture variant instead of the YIELD-based one used against
# Orkes Enterprise -- see _signal_test_workflow_names() in
# tests/integration/workflow/test_workflow_execution.py.
#
# The stack (Conductor OSS + Postgres + httpbin) is defined in
# scripts/docker-compose-oss.yaml and is torn down automatically on exit.
#
# Usage:
# scripts/run-integration-oss.sh [--keep-up] [--version <tag>] [-- pytest args]
# Examples:
# scripts/run-integration-oss.sh -- --bucket=all # what CI runs: the full suite
# scripts/run-integration-oss.sh # faster: --bucket=core against `latest`
# scripts/run-integration-oss.sh --version 3.32.0-rc18
# scripts/run-integration-oss.sh --keep-up # leave the stack up afterwards
set -euo pipefail

KEEP_UP=0
UP_ONLY=0
extra=()

while [[ $# -gt 0 ]]; do
case "$1" in
--keep-up) KEEP_UP=1; shift ;;
# Bring the stack up and stop there, for pointing repeated test runs at it
# by hand. Implies --keep-up: tearing down the stack we just started would
# defeat the purpose.
--up-only) UP_ONLY=1; KEEP_UP=1; shift ;;
--version) OSS_CONDUCTOR_VERSION="${2:?--version needs a tag}"; shift 2 ;;
-h|--help)
echo "Usage: $0 [--up-only] [--keep-up] [--version <tag>] [-- pytest args]"
exit 0
;;
--) shift; extra=("$@"); break ;;
*) echo "Unknown argument: $1" >&2; exit 1 ;;
esac
done

export OSS_CONDUCTOR_VERSION="${OSS_CONDUCTOR_VERSION:-latest}"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
COMPOSE_FILE="${SCRIPT_DIR}/docker-compose-oss.yaml"
cd "${REPO_ROOT}"

compose() { docker compose -f "${COMPOSE_FILE}" "$@"; }

cleanup() {
if [[ "${KEEP_UP}" == "1" ]]; then
echo "--keep-up set: leaving the OSS stack running. Tear down with:"
echo " docker compose -f ${COMPOSE_FILE} down -v"
return
fi
echo "Tearing down Conductor OSS stack..."
compose down -v || true
}
trap cleanup EXIT

echo "Starting Conductor OSS stack (conductoross/conductor:${OSS_CONDUCTOR_VERSION})..."
compose up -d

echo "Waiting for Conductor to be healthy..."
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-180}"
deadline=$(( SECONDS + HEALTH_TIMEOUT ))
until curl -sf http://localhost:8080/health >/dev/null 2>&1; do
if (( SECONDS >= deadline )); then
echo "Error: Conductor did not become healthy within ${HEALTH_TIMEOUT}s." >&2
compose logs conductor-server || true
exit 1
fi
sleep 5
done
echo "Conductor is up."

export CONDUCTOR_SERVER_URL="http://localhost:8080/api"
export CONDUCTOR_SERVER_TYPE="oss"

if [[ "${UP_ONLY}" == "1" ]]; then
echo "--up-only set: stack is up, not running the suite. Point runs at it with:"
echo " export CONDUCTOR_SERVER_URL=\"${CONDUCTOR_SERVER_URL}\""
echo " export CONDUCTOR_SERVER_TYPE=\"${CONDUCTOR_SERVER_TYPE}\""
echo " bash scripts/run_integration_tests.sh --bucket=all"
exit 0
fi

bash scripts/run_integration_tests.sh ${extra[@]+"${extra[@]}"}
63 changes: 57 additions & 6 deletions tests/integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ End-to-end integration tests that run against a **real Conductor server**.

### 1. Conductor Server Running

**Option A: Local Conductor (Docker)**
**Option A: Local Conductor OSS (Docker Compose, recommended)**
```bash
docker run --init -p 8080:8080 -p 5000:5000 conductoross/conductor-standalone:3.15.0
scripts/run-integration-oss.sh --up-only
```
This starts a Postgres-backed Conductor OSS stack
(`scripts/docker-compose-oss.yaml`), waits for it to become healthy, and stops
there, leaving it running for you to point test runs at. Tear it down with
`docker compose -f scripts/docker-compose-oss.yaml down -v`. Pass
`--version <tag>` to pin a specific `conductoross/conductor` image.

Without `--up-only` the same script also runs the suite and then tears the
stack down — see [Against local Conductor OSS](#against-local-conductor-oss)
below.

**Option B: Orkes Cloud**
```bash
Expand All @@ -29,6 +38,12 @@ export CONDUCTOR_AUTH_SECRET="your-key-secret"
# Required
export CONDUCTOR_SERVER_URL="http://localhost:8080/api"

# Required when the server is plain OSS Conductor (Option A). Orkes-only
# tests check this and skip themselves; without it they fail instead.
# run-integration-oss.sh exports it for the run it drives, but a stack left
# up with --up-only or --keep-up needs it set in your own shell.
export CONDUCTOR_SERVER_TYPE="oss"

# Optional (for Orkes Cloud)
export CONDUCTOR_AUTH_KEY="your-key"
export CONDUCTOR_AUTH_SECRET="your-secret"
Expand All @@ -40,8 +55,11 @@ export CONDUCTOR_AUTH_SECRET="your-secret"

### Run the CI suite locally (recommended)

Use the helper script to run exactly what CI runs (the `integration-test` job in
[`.github/workflows/pull_request.yml`](../../.github/workflows/pull_request.yml)).
Use the helper script to run exactly what the `integration-test` job in
[`.github/workflows/pull_request.yml`](../../.github/workflows/pull_request.yml)
runs against the authenticated Orkes server. (For the second CI job, which runs
against plain OSS Conductor, see
[Against local Conductor OSS](#against-local-conductor-oss) below.)
It excludes the AI/agentic tests (which need a dedicated AI-enabled server) and
the slow performance test, so you don't have to remember the `--ignore` flags:

Expand Down Expand Up @@ -82,6 +100,39 @@ targeting a subset of tests or getting more detail on failures. See additional
options and examples in the comments at the top of
[`scripts/run_integration_tests.sh`](../../scripts/run_integration_tests.sh).

### Against local Conductor OSS

`scripts/run-integration-oss.sh` brings up the OSS stack, runs the suite against
it with `CONDUCTOR_SERVER_TYPE=oss` set, and tears the stack down on exit:

```bash
# What the integration-tests-oss CI job runs — the full suite:
scripts/run-integration-oss.sh -- --bucket=all

# Faster loop: the `core` bucket only (the default if no bucket is given)
scripts/run-integration-oss.sh
```

**The default is `--bucket=core`, which is not what CI runs.** `core` excludes
`test_workflow_client_intg.py`, and that file is the only entry point to the
workflow-execution and Signal API scenarios — so the default run exercises
neither. Use `-- --bucket=all` to reproduce a CI failure.

Anything after `--` is forwarded to `scripts/run_integration_tests.sh`, so the
bucket table and pytest passthrough above apply here too. `--version <tag>`,
`--keep-up` (leave the stack up afterwards) and `--up-only` (start the stack and
skip the suite) are handled by the script itself and go *before* the `--`.

On OSS, the Orkes-Enterprise-only tests, classes, and modules (Authorization,
Secrets, Schema, Service Registry, metadata/scheduler tags) check
`CONDUCTOR_SERVER_TYPE` and skip themselves — see the individual test files for
the specific gap each one covers. The Signal API tests *do* run, using
WAIT-task-based fixtures (`complex_wf_signal_test_oss` and friends) instead of
the Orkes-only YIELD-based ones; see `_signal_test_workflow_names()` in
[`workflow/test_workflow_execution.py`](workflow/test_workflow_execution.py).

Expect a large number of skips: a full OSS run is roughly 20 passed / 70 skipped.

### Run All Integration Tests

This includes the AI/agentic tests, which require an AI-enabled server (see
Expand Down Expand Up @@ -407,8 +458,8 @@ curl http://localhost:8080/api/health
# Check environment variable
echo $CONDUCTOR_SERVER_URL

# Start local server
docker run --init -p 8080:8080 -p 5000:5000 conductoross/conductor-standalone:3.15.0
# Start local server (stack only, no test run)
scripts/run-integration-oss.sh --up-only
```

### Tests Timeout
Expand Down
Loading
Loading