INIclaw / test /e2e-test.sh
Nitish kumar
Upload folder using huggingface_hub
0722e92 verified
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# E2E test for IniClaw + blueprint
# Runs inside the Docker sandbox
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}PASS${NC}: $1"; }
fail() { echo -e "${RED}FAIL${NC}: $1"; exit 1; }
info() { echo -e "${YELLOW}TEST${NC}: $1"; }
# -------------------------------------------------------
info "1. Verify OpenClaw CLI is installed"
# -------------------------------------------------------
openclaw --version && pass "OpenClaw CLI installed" || fail "OpenClaw CLI not found"
# -------------------------------------------------------
info "2. Verify plugin can be installed"
# -------------------------------------------------------
openclaw plugins install /opt/iniclaw 2>&1 && pass "Plugin installed" || {
# If plugins install isn't available, verify the built artifacts exist
if [ -f /opt/iniclaw/dist/index.js ]; then
pass "Plugin built successfully (dist/index.js exists)"
else
fail "Plugin build artifacts missing"
fi
}
# -------------------------------------------------------
info "3. Verify blueprint YAML is valid"
# -------------------------------------------------------
python3 -c "
import yaml, sys
bp = yaml.safe_load(open('/opt/iniclaw-blueprint/blueprint.yaml'))
assert bp['version'] == '0.1.0', f'Bad version: {bp[\"version\"]}'
profiles = bp['components']['inference']['profiles']
assert 'default' in profiles, 'Missing default profile'
assert 'vllm' in profiles, 'Missing vllm profile'
assert 'nim-local' in profiles, 'Missing nim-local profile'
print(f'Profiles: {list(profiles.keys())}')
" && pass "Blueprint YAML valid with all 3 profiles" || fail "Blueprint YAML invalid"
# -------------------------------------------------------
info "4. Verify blueprint runner plan command"
# -------------------------------------------------------
cd /opt/iniclaw-blueprint
# Runner will fail at openshell prereq check (expected in test container)
# We just verify it gets past validation and profile resolution
python3 orchestrator/runner.py plan --profile vllm --dry-run 2>&1 | tee /tmp/plan-output.txt || true
grep -q "RUN_ID:" /tmp/plan-output.txt && pass "Blueprint plan generates run ID" || fail "No run ID in plan output"
grep -q "Validating blueprint" /tmp/plan-output.txt && pass "Blueprint runner validates before execution" || fail "No validation step"
# -------------------------------------------------------
info "5. Verify host OpenClaw detection (migration source)"
# -------------------------------------------------------
[ -f /sandbox/.openclaw/openclaw.json ] && pass "Host OpenClaw config detected" || fail "No host config"
[ -d /sandbox/.openclaw/workspace ] && pass "Host workspace directory exists" || fail "No workspace dir"
[ -d /sandbox/.openclaw/skills ] && pass "Host skills directory exists" || fail "No skills dir"
[ -d /sandbox/.openclaw/hooks ] && pass "Host hooks directory exists" || fail "No hooks dir"
[ -f /sandbox/.openclaw/hooks/demo-hook/HOOK.md ] && pass "Host hook fixture exists" || fail "No hook fixture"
# -------------------------------------------------------
info "6. Verify snapshot creation (migration pre-step)"
# -------------------------------------------------------
python3 -c "
import sys
sys.path.insert(0, '/opt/iniclaw-blueprint/migrations')
from snapshot import create_snapshot, list_snapshots
snap = create_snapshot()
assert snap is not None, 'Snapshot returned None'
assert snap.exists(), f'Snapshot dir does not exist: {snap}'
hook_file = snap / 'openclaw' / 'hooks' / 'demo-hook' / 'HOOK.md'
assert hook_file.exists(), f'Hook file missing from snapshot: {hook_file}'
snaps = list_snapshots()
assert len(snaps) == 1, f'Expected 1 snapshot, got {len(snaps)}'
print(f'Snapshot created at: {snap}')
print(f'Files captured: {snaps[0][\"file_count\"]}')
" && pass "Migration snapshot created successfully" || fail "Snapshot creation failed"
# -------------------------------------------------------
info "7. Verify snapshot restore (eject path)"
# -------------------------------------------------------
python3 -c "
import sys, json, shutil
sys.path.insert(0, '/opt/iniclaw-blueprint/migrations')
from snapshot import list_snapshots, rollback_from_snapshot
from pathlib import Path
snaps = list_snapshots()
snap_path = Path(snaps[0]['path'])
# Simulate corruption: modify the host config
config = Path.home() / '.openclaw' / 'openclaw.json'
original = json.loads(config.read_text())
config.write_text(json.dumps({'corrupted': True}))
# Rollback
success = rollback_from_snapshot(snap_path)
assert success, 'Rollback returned False'
# Verify restoration
restored = json.loads(config.read_text())
assert restored.get('meta', {}).get('lastTouchedVersion') == '2026.3.11', f'Restored config wrong: {restored}'
assert 'corrupted' not in restored, 'Config still corrupted after rollback'
print(f'Restored config: {restored}')
" && pass "Snapshot rollback restores original config" || fail "Rollback failed"
# -------------------------------------------------------
info "8. Verify migration inventory for external OpenClaw roots"
# -------------------------------------------------------
OPENCLAW_STATE_DIR=/sandbox/openclaw-state OPENCLAW_CONFIG_PATH=/sandbox/config/openclaw.json node --input-type=module <<'JS'
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import {
cleanupSnapshotBundle,
createArchiveFromDirectory,
createSnapshotBundle,
detectHostOpenClaw,
} from "/opt/iniclaw/dist/commands/migration-state.js";
const logger = {
info() {},
warn() {},
error(message) {
throw new Error(String(message));
},
debug() {},
};
const state = detectHostOpenClaw(process.env);
if (!state.exists) {
throw new Error("detectHostOpenClaw did not find the overridden install");
}
if (state.stateDir !== "/sandbox/openclaw-state") {
throw new Error(`Unexpected state dir: ${state.stateDir}`);
}
if (state.configPath !== "/sandbox/config/openclaw.json") {
throw new Error(`Unexpected config path: ${state.configPath}`);
}
if (state.externalRoots.length < 3) {
throw new Error(`Expected at least 3 external roots, got ${state.externalRoots.length}`);
}
const bundle = createSnapshotBundle(state, logger, { persist: false });
if (!bundle) {
throw new Error("createSnapshotBundle returned null");
}
try {
const workspaceRoot = bundle.manifest.externalRoots.find((root) => root.kind === "workspace");
if (!workspaceRoot) {
throw new Error("Missing workspace root in manifest");
}
const snapshotLink = path.join(
bundle.snapshotDir,
workspaceRoot.snapshotRelativePath,
"shared-link.md",
);
if (!fs.lstatSync(snapshotLink).isSymbolicLink()) {
throw new Error(`Snapshot did not preserve symlink: ${snapshotLink}`);
}
const sandboxConfig = JSON.parse(
fs.readFileSync(path.join(bundle.preparedStateDir, "openclaw.json"), "utf-8"),
);
if (sandboxConfig.agents.defaults.workspace !== workspaceRoot.sandboxPath) {
throw new Error(
`Sandbox config was not rewritten for default workspace: ${sandboxConfig.agents.defaults.workspace}`,
);
}
if (sandboxConfig.agents.list[0].agentDir !== "/sandbox/.iniclaw/migration/agent-dirs/agent-dirs-main-agent-dir") {
throw new Error(`Sandbox config did not rewrite agentDir: ${sandboxConfig.agents.list[0].agentDir}`);
}
const archivePath = path.join(bundle.archivesDir, "workspace.tar");
await createArchiveFromDirectory(path.join(bundle.snapshotDir, workspaceRoot.snapshotRelativePath), archivePath);
const extractDir = fs.mkdtempSync(path.join(os.tmpdir(), "iniclaw-archive-"));
execFileSync("tar", ["-xf", archivePath, "-C", extractDir]);
const extractedLink = path.join(extractDir, "shared-link.md");
if (!fs.lstatSync(extractedLink).isSymbolicLink()) {
throw new Error(`Tar archive did not preserve symlink: ${extractedLink}`);
}
const fallbackHome = fs.mkdtempSync(path.join(os.tmpdir(), "iniclaw-userprofile-"));
fs.mkdirSync(path.join(fallbackHome, ".openclaw"), { recursive: true });
fs.writeFileSync(path.join(fallbackHome, ".openclaw", "openclaw.json"), "{}");
const fallbackState = detectHostOpenClaw({
HOME: "",
USERPROFILE: fallbackHome,
});
if (!fallbackState.exists || fallbackState.stateDir !== path.join(fallbackHome, ".openclaw")) {
throw new Error("USERPROFILE fallback did not resolve the host OpenClaw state");
}
} finally {
cleanupSnapshotBundle(bundle);
}
JS
pass "Migration inventory handles overrides, external roots, and symlink-safe archives"
# -------------------------------------------------------
info "9. Verify plugin TypeScript compilation"
# -------------------------------------------------------
[ -f /opt/iniclaw/dist/index.js ] && pass "index.js compiled" || fail "index.js missing"
[ -f /opt/iniclaw/dist/commands/migrate.js ] && pass "migrate.js compiled" || fail "migrate.js missing"
[ -f /opt/iniclaw/dist/commands/migration-state.js ] && pass "migration-state.js compiled" || fail "migration-state.js missing"
[ -f /opt/iniclaw/dist/commands/launch.js ] && pass "launch.js compiled" || fail "launch.js missing"
[ -f /opt/iniclaw/dist/commands/connect.js ] && pass "connect.js compiled" || fail "connect.js missing"
[ -f /opt/iniclaw/dist/commands/eject.js ] && pass "eject.js compiled" || fail "eject.js missing"
[ -f /opt/iniclaw/dist/commands/status.js ] && pass "status.js compiled" || fail "status.js missing"
[ -f /opt/iniclaw/dist/commands/slash.js ] && pass "slash.js compiled" || fail "slash.js missing"
[ -f /opt/iniclaw/dist/blueprint/resolve.js ] && pass "resolve.js compiled" || fail "resolve.js missing"
[ -f /opt/iniclaw/dist/blueprint/verify.js ] && pass "verify.js compiled" || fail "verify.js missing"
[ -f /opt/iniclaw/dist/blueprint/exec.js ] && pass "exec.js compiled" || fail "exec.js missing"
[ -f /opt/iniclaw/dist/blueprint/state.js ] && pass "state.js compiled" || fail "state.js missing"
# -------------------------------------------------------
info "10. Verify IniClaw state management"
# -------------------------------------------------------
node -e "
const { loadState, saveState, clearState } = require('/opt/iniclaw/dist/blueprint/state.js');
// Initial state should be empty
let state = loadState();
console.assert(state.lastAction === null, 'Initial state should be null');
// Save and reload
saveState({ ...state, lastAction: 'migrate', lastRunId: 'test-123', sandboxName: 'openclaw' });
state = loadState();
console.assert(state.lastAction === 'migrate', 'Should be migrate');
console.assert(state.lastRunId === 'test-123', 'Should be test-123');
console.assert(state.updatedAt !== null, 'Should have timestamp');
// Clear
clearState();
state = loadState();
console.assert(state.lastAction === null, 'Should be cleared');
console.log('State management: create, save, load, clear all working');
" && pass "IniClaw state management works" || fail "State management broken"
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} ALL E2E TESTS PASSED${NC}"
echo -e "${GREEN}========================================${NC}"