Skip to content
Closed
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
11 changes: 10 additions & 1 deletion .github/workflows/js.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,13 @@ jobs:
fail-fast: false
matrix:
node-version: [20, 22]
mode:
[
braintrust-node-compat,
braintrust-browser-node-compat,
braintrust-browser-no-compat,
braintrust-no-compat,
]

steps:
- name: Checkout repository
Expand Down Expand Up @@ -439,6 +446,8 @@ jobs:
- name: Run Cloudflare Worker smoke test
working-directory: js/smoke
shell: bash
env:
CLOUDFLARE_WORKER_MODE: ${{ matrix.mode }}
run: |
./run-tests.sh cloudflare-worker

Expand Down Expand Up @@ -567,6 +576,6 @@ jobs:
- name: Run Deno smoke test
working-directory: js/smoke
env:
BRAINTRUST_BUILD_DIR: ${{ github.workspace }}/js/smoke/tests/deno/build/braintrust/dist/browser.mjs
BRAINTRUST_BUILD_DIR: ${{ github.workspace }}/js/smoke/tests/deno/build/braintrust/dist/index.mjs
run: |
./run-tests.sh deno
36 changes: 14 additions & 22 deletions js/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,15 @@ help:
@echo " make test-ai-sdk-v6 - Run AI SDK v6 wrapper tests"
@echo " make test-claude-agent-sdk - Run Claude Agent SDK wrapper tests"
@echo " make test-api-compat - Run API compatibility tests"
@echo " make test-smoke - Run smoke tests"
@echo " make bench - Run queue performance benchmarks"
@echo " make test-latest - Run core + latest versions of wrappers"
@echo ""
@echo "Smoke tests (mimics CI workflow):"
@echo " make test-smoke - Run all smoke tests (auto-prepares)"
@echo " make test-smoke deno - Run deno smoke test only"
@echo " make test-smoke span - Run span smoke test only"
@echo ""
@echo "See smoke/README.md for details on smoke test infrastructure"

.PHONY: help bench build clean test test-core test-openai test-anthropic test-google-genai test-ai-sdk test-ai-sdk-v5 test-ai-sdk-v6 test-claude-agent-sdk test-latest install-optional-deps publish-beta-local test-smoke

Expand Down Expand Up @@ -155,33 +161,19 @@ clean:
# Smoke tests
# -------------------------------------------------------------------------------------------------

# Marker file to track when smoke tests were last prepared
SMOKE_PREPARE_MARKER := smoke/.prepare-marker
SMOKE_DIR := smoke

# Run smoke tests (prepares if source changed)
# Run smoke tests (always prepares - relies on turbo/tsup caching for build efficiency)
# Usage: make test-smoke [TEST_NAME...]
test-smoke:
@if [ ! -f "$(SMOKE_PREPARE_MARKER)" ]; then \
echo "⚠ Smoke tests not prepared, preparing..."; \
cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \
elif [ -n "$$(find src -type f \( -name '*.ts' -o -name '*.tsx' \) -newer "$(SMOKE_PREPARE_MARKER)" 2>/dev/null | head -1)" ]; then \
echo "⚠ SDK source files changed, preparing smoke tests..."; \
cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \
elif [ -n "$$(find dist -type f -name '*.js' -newer "$(SMOKE_PREPARE_MARKER)" 2>/dev/null | head -1)" ]; then \
echo "⚠ SDK build artifacts changed, preparing smoke tests..."; \
cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \
elif [ ! -d "artifacts" ] || [ -z "$$(ls artifacts/*.tgz 2>/dev/null | head -1)" ]; then \
echo "⚠ Smoke test artifacts missing, preparing..."; \
cd $(SMOKE_DIR) && ./prepare-tests.sh && touch .prepare-marker; \
else \
echo "✓ Smoke tests are up to date"; \
fi
@echo "Running smoke tests..."
@EXIT_CODE=0; \
cd $(SMOKE_DIR) && ./run-tests.sh $(filter-out test-smoke,$(MAKECMDGOALS)) || EXIT_CODE=$$?; \
@TESTS="$(filter-out test-smoke,$(MAKECMDGOALS))"; \
SMOKE_ABS="$$(pwd)/$(SMOKE_DIR)"; \
cd $(SMOKE_DIR) && ./prepare-tests.sh $$TESTS; \
EXIT_CODE=0; \
cd $$SMOKE_ABS && ./run-tests.sh $$TESTS || EXIT_CODE=$$?; \
echo ""; \
echo "Restoring package files..."; \
cd $$SMOKE_ABS && \
for dir in tests/*/; do \
if [ -f "$$dir/package.json" ] && grep -q '"restore"' "$$dir/package.json" 2>/dev/null; then \
(cd "$$dir" && npm run restore >/dev/null 2>&1 && echo " ✓ Restored $$(basename $$dir)") || true; \
Expand Down
5 changes: 5 additions & 0 deletions js/eslint.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import tseslint from "@typescript-eslint/eslint-plugin";
import tsparser from "@typescript-eslint/parser";
import nodeImport from "eslint-plugin-node-import";
import tsupConfigImport from "./tsup.config";

// Handle both ESM and CJS module formats
Expand Down Expand Up @@ -30,6 +31,7 @@ export default [
},
plugins: {
"@typescript-eslint": tseslint,
"node-import": nodeImport,
},
rules: {
// Base TypeScript rules
Expand All @@ -54,6 +56,9 @@ export default [
],
"no-unused-expressions": ["error", { allowShortCircuit: true }],
"@typescript-eslint/no-unused-expressions": "off",
// Require node: protocol for Node.js built-in imports (for Deno compatibility)
// This plugin automatically detects ALL Node.js built-ins - no manual list needed!
"node-import/prefer-node-protocol": "error",
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
"async": "^3.2.5",
"autoevals": "^0.0.131",
"cross-env": "^7.0.3",
"eslint-plugin-node-import": "^1.0.5",
"jiti": "^2.6.1",
"npm-run-all": "^4.1.5",
"openapi-zod-client": "^1.18.3",
"prettier": "^3.5.3",
Expand Down
188 changes: 120 additions & 68 deletions js/smoke/prepare-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,44 @@ set -euo pipefail

# Script to prepare smoke tests by building SDK and installing into test directories
# This mimics what CI does
# Usage: ./prepare-tests.sh [test-name...]
# If no test names provided, prepares all tests

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
JS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SDK_DIR="$(cd "$JS_DIR/.." && pwd)"
ARTIFACTS_DIR="$JS_DIR/artifacts"

echo "============================================================"
echo "Preparing Smoke Tests"
echo "============================================================"
# Parse which tests to prepare
REQUESTED_TESTS=("$@")
PREPARE_ALL=false
if [ ${#REQUESTED_TESTS[@]} -eq 0 ]; then
PREPARE_ALL=true
fi

# Helper to check if we should prepare a specific test
should_prepare() {
local test_name=$1
if [ "$PREPARE_ALL" = true ]; then
return 0
fi
for requested in "${REQUESTED_TESTS[@]}"; do
if [ "$requested" = "$test_name" ]; then
return 0
fi
done
return 1
}

if [ "$PREPARE_ALL" = true ]; then
echo "============================================================"
echo "Preparing All Smoke Tests"
echo "============================================================"
else
echo "============================================================"
echo "Preparing Smoke Tests: ${REQUESTED_TESTS[*]}"
echo "============================================================"
fi
echo ""

# Step 0: Restore test files to clean git state
Expand Down Expand Up @@ -47,36 +76,46 @@ echo "✓ Packed: $PACKED_TARBALL"
echo ""

# Step 3: Build and pack @braintrust/otel (needed for otel-v1 and nextjs tests)
echo "Step 3: Building @braintrust/otel..."
OTEL_DIR="$SDK_DIR/integrations/otel-js"
if [ -d "$OTEL_DIR" ]; then
cd "$OTEL_DIR"

# Install the built braintrust package as a dependency
BRAINTRUST_TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1)
if [ -z "$BRAINTRUST_TARBALL" ]; then
echo "Error: braintrust tarball not found"
exit 1
fi

echo "Installing braintrust and OpenTelemetry dependencies..."
npm_config_save=false npm_config_lockfile=false pnpm add \
"file:$BRAINTRUST_TARBALL" \
"@opentelemetry/api@^1.9.0" \
"@opentelemetry/core@^1.9.0" \
"@opentelemetry/exporter-trace-otlp-http@^0.35.0" \
"@opentelemetry/sdk-trace-base@^1.9.0" || true
NEEDS_OTEL=false
if should_prepare "otel-v1" || should_prepare "nextjs-instrumentation"; then
NEEDS_OTEL=true
fi

echo "Building @braintrust/otel..."
pnpm run build
if [ "$NEEDS_OTEL" = true ]; then
echo "Step 3: Building @braintrust/otel..."
OTEL_DIR="$SDK_DIR/integrations/otel-js"
if [ -d "$OTEL_DIR" ]; then
cd "$OTEL_DIR"

# Install the built braintrust package as a dependency
BRAINTRUST_TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1)
if [ -z "$BRAINTRUST_TARBALL" ]; then
echo "Error: braintrust tarball not found"
exit 1
fi

echo "Packing @braintrust/otel..."
OTEL_TARBALL=$(npm pack --pack-destination "$ARTIFACTS_DIR" 2>&1 | tail -1)
echo "✓ Packed: $OTEL_TARBALL"
echo "Installing braintrust and OpenTelemetry dependencies..."
npm_config_save=false npm_config_lockfile=false pnpm add \
"file:$BRAINTRUST_TARBALL" \
"@opentelemetry/api@^1.9.0" \
"@opentelemetry/core@^1.9.0" \
"@opentelemetry/exporter-trace-otlp-http@^0.35.0" \
"@opentelemetry/sdk-trace-base@^1.9.0" || true

echo "Building @braintrust/otel..."
pnpm run build

echo "Packing @braintrust/otel..."
OTEL_TARBALL=$(npm pack --pack-destination "$ARTIFACTS_DIR" 2>&1 | tail -1)
echo "✓ Packed: $OTEL_TARBALL"
else
echo "⚠ @braintrust/otel directory not found, skipping"
fi
echo ""
else
echo "⚠ @braintrust/otel directory not found, skipping"
echo "Step 3: Skipping @braintrust/otel (not needed for requested tests)"
echo ""
fi
echo ""

# Step 4: Backup package.json files before modifications
echo "Step 4: Creating backups of package.json files..."
Expand All @@ -89,6 +128,7 @@ TESTS_NEEDING_BRAINTRUST=(
"otel-v1"
"span"
"span-jest"
"browser"
)

# Tests that also need @braintrust/otel
Expand All @@ -97,17 +137,18 @@ TESTS_NEEDING_OTEL=(
"nextjs-instrumentation"
)

# Create backups for all tests that will be modified
ALL_TESTS=("${TESTS_NEEDING_BRAINTRUST[@]}" "${TESTS_NEEDING_OTEL[@]}")
for test_name in $(printf '%s\n' "${ALL_TESTS[@]}" | sort -u); do
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
cd "$test_dir"
# Remove old backups first to ensure fresh backups
rm -f package.json.bak package-lock.json.bak
if grep -q '"backup"' package.json 2>/dev/null; then
npm run backup 2>/dev/null || true
echo " ✓ Backed up $test_name/package.json"
# Create backups only for tests we're preparing
for test_name in "${TESTS_NEEDING_BRAINTRUST[@]}"; do
if should_prepare "$test_name"; then
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
cd "$test_dir"
# Remove old backups first to ensure fresh backups
rm -f package.json.bak package-lock.json.bak
if grep -q '"backup"' package.json 2>/dev/null; then
npm run backup 2>/dev/null || true
echo " ✓ Backed up $test_name/package.json"
fi
fi
fi
done
Expand All @@ -118,44 +159,55 @@ echo "Step 5: Installing built packages into test directories..."
cd "$SCRIPT_DIR"

for test_name in "${TESTS_NEEDING_BRAINTRUST[@]}"; do
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
echo "Installing braintrust into $test_name..."
cd "$test_dir"
# Remove package-lock.json to avoid version conflicts
rm -f package-lock.json
npm install --legacy-peer-deps 2>/dev/null || npm install
npx tsx ../../install-build.ts ../../../artifacts braintrust
echo " ✓ $test_name"
if should_prepare "$test_name"; then
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
echo "Installing braintrust into $test_name..."
cd "$test_dir"
# Remove package-lock.json to avoid version conflicts
rm -f package-lock.json
npm install --legacy-peer-deps 2>/dev/null || npm install
npx tsx ../../install-build.ts ../../../artifacts braintrust
echo " ✓ $test_name"
fi
fi
done

for test_name in "${TESTS_NEEDING_OTEL[@]}"; do
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
echo "Installing @braintrust/otel into $test_name..."
cd "$test_dir"
npx tsx ../../install-build.ts ../../../artifacts otel
echo " ✓ $test_name (otel)"
if should_prepare "$test_name"; then
test_dir="$SCRIPT_DIR/tests/$test_name"
if [ -d "$test_dir" ] && [ -f "$test_dir/package.json" ]; then
echo "Installing @braintrust/otel into $test_name..."
cd "$test_dir"
npx tsx ../../install-build.ts ../../../artifacts otel
echo " ✓ $test_name (otel)"
fi
fi
done

# Special handling for Deno - extract tarball for file:// import
echo "Setting up Deno test..."
DENO_DIR="$SCRIPT_DIR/tests/deno"
if [ -d "$DENO_DIR" ]; then
cd "$DENO_DIR"
TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1)
if [ -n "$TARBALL" ]; then
echo "Extracting braintrust tarball for Deno..."
rm -rf build
mkdir -p build
tar -xzf "$TARBALL" -C build
[ -d build/package ] && mv build/package build/braintrust
echo " ✓ Deno build extracted"
if should_prepare "deno"; then
echo "Setting up Deno test..."
DENO_DIR="$SCRIPT_DIR/tests/deno"
if [ -d "$DENO_DIR" ]; then
cd "$DENO_DIR"
TARBALL=$(ls "$ARTIFACTS_DIR"/braintrust-*.tgz | head -n 1)
if [ -n "$TARBALL" ]; then
echo "Extracting braintrust tarball for Deno..."
rm -rf build
mkdir -p build
tar -xzf "$TARBALL" -C build
[ -d build/package ] && mv build/package build/braintrust
echo " ✓ Deno build extracted"

# Install Deno dependencies (npm packages in deno.json)
echo "Installing Deno dependencies..."
deno install
echo " ✓ Deno dependencies installed"
fi
fi
echo ""
fi
echo ""

# Step 6: Build shared test package
echo "Step 6: Building shared test package..."
Expand Down
13 changes: 10 additions & 3 deletions js/smoke/run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,18 @@ run_test() {

cd "$test_dir"

log_info "Running: npm test"
local npm_script="test"

# Allow CI to run Cloudflare Worker variants as separate pipeline jobs
if [ "$test_name" = "cloudflare-worker" ] && [ -n "${CLOUDFLARE_WORKER_MODE:-}" ]; then
npm_script="test:${CLOUDFLARE_WORKER_MODE}"
fi

log_info "Running: npm run $npm_script"

# Set BRAINTRUST_BUILD_DIR for Deno tests if not already set
if [ "$test_name" = "deno" ] && [ -z "${BRAINTRUST_BUILD_DIR:-}" ]; then
local deno_build_file="$TESTS_DIR/deno/build/braintrust/dist/browser.mjs"
local deno_build_file="$TESTS_DIR/deno/build/braintrust/dist/index.mjs"
if [ -f "$deno_build_file" ]; then
# Convert to absolute path for Deno file:// imports
local abs_dir="$(cd "$(dirname "$deno_build_file")" && pwd)"
Expand All @@ -178,7 +185,7 @@ run_test() {
local test_output
local test_exit_code

if test_output=$(npm test 2>&1); then
if test_output=$(npm run "$npm_script" 2>&1); then
test_exit_code=0
else
test_exit_code=$?
Expand Down
Loading
Loading