From 461cbe9b9cbe1e887b9cc6ebc0e299673456fd9b Mon Sep 17 00:00:00 2001 From: Ryan Hughes Date: Mon, 7 Sep 2026 20:05:02 -0400 Subject: [PATCH] Exercise selected AI ports against the 4.0.2 baseline Keep provisioning tests isolated from application refresh and link the real command-presence helper into the Hermes remover fixture. Cover only selected AI menu/scaffold additions, retained default agents and Dictation placement, and new-user/migration mise pruning parity. (cherry picked from commit 1bd969eb386169e107d0b9f15be4b3e1d2a9b4cb) --- test/shell.d/hermes-remove-test.sh | 3 ++ test/shell.d/provision-user-test.sh | 43 ++++++++++++++++----- test/shell.d/release-ai-port-test.sh | 57 ++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 test/shell.d/release-ai-port-test.sh diff --git a/test/shell.d/hermes-remove-test.sh b/test/shell.d/hermes-remove-test.sh index f325b472..865d6b18 100755 --- a/test/shell.d/hermes-remove-test.sh +++ b/test/shell.d/hermes-remove-test.sh @@ -10,6 +10,9 @@ trap 'rm -rf "$test_tmp"' EXIT mock_bin="$test_tmp/bin" test_home="$test_tmp/home" mkdir -p "$mock_bin" +# The remover uses the installed helper; keep it available even in the Python +# cases that deliberately replace PATH with the fixture and system binaries. +ln -s "$ROOT/bin/omarchy-cmd-present" "$mock_bin/omarchy-cmd-present" # Keep package-path checks scoped to the fixture, even with a live app open. python3 - "$ROOT/bin/omarchy-remove-ai-hermes" "$test_tmp" <<'PY' diff --git a/test/shell.d/provision-user-test.sh b/test/shell.d/provision-user-test.sh index 7497d752..bbe63c36 100755 --- a/test/shell.d/provision-user-test.sh +++ b/test/shell.d/provision-user-test.sh @@ -15,25 +15,48 @@ for command in xdg-user-dirs-update xdg-settings xdg-mime; do done chmod +x "$mock_bin"/* -# Provisioning prepends $OMARCHY_PATH/bin, which shadows a mock for anything -# Omarchy ships, so the install suite is stubbed out at its path instead. The -# real one rethemes the session it runs in: hyprctl reload against the live -# compositor, gsettings against the live desktop, and a global Node install. -mkdir -p "$test_tmp/install/user" +# Isolate both setup leaves and application refresh. Refresh also invokes the +# mise leaf through OMARCHY_PATH, independently of OMARCHY_INSTALL. +fixture="$test_tmp/omarchy" +mkdir -p "$fixture/bin" "$test_tmp/install/user" +cp "$ROOT/bin/omarchy-provision-user" "$ROOT/bin/omarchy-done" "$fixture/bin/" +ln -s "$ROOT/default" "$fixture/default" +printf '#!/bin/bash\nexit 0\n' >"$fixture/bin/omarchy-refresh-applications" +chmod +x "$fixture/bin/omarchy-refresh-applications" : >"$test_tmp/install/user/all.sh" -HOME="$test_tmp/home" PATH="$mock_bin:$ROOT/bin:$PATH" OMARCHY_PATH="$ROOT" \ - OMARCHY_INSTALL="$test_tmp/install" bash "$ROOT/bin/omarchy-provision-user" >/dev/null || - fail "omarchy-provision-user finishes" +provision() { + HOME="$test_tmp/home" PATH="$mock_bin:$fixture/bin:$PATH" OMARCHY_PATH="$fixture" \ + OMARCHY_INSTALL="$test_tmp/install" bash "$fixture/bin/omarchy-provision-user" "$@" >/dev/null || + fail "omarchy-provision-user finishes" +} +provision for skill in omarchy diagnose-crash; do link="$test_tmp/home/.hermes/skills/$skill" - [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || + [[ -L $link && $(readlink "$link") == "$fixture/default/agents/skills/$skill" ]] || fail "omarchy-provision-user provisions the $skill skill for Hermes" link="$test_tmp/home/.hermes/profiles/james/skills/$skill" - [[ -L $link && $(readlink "$link") == "$ROOT/default/agents/skills/$skill" ]] || + [[ -L $link && $(readlink "$link") == "$fixture/default/agents/skills/$skill" ]] || fail "omarchy-provision-user provisions the $skill skill for a Hermes profile" done pass "omarchy-provision-user provisions Hermes skills" + +provision --force +for skill in omarchy diagnose-crash; do + for directory in .agents/skills .claude/skills .codex/skills .pi/agent/skills .hermes/skills .hermes/profiles/james/skills; do + link="$test_tmp/home/$directory/$skill" + [[ -L $link && $(readlink "$link") == "$fixture/default/agents/skills/$skill" ]] || + fail "forced provisioning keeps baseline and Hermes skill links idempotent" + done +done +pass "forced provisioning keeps baseline and Hermes skill links idempotent" + +rm -rf "$test_tmp/home" +mkdir -p "$test_tmp/home" +provision +[[ ! -e $test_tmp/home/.hermes/profiles ]] || fail "provisioning does not invent Hermes profiles" +[[ ! -e $test_tmp/home/.gemini ]] || fail "Hermes provisioning does not add unrelated Antigravity setup" +pass "provisioning without existing profiles adds only the Hermes default skill home" diff --git a/test/shell.d/release-ai-port-test.sh b/test/shell.d/release-ai-port-test.sh new file mode 100644 index 00000000..76d4aefd --- /dev/null +++ b/test/shell.d/release-ai-port-test.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +set -euo pipefail +source "$(dirname "$0")/base-test.sh" + +run_node_test <<'JS' +const fs = require('fs') +const text = fs.readFileSync(path.join(root, 'default/omarchy/omarchy-menu.jsonc'), 'utf8') +const rows = Object.fromEntries(text.split('\n').filter(line => /^ "/.test(line)).map(line => { + const object = JSON.parse('{' + line.trim().replace(/,$/, '') + '}') + return Object.entries(object)[0] +})) +assert(rows['remove.ai'] && !rows['remove.ai'].action, 'selected AI removers have a reachable parent') +assert(rows['remove.dictation'] && !rows['remove.ai.dictation'], 'Dictation retains its baseline location') +const selected = {'t3-code': ['t3code-bin', '\ue908'], hermes: ['hermes-desktop', '\ue90a'], openclaw: ['openclaw', '\ue90c'], perplexity: ['perplexity', '\ue90b']} +for (const [id, [pkg, glyph]] of Object.entries(selected)) { + const install = rows[`install.ai.${id}`], remove = rows[`remove.ai.${id}`] + assert(install && remove, `${id} has install and remove entries`) + assertEqual(install.when, `! omarchy-pkg-present ${pkg}`, `${id} preserves baseline install visibility`) + assertEqual(remove.when, `omarchy-pkg-present ${pkg}`, `${id} removal follows package presence`) + assertEqual(install.icon, glyph, `${id} install uses selected glyph`) + assertEqual(remove.icon, glyph, `${id} remove uses selected glyph`) + assertEqual(install.iconFont, 'omarchy', `${id} uses the packaged icon font`) + assert(fs.existsSync(path.join(root, `bin/omarchy-remove-ai-${id}`)), `${id} remover exists`) +} +assertDeepEqual(Object.keys(rows).filter(k => k.startsWith('remove.ai.')).sort(), Object.keys(selected).map(k => `remove.ai.${k}`).sort(), 'removal prerequisite imports no unselected apps') +assertDeepEqual(Object.keys(rows).filter(k => k.startsWith('setup.default.agent.')).map(k => k.split('.').pop()).sort(), ['claude', 'codex', 'copilot', 'crush', 'gemini', 'grok', 'hermes', 'omp', 'openclaw', 'opencode', 'pi'], 'baseline agent choices survive with only Hermes and OpenClaw added') +assert(!fs.existsSync(path.join(root, 'bin/omarchy-theme-set-openclaw')), 'deferred OpenClaw theming is absent') +JS + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT +mkdir -p "$test_tmp/bin" +export TEST_CALLS="$test_tmp/calls" +cat >"$test_tmp/bin/mise" <<'SH' +#!/bin/bash +printf 'mise:%s\n' "$*" >>"$TEST_CALLS" +SH +cat >"$test_tmp/bin/omarchy-mise-install" <<'SH' +#!/bin/bash +printf 'stub:%s\n' "$*" >>"$TEST_CALLS" +SH +cat >"$test_tmp/bin/omarchy-install-hermes-cli" <<'SH' +#!/bin/bash +printf 'hermes:%s\n' "$*" >>"$TEST_CALLS" +exit 1 +SH +chmod +x "$test_tmp/bin"/* +PATH="$test_tmp/bin:$PATH" bash -eE "$ROOT/install/user/mise.sh" +grep -qx 'mise:settings set upgrade.auto_prune false' "$TEST_CALLS" || fail "new users retain running mise versions" +grep -qx 'hermes:' "$TEST_CALLS" || fail "new users receive the Hermes CLI stub" +! grep -Eq 'stub:.*(ori|antigravity|hey-cli)' "$TEST_CALLS" || fail "mise setup avoids unrelated prerequisites" +pass "baseline mise setup tolerates an unfinished Hermes Desktop and disables pruning" +: >"$TEST_CALLS" +PATH="$test_tmp/bin:$PATH" bash -euo pipefail "$ROOT/migrations/1787215483.sh" >/dev/null +grep -qx 'mise:settings set upgrade.auto_prune false' "$TEST_CALLS" || fail "existing users retain running mise versions" +pass "mise migration applies the same no-pruning setting"