Skip to content

Commit a6dcd1c

Browse files
kulvirgitclaude
andcommitted
test: expand sanity suite with branding, deny enforcement, driver, and resilience tests
Add 22 new sanity tests across all phases: verify-install.sh: - Semver version format check (#212) - --help branding leak detection for "opencode" strings (#416, #417) - "altimate run --help" branding leak detection - 9 driver resolvability checks: pg, snowflake-sdk, mysql2, mssql, duckdb, mongodb, bigquery, databricks, oracledb (#295) resilience.sh (reorganized from 8 to 10 tests): - Yolo deny enforcement using observable side-effect marker file, with config written to correct altimate-code/ subdir (#372, #377) - Missing API key graceful handling - No-internet graceful error via TEST-NET-1 proxy (#181) smoke-tests.sh: - dbt config discovery in isolated tmpdir (#448, #270) - altimate check command with positional file arg (#453) - MongoDB smoke test alongside existing postgres/snowflake Infrastructure: - Dockerfile: install all 9 driver packages (not just altimate-core) - docker-compose.yml: add MongoDB 7 service with healthcheck - ci-local.sh: add mongodb to Docker services, bump healthy count to 5, add mongodb E2E test step - pr-tests/generate.sh: add triggers for permission/yolo, branding, dbt, and driver file changes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent abcaa1d commit a6dcd1c

7 files changed

Lines changed: 285 additions & 35 deletions

File tree

test/sanity/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ COPY --chown=testuser packages/opencode/script/postinstall.mjs /home/testuser/.a
2424
COPY --chown=testuser packages/opencode/package.json /home/testuser/.altimate-install/package.json
2525
COPY --chown=testuser .opencode/skills/ /home/testuser/.altimate-install/skills/
2626

27-
# Install altimate-core native binding (required at runtime)
27+
# Install altimate-core native binding + all database drivers (required at runtime)
28+
# Drivers are optionalDependencies in @altimateai/drivers — must be explicitly installed
29+
# to match what a real `npm install` with peerDependencies would provide.
2830
RUN cd /home/testuser/.altimate-install && \
29-
echo '{"dependencies":{"@altimateai/altimate-core":"latest"}}' > package.json && \
31+
echo '{"dependencies":{"@altimateai/altimate-core":"latest","pg":"^8.0.0","snowflake-sdk":"^2.0.3","mysql2":"^3.0.0","mssql":"^11.0.0","duckdb":"^1.0.0","mongodb":"^6.0.0","@google-cloud/bigquery":"^8.0.0","@databricks/sql":"^1.0.0","oracledb":"^6.0.0"}}' > package.json && \
3032
bun install && \
3133
node -e "require('@altimateai/altimate-core')" 2>/dev/null || { echo "FATAL: altimate-core install failed"; exit 1; }
3234

test/sanity/ci-local.sh

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,25 @@ if [ "$MODE" = "--full" ] || [ "$MODE" = "full" ]; then
5656
echo "=== Full CI (Docker) ==="
5757

5858
# Driver E2E with Docker containers
59-
run_step "Docker Services Up" docker compose -f "$REPO_ROOT/test/sanity/docker-compose.yml" up -d postgres mysql mssql redshift
59+
run_step "Docker Services Up" docker compose -f "$REPO_ROOT/test/sanity/docker-compose.yml" up -d postgres mysql mssql redshift mongodb
6060

6161
echo " Waiting for services to be healthy..."
6262
HEALTHY=0
6363
for _wait in $(seq 1 30); do
6464
HEALTHY=$(docker compose -f "$REPO_ROOT/test/sanity/docker-compose.yml" ps --format json 2>/dev/null | grep -c '"healthy"' || echo "0")
65-
if [ "$HEALTHY" -ge 4 ]; then break; fi
65+
if [ "$HEALTHY" -ge 5 ]; then break; fi
6666
sleep 2
6767
done
6868

69-
if [ "$HEALTHY" -lt 4 ]; then
70-
echo " >>> Docker Services: FAILED ($HEALTHY/4 healthy after 60s)"
69+
if [ "$HEALTHY" -lt 5 ]; then
70+
echo " >>> Docker Services: FAILED ($HEALTHY/5 healthy after 60s)"
7171
EXIT_CODE=1
7272
else
73-
echo " >>> Docker Services: $HEALTHY/4 healthy"
73+
echo " >>> Docker Services: $HEALTHY/5 healthy"
7474
fi
7575

7676
# Skip driver tests if services aren't healthy
77-
if [ "$HEALTHY" -lt 4 ]; then
77+
if [ "$HEALTHY" -lt 5 ]; then
7878
echo " SKIP: Driver E2E tests (services not healthy)"
7979
else
8080

@@ -88,6 +88,10 @@ if [ "$MODE" = "--full" ] || [ "$MODE" = "full" ]; then
8888
TEST_REDSHIFT_HOST=127.0.0.1 TEST_REDSHIFT_PORT=15439 TEST_REDSHIFT_PASSWORD=testpass123 \
8989
bun test test/altimate/drivers-docker-e2e.test.ts --timeout 30000"
9090

91+
run_step "Driver E2E (mongodb)" bash -c "cd $REPO_ROOT/packages/opencode && \
92+
TEST_MONGODB_HOST=127.0.0.1 TEST_MONGODB_PORT=17017 \
93+
bun test test/altimate/drivers-mongodb-e2e.test.ts --timeout 30000"
94+
9195
# Full sanity suite in Docker
9296
run_step "Sanity Suite (Docker)" docker compose -f "$REPO_ROOT/test/sanity/docker-compose.yml" \
9397
up --build --abort-on-container-exit --exit-code-from sanity

test/sanity/docker-compose.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ services:
1818
- TEST_REDSHIFT_HOST=redshift
1919
- TEST_REDSHIFT_PORT=5432
2020
- TEST_REDSHIFT_PASSWORD=testpass123
21+
- TEST_MONGODB_HOST=mongodb
22+
- TEST_MONGODB_PORT=27017
2123
depends_on:
2224
postgres:
2325
condition: service_healthy
@@ -27,6 +29,8 @@ services:
2729
condition: service_healthy
2830
redshift:
2931
condition: service_healthy
32+
mongodb:
33+
condition: service_healthy
3034

3135
postgres:
3236
image: postgres:16-alpine
@@ -74,3 +78,12 @@ services:
7478
test: pg_isready
7579
interval: 5s
7680
retries: 10
81+
82+
mongodb:
83+
image: mongo:7
84+
ports:
85+
- "17017:27017"
86+
healthcheck:
87+
test: mongosh --eval "db.adminCommand('ping')" --quiet
88+
interval: 5s
89+
retries: 10

test/sanity/phases/resilience.sh

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ echo '{}' > package.json
2323
git add -A && git commit -q -m "init"
2424

2525
# 1. SQLite DB created after first run
26-
echo " [1/7] SQLite DB creation..."
26+
echo " [1/10] SQLite DB creation..."
2727
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
2828
altimate_run "db-create" "say hello" || true
2929
# Find the DB — could be opencode.db, opencode-latest.db, or opencode-{channel}.db
@@ -42,7 +42,7 @@ else
4242
fi
4343

4444
# 2. WAL mode enabled
45-
echo " [2/7] WAL mode..."
45+
echo " [2/10] WAL mode..."
4646
if [ -n "$DB_PATH" ] && [ -f "$DB_PATH" ] && command -v sqlite3 >/dev/null 2>&1; then
4747
WAL_MODE=$(sqlite3 "$DB_PATH" "PRAGMA journal_mode;" 2>/dev/null || echo "unknown")
4848
assert_eq "$WAL_MODE" "wal" "WAL mode enabled"
@@ -51,7 +51,7 @@ else
5151
fi
5252

5353
# 3. Session persisted
54-
echo " [3/7] Session persistence..."
54+
echo " [3/10] Session persistence..."
5555
if [ -n "$DB_PATH" ] && [ -f "$DB_PATH" ] && command -v sqlite3 >/dev/null 2>&1; then
5656
SESSION_COUNT=$(sqlite3 "$DB_PATH" "SELECT count(*) FROM session;" 2>/dev/null || echo "0")
5757
assert_ge "$SESSION_COUNT" 1 "session persisted (got $SESSION_COUNT)"
@@ -60,7 +60,7 @@ else
6060
fi
6161

6262
# 4. Session continue (DB survives restart)
63-
echo " [4/7] Session continue..."
63+
echo " [4/10] Session continue..."
6464
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
6565
altimate_run "continue" --continue "what was my last message" || true
6666
assert_not_contains "$(get_output continue)" "TIMEOUT" "session continue works"
@@ -69,7 +69,7 @@ else
6969
fi
7070

7171
# 5. Compaction doesn't crash (best-effort — seed if fixture available)
72-
echo " [5/7] Compaction resilience..."
72+
echo " [5/10] Compaction resilience..."
7373
if [ -n "$DB_PATH" ] && [ -f "$SCRIPT_DIR/fixtures/compaction-session.sql" ] && command -v sqlite3 >/dev/null 2>&1; then
7474
sqlite3 "$DB_PATH" < "$SCRIPT_DIR/fixtures/compaction-session.sql" 2>/dev/null || true
7575
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
@@ -90,20 +90,8 @@ else
9090
skip_test "Compaction resilience" "fixture or sqlite3 not available"
9191
fi
9292

93-
# 6. Graceful on missing provider key
94-
echo " [6/7] Missing API key handling..."
95-
SAVED_KEY="${ANTHROPIC_API_KEY:-}"
96-
unset ANTHROPIC_API_KEY
97-
OUTPUT=$(timeout 10 altimate run --max-turns 1 --yolo "hello" 2>&1 || true)
98-
# Should get a clean error, not an unhandled exception / stack trace
99-
assert_not_contains "$OUTPUT" "TypeError" "no TypeError on missing key"
100-
assert_not_contains "$OUTPUT" "Cannot read properties" "no unhandled error on missing key"
101-
if [ -n "$SAVED_KEY" ]; then
102-
export ANTHROPIC_API_KEY="$SAVED_KEY"
103-
fi
104-
105-
# 7. Config backwards compatibility
106-
echo " [7/7] Config backwards compat..."
93+
# 6. Config backwards compatibility
94+
echo " [6/10] Config backwards compat..."
10795
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/altimate-code"
10896
mkdir -p "$CONFIG_DIR"
10997
if [ -f "$SCRIPT_DIR/fixtures/old-config.json" ]; then
@@ -119,8 +107,8 @@ else
119107
skip_test "Config backwards compat" "old-config.json fixture not found"
120108
fi
121109

122-
# 8. Broken config graceful handling
123-
echo " [8/8] Broken config handling..."
110+
# 7. Broken config graceful handling
111+
echo " [7/10] Broken config handling..."
124112
if [ -f "$SCRIPT_DIR/fixtures/broken-config.json" ]; then
125113
cp "$SCRIPT_DIR/fixtures/broken-config.json" "$CONFIG_DIR/opencode.json"
126114
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
@@ -135,6 +123,76 @@ else
135123
skip_test "Broken config handling" "broken-config.json fixture not found"
136124
fi
137125

126+
# 8. Yolo deny enforcement — deny rules block even with --yolo (#372, #377)
127+
echo " [8/10] Yolo deny enforcement..."
128+
if [ -n "${ANTHROPIC_API_KEY:-}" ]; then
129+
# Create a config with an explicit deny rule
130+
# App reads from $XDG_CONFIG_HOME/altimate-code/opencode.jsonc
131+
DENY_CONFIG_DIR=$(mktemp -d /tmp/sanity-deny-config-XXXXXX)
132+
DENY_MARKER="$DENY_CONFIG_DIR/deny-marker"
133+
mkdir -p "$DENY_CONFIG_DIR/altimate-code"
134+
cat > "$DENY_CONFIG_DIR/altimate-code/opencode.jsonc" <<DENYEOF
135+
{
136+
"permission": {
137+
"bash": {
138+
"*": "allow",
139+
"touch ${DENY_MARKER}": "deny",
140+
"touch /tmp/sanity-deny-*": "deny"
141+
}
142+
}
143+
}
144+
DENYEOF
145+
# Use an observable side-effect: deny "touch" on a marker file.
146+
# If deny enforcement works, the marker file will NOT be created.
147+
DENY_OUTPUT=$(XDG_CONFIG_HOME="$DENY_CONFIG_DIR" timeout 30 altimate run --max-turns 2 --yolo --format json \
148+
"run this exact bash command: touch $DENY_MARKER" 2>&1 || true)
149+
# Primary check: the marker file must not exist (deny blocked execution)
150+
if [ -f "$DENY_MARKER" ]; then
151+
echo " FAIL: yolo mode bypassed deny rule — denied command was executed"
152+
FAIL_COUNT=$((FAIL_COUNT + 1))
153+
elif echo "$DENY_OUTPUT" | grep -qi "denied\|blocked\|BLOCKED by deny rule\|not allowed"; then
154+
echo " PASS: yolo deny rule explicitly blocked command"
155+
PASS_COUNT=$((PASS_COUNT + 1))
156+
elif [ -z "$DENY_OUTPUT" ]; then
157+
echo " FAIL: no output from deny enforcement test"
158+
FAIL_COUNT=$((FAIL_COUNT + 1))
159+
else
160+
# Model may have refused on its own — marker absent so still safe
161+
echo " PASS: yolo deny rule (command not executed, marker absent)"
162+
PASS_COUNT=$((PASS_COUNT + 1))
163+
fi
164+
rm -rf "$DENY_CONFIG_DIR"
165+
else
166+
skip_test "Yolo deny enforcement" "no ANTHROPIC_API_KEY"
167+
fi
168+
169+
# 9. Missing API key handling (no unhandled exceptions)
170+
echo " [9/10] Missing API key handling..."
171+
SAVED_KEY="${ANTHROPIC_API_KEY:-}"
172+
unset ANTHROPIC_API_KEY
173+
OUTPUT=$(timeout 10 altimate run --max-turns 1 --yolo "hello" 2>&1 || true)
174+
assert_not_contains "$OUTPUT" "TypeError" "no TypeError on missing key"
175+
assert_not_contains "$OUTPUT" "Cannot read properties" "no unhandled error on missing key"
176+
if [ -n "$SAVED_KEY" ]; then
177+
export ANTHROPIC_API_KEY="$SAVED_KEY"
178+
fi
179+
180+
# 10. No internet — graceful error, not blank screen (#181)
181+
echo " [10/10] No internet graceful handling..."
182+
# Use an unreachable DNS to simulate no internet (timeout quickly)
183+
NO_NET_OUTPUT=$(timeout 15 env https_proxy=http://192.0.2.1:1 http_proxy=http://192.0.2.1:1 \
184+
altimate run --max-turns 1 --yolo "hello" 2>&1 || true)
185+
assert_not_contains "$NO_NET_OUTPUT" "TypeError" "no TypeError without internet"
186+
assert_not_contains "$NO_NET_OUTPUT" "Cannot read properties" "no unhandled error without internet"
187+
# Should get some kind of connection/auth error, not a blank hang
188+
if [ -z "$NO_NET_OUTPUT" ]; then
189+
echo " FAIL: no output at all without internet (blank screen)"
190+
FAIL_COUNT=$((FAIL_COUNT + 1))
191+
else
192+
echo " PASS: produced output without internet ($(echo "$NO_NET_OUTPUT" | wc -l) lines)"
193+
PASS_COUNT=$((PASS_COUNT + 1))
194+
fi
195+
138196
# Cleanup
139197
rm -rf "$WORKDIR"
140198

test/sanity/phases/smoke-tests.sh

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,19 @@ test_duckdb() {
7979
}
8080

8181
test_postgres() {
82-
if [ -z "${TEST_PG_HOST:-}" ]; then
83-
echo "SKIP" > "$RESULTS_DIR/postgres"
84-
return
82+
# Use env vars if set (Docker compose), else probe local Docker port
83+
local pg_host="${TEST_PG_HOST:-}"
84+
local pg_port="${TEST_PG_PORT:-5432}"
85+
if [ -z "$pg_host" ]; then
86+
if timeout 2 bash -c "echo >/dev/tcp/127.0.0.1/15432" 2>/dev/null; then
87+
pg_host="127.0.0.1"; pg_port="15432"
88+
else
89+
echo "SKIP" > "$RESULTS_DIR/postgres"
90+
return
91+
fi
8592
fi
8693
cd "$WORKDIR" || return 1
87-
altimate_run "postgres" "run SELECT 1 against postgres at ${TEST_PG_HOST}:${TEST_PG_PORT:-5432}"
94+
altimate_run "postgres" "run SELECT 1 against postgres at ${pg_host}:${pg_port}"
8895
local output=$(get_output "postgres")
8996
if echo "$output" | grep -qi "TIMEOUT\|unhandled"; then
9097
echo "FAIL" > "$RESULTS_DIR/postgres"
@@ -94,9 +101,19 @@ test_postgres() {
94101
}
95102

96103
test_snowflake() {
104+
# Snowflake requires real cloud credentials — cannot be Docker'd.
105+
# Accepts either ALTIMATE_CODE_CONN_SNOWFLAKE_TEST (JSON config) or
106+
# individual SNOWFLAKE_ACCOUNT/USER/PASSWORD env vars.
97107
if [ -z "${ALTIMATE_CODE_CONN_SNOWFLAKE_TEST:-}" ]; then
98-
echo "SKIP" > "$RESULTS_DIR/snowflake"
99-
return
108+
if [ -n "${SNOWFLAKE_ACCOUNT:-}" ] && [ -n "${SNOWFLAKE_USER:-}" ] && [ -n "${SNOWFLAKE_PASSWORD:-}" ]; then
109+
export ALTIMATE_CODE_CONN_SNOWFLAKE_TEST=$(cat <<SFEOF
110+
{"type":"snowflake","account":"${SNOWFLAKE_ACCOUNT}","user":"${SNOWFLAKE_USER}","password":"${SNOWFLAKE_PASSWORD}","warehouse":"${SNOWFLAKE_WAREHOUSE:-COMPUTE_WH}","role":"${SNOWFLAKE_ROLE:-PUBLIC}"}
111+
SFEOF
112+
)
113+
else
114+
echo "SKIP" > "$RESULTS_DIR/snowflake"
115+
return
116+
fi
100117
fi
101118
cd "$WORKDIR" || return 1
102119
altimate_run "snowflake" "run SELECT 1 against snowflake"
@@ -108,6 +125,28 @@ test_snowflake() {
108125
fi
109126
}
110127

128+
test_mongodb() {
129+
# Use env vars if set (Docker compose), else probe local Docker port
130+
local mongo_host="${TEST_MONGODB_HOST:-}"
131+
local mongo_port="${TEST_MONGODB_PORT:-27017}"
132+
if [ -z "$mongo_host" ]; then
133+
if timeout 2 bash -c "echo >/dev/tcp/127.0.0.1/17017" 2>/dev/null; then
134+
mongo_host="127.0.0.1"; mongo_port="17017"
135+
else
136+
echo "SKIP" > "$RESULTS_DIR/mongodb"
137+
return
138+
fi
139+
fi
140+
cd "$WORKDIR" || return 1
141+
altimate_run "mongodb" "run a find command on the admin database against mongodb at ${mongo_host}:${mongo_port}"
142+
local output=$(get_output "mongodb")
143+
if echo "$output" | grep -qi "TIMEOUT\|unhandled\|driver not installed"; then
144+
echo "FAIL" > "$RESULTS_DIR/mongodb"
145+
else
146+
echo "PASS" > "$RESULTS_DIR/mongodb"
147+
fi
148+
}
149+
111150
test_builder() {
112151
cd "$WORKDIR"
113152
altimate_run "builder" --agent builder "say hello"
@@ -152,6 +191,47 @@ test_discover() {
152191
fi
153192
}
154193

194+
test_dbt_discover() {
195+
# Use isolated workdir to avoid race conditions with parallel tests (#448, #270)
196+
local dbt_dir=$(mktemp -d /tmp/sanity-dbt-XXXXXX)
197+
cd "$dbt_dir"
198+
git init -q
199+
git config user.name "sanity-test"
200+
git config user.email "sanity@test.local"
201+
echo '{}' > package.json
202+
mkdir -p models
203+
cat > dbt_project.yml <<'DBTEOF'
204+
name: sanity_test
205+
version: '1.0.0'
206+
config-version: 2
207+
profile: sanity_test
208+
DBTEOF
209+
cat > models/test_model.sql <<'SQLEOF'
210+
SELECT 1 AS id
211+
SQLEOF
212+
git add -A && git commit -q -m "add dbt project"
213+
SANITY_TIMEOUT=90 altimate_run_with_turns "dbt-discover" 2 "discover dbt project config in this repo" || true
214+
local output=$(get_output "dbt-discover")
215+
if echo "$output" | grep -qi "unhandled\|TypeError\|Cannot read"; then
216+
echo "FAIL" > "$RESULTS_DIR/dbt-discover"
217+
else
218+
echo "PASS" > "$RESULTS_DIR/dbt-discover"
219+
fi
220+
rm -rf "$dbt_dir"
221+
}
222+
223+
test_check_command() {
224+
cd "$WORKDIR"
225+
# altimate-code check should run deterministic SQL checks without LLM (#453)
226+
echo "SELECT * FROM users WHERE 1=1;" > check_target.sql
227+
local output=$(timeout 30 altimate check check_target.sql 2>&1 || true)
228+
if echo "$output" | grep -qi "TypeError\|unhandled\|Cannot read properties"; then
229+
echo "FAIL" > "$RESULTS_DIR/check-cmd"
230+
else
231+
echo "PASS" > "$RESULTS_DIR/check-cmd"
232+
fi
233+
}
234+
155235
# Run tests in parallel batches
156236
echo ""
157237
echo " Running $MAX_PARALLEL tests concurrently..."
@@ -164,10 +244,13 @@ TESTS=(
164244
"test_duckdb"
165245
"test_postgres"
166246
"test_snowflake"
247+
"test_mongodb"
167248
"test_builder"
168249
"test_analyst"
169250
"test_bad_command"
170251
"test_discover"
252+
"test_dbt_discover"
253+
"test_check_command"
171254
)
172255

173256
TEST_NAMES=(
@@ -177,10 +260,13 @@ TEST_NAMES=(
177260
"duckdb"
178261
"postgres"
179262
"snowflake"
263+
"mongodb"
180264
"builder"
181265
"analyst"
182266
"bad-cmd"
183267
"discover"
268+
"dbt-discover"
269+
"check-cmd"
184270
)
185271

186272
# Launch in batches of MAX_PARALLEL

0 commit comments

Comments
 (0)