feat(tests): add TypeScript client integration test support

Integration tests now support replaying TypeScript SDK tests alongside Python tests when running against server-mode stacks. This enables cross-language validation of API contracts and ensures the TypeScript client properly handles recorded responses.

The implementation adds a new `RUN_CLIENT_TS_TESTS` environment variable that triggers TypeScript test execution after successful Python runs. A mapping file (`suites.json`) defines which TypeScript test files correspond to each Python test suite/setup combination. The script automatically installs npm dependencies, forwards server configuration (base URL and model defaults from setup definitions), and executes matching TypeScript tests using Jest.

CI integration is enabled for server-based test jobs, and the feature can be exercised locally with commands like:

\`\`\`bash
RUN_CLIENT_TS_TESTS=1 scripts/integration-tests.sh --stack-config server:ci-tests --suite responses --setup gpt
\`\`\`

The TypeScript tests reuse existing replay fixtures through the forwarded \`TEST_API_BASE_URL\`, avoiding the need for duplicate response recordings.
This commit is contained in:
Ashwin Bharambe 2025-11-18 14:17:40 -08:00
parent 91f1b352b4
commit 78a676e231
12 changed files with 6082 additions and 0 deletions

View file

@ -20,6 +20,7 @@ TEST_PATTERN=""
INFERENCE_MODE="replay"
EXTRA_PARAMS=""
COLLECT_ONLY=false
RUN_CLIENT_TS_TESTS="${RUN_CLIENT_TS_TESTS:-0}"
# Function to display usage
usage() {
@ -120,6 +121,22 @@ if [[ -z "$TEST_SUITE" && -z "$TEST_SUBDIRS" ]]; then
exit 1
fi
RESOLVED_TEST_SETUP=$(python - "$TEST_SUITE" "$TEST_SETUP" <<'PY'
import sys
from tests.integration.suites import SUITE_DEFINITIONS
suite = sys.argv[1]
setup = sys.argv[2]
if not setup:
suite_def = SUITE_DEFINITIONS.get(suite)
if suite_def:
setup = suite_def.default_setup or ""
print(setup or "")
PY
)
echo "=== Llama Stack Integration Test Runner ==="
echo "Stack Config: $STACK_CONFIG"
echo "Setup: $TEST_SETUP"
@ -180,6 +197,38 @@ echo "Setting up environment variables:"
echo "$SETUP_ENV"
eval "$SETUP_ENV"
echo ""
if [[ -n "$RESOLVED_TEST_SETUP" ]]; then
SETUP_DEFAULTS=$(PYTHONPATH=$THIS_DIR/.. python - "$RESOLVED_TEST_SETUP" <<'PY'
import sys
from tests.integration.suites import SETUP_DEFINITIONS
setup_name = sys.argv[1]
if not setup_name:
sys.exit(0)
setup = SETUP_DEFINITIONS.get(setup_name)
if not setup:
sys.exit(0)
for key, value in setup.defaults.items():
print(f"{key}={value}")
PY
)
while IFS='=' read -r key value; do
case "$key" in
text_model)
export LLAMA_STACK_TEST_MODEL="$value"
;;
embedding_model)
export LLAMA_STACK_TEST_EMBEDDING_MODEL="$value"
;;
vision_model)
export LLAMA_STACK_TEST_VISION_MODEL="$value"
;;
esac
done <<< "$SETUP_DEFAULTS"
fi
ROOT_DIR="$THIS_DIR/.."
cd $ROOT_DIR
@ -212,6 +261,34 @@ find_available_port() {
return 1
}
run_client_ts_tests() {
local files=("$@")
if [[ ${#files[@]} -eq 0 ]]; then
echo "No TypeScript integration tests mapped for suite $TEST_SUITE (setup $RESOLVED_TEST_SETUP)"
return 0
fi
if ! command -v npm &>/dev/null; then
echo "npm could not be found; ensure Node.js is installed"
return 1
fi
pushd tests/integration/client-typescript >/dev/null
local install_cmd="npm install"
if [[ "${CI:-}" == "true" || "${CI:-}" == "1" ]]; then
install_cmd="npm ci"
fi
echo "Installing TypeScript client test dependencies using: $install_cmd"
$install_cmd
echo "Running TypeScript tests: ${files[*]}"
npx jest --config jest.integration.config.ts "${files[@]}"
popd >/dev/null
}
# Start Llama Stack Server if needed
if [[ "$STACK_CONFIG" == *"server:"* && "$COLLECT_ONLY" == false ]]; then
# Find an available port for the server
@ -221,6 +298,7 @@ if [[ "$STACK_CONFIG" == *"server:"* && "$COLLECT_ONLY" == false ]]; then
exit 1
fi
export LLAMA_STACK_PORT
export TEST_API_BASE_URL="http://localhost:$LLAMA_STACK_PORT"
echo "Will use port: $LLAMA_STACK_PORT"
stop_server() {
@ -298,6 +376,7 @@ if [[ "$STACK_CONFIG" == *"docker:"* && "$COLLECT_ONLY" == false ]]; then
exit 1
fi
export LLAMA_STACK_PORT
export TEST_API_BASE_URL="http://localhost:$LLAMA_STACK_PORT"
echo "Will use port: $LLAMA_STACK_PORT"
echo "=== Building Docker Image for distribution: $DISTRO ==="
@ -506,5 +585,45 @@ else
exit 1
fi
if [[ $exit_code -eq 0 && "$RUN_CLIENT_TS_TESTS" == "1" && "${LLAMA_STACK_TEST_STACK_CONFIG_TYPE:-}" == "server" ]]; then
CLIENT_TS_FILES=$(python - "$TEST_SUITE" "$RESOLVED_TEST_SETUP" <<'PY'
from pathlib import Path
import json
import sys
suite = sys.argv[1] or ""
setup = sys.argv[2] or ""
config_path = Path("tests/integration/client-typescript/suites.json")
if not config_path.exists():
sys.exit(0)
config = json.loads(config_path.read_text())
for entry in config:
if entry.get("suite") != suite:
continue
entry_setup = entry.get("setup") or ""
if entry_setup and entry_setup != setup:
continue
for file_name in entry.get("files", []):
print(file_name)
break
PY
)
if [[ -n "$CLIENT_TS_FILES" ]]; then
echo "Running TypeScript client tests for suite $TEST_SUITE (setup $RESOLVED_TEST_SETUP)"
CLIENT_TS_FILE_ARRAY=()
while IFS= read -r file; do
[[ -z "$file" ]] && continue
CLIENT_TS_FILE_ARRAY+=("$file")
done <<< "$CLIENT_TS_FILES"
run_client_ts_tests "${CLIENT_TS_FILE_ARRAY[@]}"
else
echo "No TypeScript client tests configured for suite $TEST_SUITE and setup $RESOLVED_TEST_SETUP"
fi
fi
echo ""
echo "=== Integration Tests Complete ==="