#!/usr/bin/env bash set -euo pipefail PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CLI="$PROJECT_DIR/bin/easel" TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/easel-test.XXXXXX")" WORK_DIR="$TEST_ROOT/work space" TEST_BIN="$TEST_ROOT/bin" cleanup() { case "$TEST_ROOT" in "${TMPDIR:-/tmp}"/easel-test.*|/private/tmp/easel-test.*) rm -rf "$TEST_ROOT" ;; esac } trap cleanup EXIT mkdir -p "$WORK_DIR" "$TEST_BIN" WORK_DIR="$(cd "$WORK_DIR" && pwd -P)" ln -s "$CLI" "$TEST_BIN/ac" assert_contains() { local output="$1" local expected="$2" [[ "$output" == *"$expected"* ]] || { printf 'Expected output to contain: %s\nActual output:\n%s\n' "$expected" "$output" >&2 exit 1 } } output="$($CLI --version)" expected_version="$(node -p "require('$PROJECT_DIR/package.json').version")" assert_contains "$output" "aesel $expected_version" output="$(AESEL_DRY_RUN=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'interface=easel' assert_contains "$output" "directory=$WORK_DIR" output="$(AESEL_DRY_RUN=1 "$TEST_BIN/ac" "$WORK_DIR")" assert_contains "$output" 'interface=easel' assert_contains "$output" "directory=$WORK_DIR" output="$(AESEL_DRY_RUN=1 "$CLI" --resume 00000000-0000-0000-0000-000000000001 --prompt continue "$WORK_DIR")" assert_contains "$output" 'resume=yes' assert_contains "$output" 'initial_prompt=yes' output="$($CLI doctor)" assert_contains "$output" 'control plane: local' assert_contains "$output" 'telemetry: off' assert_contains "$output" 'account: ' # Account commands read the shared ~/.ac-token under $HOME. FAKE_HOME="$TEST_ROOT/home" mkdir -p "$FAKE_HOME" if output="$(HOME="$FAKE_HOME" "$CLI" whoami 2>&1)"; then printf 'Expected whoami to exit non-zero when signed out.\n' >&2 exit 1 fi assert_contains "$output" 'not signed in' printf '{"access_token":"t","user":{"handle":"tester"}}' > "$FAKE_HOME/.ac-token" output="$(HOME="$FAKE_HOME" "$CLI" whoami)" assert_contains "$output" '@tester' printf 'export function paint() {}\n' > "$WORK_DIR/smiley.mjs" output="$(HOME="$FAKE_HOME" AESEL_DRY_RUN=1 "$CLI" publish "$WORK_DIR/smiley.mjs")" assert_contains "$output" 'https://aesthetic.computer/@tester/smiley' if HOME="$FAKE_HOME" AESEL_DRY_RUN=1 "$CLI" publish >/dev/null 2>&1; then printf 'Expected publish without a file to fail.\n' >&2 exit 1 fi output="$(AESEL_DRY_RUN=1 "$CLI" --runtime lisp "$WORK_DIR")" assert_contains "$output" 'runtime=lisp' output="$(AESEL_DRY_RUN=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'runtime=mjs' assert_contains "$output" 'genre=choose' output="$(AESEL_DRY_RUN=1 "$CLI" --genre nopaint "$WORK_DIR")" assert_contains "$output" 'genre=nopaint' assert_contains "$output" 'runtime=mjs' if AESEL_DRY_RUN=1 "$CLI" --genre nopaint --runtime lisp "$WORK_DIR" >/dev/null 2>&1; then printf 'Expected a nopaint brush to reject a non-JavaScript runtime.\n' >&2 exit 1 fi # Publishing is outward-facing, so it is off unless this session asked for it — # by flag or by environment, with the flag able to say no to the environment. assert_contains "$output" 'autopublish=off' output="$(AESEL_DRY_RUN=1 "$CLI" --autopublish "$WORK_DIR")" assert_contains "$output" 'autopublish=on' output="$(AESEL_DRY_RUN=1 AESEL_AUTOPUBLISH=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'autopublish=on' output="$(AESEL_DRY_RUN=1 AESEL_AUTOPUBLISH=1 "$CLI" --no-autopublish "$WORK_DIR")" assert_contains "$output" 'autopublish=off' # The names from before the rename still work, and the new name wins. output="$(EASEL_DRY_RUN=1 EASEL_AUTOPUBLISH=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'autopublish=on' output="$(AESEL_DRY_RUN=1 AESEL_AUTOPUBLISH=0 EASEL_AUTOPUBLISH=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'autopublish=off' output="$(AESEL_DRY_RUN=1 "$CLI" "$WORK_DIR")" # No backend is forced: the interface remembers the last provider, and a # first launch starts on the hosted one. assert_contains "$output" 'backend=remembered' output="$(AESEL_DRY_RUN=1 "$CLI" --backend codex "$WORK_DIR")" assert_contains "$output" 'backend=codex' output="$(AESEL_DRY_RUN=1 "$CLI" --backend claude --model claude-opus-5 "$WORK_DIR")" assert_contains "$output" 'model=claude-opus-5' if AESEL_DRY_RUN=1 "$CLI" --backend gemini "$WORK_DIR" >/dev/null 2>&1; then printf 'Expected an unknown backend to fail.\n' >&2 exit 1 fi # The terminal is pro unless a piece is asked for; private defaults off. output="$(AESEL_DRY_RUN=1 "$CLI" "$WORK_DIR")" assert_contains "$output" 'pro=on' assert_contains "$output" 'private=off' output="$(AESEL_DRY_RUN=1 "$CLI" piece "$WORK_DIR")" assert_contains "$output" 'pro=off' output="$(AESEL_DRY_RUN=1 "$CLI" --genre piece "$WORK_DIR")" assert_contains "$output" 'pro=off' output="$(AESEL_DRY_RUN=1 "$CLI" --pro "$WORK_DIR")" assert_contains "$output" 'pro=on' assert_contains "$output" 'private=off' output="$(AESEL_DRY_RUN=1 "$CLI" --private --pro "$WORK_DIR")" assert_contains "$output" 'pro=on' assert_contains "$output" 'private=on' output="$($CLI --help)" assert_contains "$output" '--pro' assert_contains "$output" '--private' output="$($CLI doctor)" assert_contains "$output" 'engine bridge claude:' assert_contains "$output" 'engine bridge codex:' assert_contains "$output" 'default engine bridge: the one you chose last (first launch: aesthetic)' if AESEL_DRY_RUN=1 "$CLI" --runtime rust "$WORK_DIR" >/dev/null 2>&1; then printf 'Expected an unknown runtime to fail.\n' >&2 exit 1 fi if AESEL_DRY_RUN=1 "$CLI" claude >/dev/null 2>&1; then printf 'Expected the removed provider shortcut to fail.\n' >&2 exit 1 fi if AESEL_DRY_RUN=1 "$CLI" "$WORK_DIR" extra >/dev/null 2>&1; then printf 'Expected extra launcher arguments to fail.\n' >&2 exit 1 fi printf 'CLI tests passed.\n'