Skip to content

Commit fbcc97b

Browse files
Add BATS test suite and GHA workflow for CI
- 51 tests across install.bats, uninstall.bats, shell_hook.bats, post_checkout.bats - PATH-shadowing mocks for curl, git, luca, sudo, uname, unzip - Hermetic test isolation via temp HOME/INSTALL_DIR overrides - GHA workflow runs on ubuntu-latest and macos-latest on push/PR - Patch install.sh and uninstall.sh to support INSTALL_DIR env override Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 83b16d8 commit fbcc97b

18 files changed

Lines changed: 972 additions & 2 deletions

File tree

.github/workflows/test.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Test
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
name: BATS Tests (${{ matrix.os }})
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
os: [ubuntu-latest, macos-latest]
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- name: Install bats-core (Linux)
22+
if: runner.os == 'Linux'
23+
run: sudo apt-get install -y bats
24+
25+
- name: Install bats-core (macOS)
26+
if: runner.os == 'macOS'
27+
run: brew install bats-core
28+
29+
- name: Install bats helper libraries
30+
run: |
31+
git clone --depth 1 https://github.com/bats-core/bats-support.git \
32+
tests/test_helper/bats-support
33+
git clone --depth 1 https://github.com/bats-core/bats-assert.git \
34+
tests/test_helper/bats-assert
35+
36+
- name: Make scripts and mocks executable
37+
run: |
38+
chmod +x install.sh uninstall.sh shell_hook.sh post-checkout
39+
chmod +x tests/test_helper/mocks/*
40+
41+
- name: Run tests
42+
run: bats --tap tests/*.bats

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# bats helper libraries (cloned at CI time, not committed)
2+
tests/test_helper/bats-support/
3+
tests/test_helper/bats-assert/
4+
5+
.DS_Store

install.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
TOOL_NAME="Luca"
1212
BIN_NAME="luca"
13-
INSTALL_DIR="/usr/local/bin"
13+
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
1414
TOOL_FOLDER=".luca"
1515
VERSION_FILE="${PWD}/.luca-version"
1616
ORGANIZATION="LucaTools"

tests/fixtures/Lucafile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tools:
2+
- name: mytool
3+
version: "1.0.0"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"tag_name": "v2.0.0", "name": "Luca v2.0.0"}

tests/fixtures/bashrc_with_hook

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export PATH="$PATH:/usr/local/bin"
2+
3+
# Initialize Luca shell hook (added by Luca installer)
4+
[[ -s "$HOME/.luca/shell_hook.sh" ]] && source "$HOME/.luca/shell_hook.sh"

tests/install.bats

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
#!/usr/bin/env bats
2+
3+
load 'test_helper/bats-support/load'
4+
load 'test_helper/bats-assert/load'
5+
load 'test_helper/common'
6+
7+
setup() {
8+
common_setup
9+
}
10+
11+
# ---------------------------------------------------------------------------
12+
# Version detection
13+
# ---------------------------------------------------------------------------
14+
15+
@test "version: reads from .luca-version file" {
16+
echo "v1.2.3" > "$BATS_TEST_TMPDIR/.luca-version"
17+
18+
run bash "$REPO_ROOT/install.sh"
19+
20+
assert_output --partial "Using version from"
21+
assert_output --partial "Target version: v1.2.3"
22+
}
23+
24+
@test "version: fetches latest from GitHub API when no version file" {
25+
run bash "$REPO_ROOT/install.sh"
26+
27+
assert_output --partial "Missing"
28+
assert_output --partial "Fetching the latest version"
29+
assert_output --partial "v2.0.0"
30+
}
31+
32+
@test "version: API returns empty object exits with error" {
33+
export MOCK_CURL_BEHAVIOR=api_empty
34+
35+
run bash "$REPO_ROOT/install.sh"
36+
37+
assert_failure
38+
assert_output --partial "ERROR: Could not fetch latest version"
39+
}
40+
41+
@test "version: invalid semver in version file exits with error" {
42+
echo "not-a-version" > "$BATS_TEST_TMPDIR/.luca-version"
43+
44+
run bash "$REPO_ROOT/install.sh"
45+
46+
assert_failure
47+
assert_output --partial "Invalid version format"
48+
}
49+
50+
@test "version: valid semver without v prefix is accepted" {
51+
echo "1.2.3" > "$BATS_TEST_TMPDIR/.luca-version"
52+
53+
run bash "$REPO_ROOT/install.sh"
54+
55+
assert_output --partial "Target version: 1.2.3"
56+
}
57+
58+
@test "version: valid semver with prerelease tag is accepted" {
59+
echo "v1.2.3-beta.1" > "$BATS_TEST_TMPDIR/.luca-version"
60+
61+
run bash "$REPO_ROOT/install.sh"
62+
63+
assert_output --partial "Target version: v1.2.3-beta.1"
64+
}
65+
66+
@test "version: GITHUB_TOKEN set prints authenticated request message" {
67+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
68+
69+
run env GITHUB_TOKEN=mytoken bash "$REPO_ROOT/install.sh"
70+
71+
assert_output --partial "Using GITHUB_TOKEN for authenticated GitHub API requests"
72+
}
73+
74+
# ---------------------------------------------------------------------------
75+
# OS detection
76+
# ---------------------------------------------------------------------------
77+
78+
@test "os: Darwin detected as macOS" {
79+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
80+
# MOCK_UNAME_OUTPUT defaults to Darwin
81+
82+
run bash "$REPO_ROOT/install.sh"
83+
84+
assert_output --partial "Detected macOS"
85+
}
86+
87+
@test "os: Linux detected via /etc/os-release" {
88+
if [ ! -f /etc/os-release ]; then
89+
skip "Requires /etc/os-release (Linux only)"
90+
fi
91+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
92+
export MOCK_UNAME_OUTPUT="Linux"
93+
94+
run bash "$REPO_ROOT/install.sh"
95+
96+
assert_output --partial "Detected Linux"
97+
}
98+
99+
# ---------------------------------------------------------------------------
100+
# Already up to date
101+
# ---------------------------------------------------------------------------
102+
103+
@test "skip: already up-to-date version exits 0 without downloading" {
104+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
105+
export MOCK_LUCA_VERSION="v2.0.0"
106+
# Place a fake luca binary that returns v2.0.0
107+
cp "$TESTS_DIR/test_helper/mocks/luca" "$TEST_INSTALL_DIR/luca"
108+
109+
run bash "$REPO_ROOT/install.sh"
110+
111+
assert_success
112+
assert_output --partial "already up to date"
113+
# Verify curl was NOT called for a download
114+
refute_output --partial "Downloading"
115+
}
116+
117+
@test "skip: different installed version proceeds with update" {
118+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
119+
export MOCK_LUCA_VERSION="v1.0.0"
120+
cp "$TESTS_DIR/test_helper/mocks/luca" "$TEST_INSTALL_DIR/luca"
121+
122+
run bash "$REPO_ROOT/install.sh"
123+
124+
assert_output --partial "Updating to version"
125+
}
126+
127+
# ---------------------------------------------------------------------------
128+
# Download and install
129+
# ---------------------------------------------------------------------------
130+
131+
@test "install: happy path installs binary to INSTALL_DIR" {
132+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
133+
134+
run bash "$REPO_ROOT/install.sh"
135+
136+
assert_success
137+
assert_output --partial "successfully installed"
138+
assert [ -f "$TEST_INSTALL_DIR/luca" ]
139+
}
140+
141+
@test "install: curl download failure exits with error" {
142+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
143+
export MOCK_CURL_BEHAVIOR=download_fail
144+
145+
run bash "$REPO_ROOT/install.sh"
146+
147+
assert_failure
148+
assert_output --partial "ERROR: Could not download"
149+
}
150+
151+
@test "install: unzip failure exits with error and cleans up zip" {
152+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
153+
export MOCK_UNZIP_BEHAVIOR=fail
154+
155+
run bash "$REPO_ROOT/install.sh"
156+
157+
assert_failure
158+
assert_output --partial "ERROR: Failed to extract"
159+
# Zip file should have been cleaned up
160+
assert [ ! -f "$BATS_TEST_TMPDIR/Luca-macOS.zip" ]
161+
}
162+
163+
# ---------------------------------------------------------------------------
164+
# Shell hook setup
165+
# ---------------------------------------------------------------------------
166+
167+
@test "shell_hook: download failure is non-fatal (exits 0)" {
168+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
169+
export MOCK_CURL_BEHAVIOR=shell_hook_fail
170+
171+
run bash "$REPO_ROOT/install.sh"
172+
173+
assert_success
174+
assert_output --partial "WARNING: Could not download shell hook"
175+
}
176+
177+
# ---------------------------------------------------------------------------
178+
# Git hook setup
179+
# ---------------------------------------------------------------------------
180+
181+
@test "git: not in a git repo skips hook installation silently" {
182+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
183+
# MOCK_GIT_REPO_ROOT unset → mock git returns exit 128 (not a repo)
184+
185+
run bash "$REPO_ROOT/install.sh"
186+
187+
assert_success
188+
refute_output --partial "Git post-checkout hook installed"
189+
}
190+
191+
@test "git: in a git repo installs post-checkout hook" {
192+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
193+
local git_root="$BATS_TEST_TMPDIR/fake_repo"
194+
mkdir -p "$git_root/.git/hooks"
195+
export MOCK_GIT_REPO_ROOT="$git_root"
196+
197+
run bash "$REPO_ROOT/install.sh"
198+
199+
assert_success
200+
assert_output --partial "Git post-checkout hook installed"
201+
assert [ -f "$git_root/.git/hooks/post-checkout" ]
202+
}
203+
204+
@test "git: hook with Luca identifier already exists is skipped" {
205+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
206+
local git_root="$BATS_TEST_TMPDIR/fake_repo"
207+
mkdir -p "$git_root/.git/hooks"
208+
echo "# LUCA POST-CHECKOUT GIT HOOK" > "$git_root/.git/hooks/post-checkout"
209+
export MOCK_GIT_REPO_ROOT="$git_root"
210+
211+
run bash "$REPO_ROOT/install.sh"
212+
213+
assert_success
214+
assert_output --partial "Luca post-checkout hook already installed"
215+
}
216+
217+
@test "git: foreign hook without Luca identifier warns and does not overwrite" {
218+
echo "v2.0.0" > "$BATS_TEST_TMPDIR/.luca-version"
219+
local git_root="$BATS_TEST_TMPDIR/fake_repo"
220+
mkdir -p "$git_root/.git/hooks"
221+
echo "#!/bin/sh\n# some other tool" > "$git_root/.git/hooks/post-checkout"
222+
export MOCK_GIT_REPO_ROOT="$git_root"
223+
224+
run bash "$REPO_ROOT/install.sh"
225+
226+
assert_success
227+
assert_output --partial "A post-checkout hook already exists"
228+
# Original hook content should be unchanged
229+
assert [ -f "$git_root/.git/hooks/post-checkout" ]
230+
}

0 commit comments

Comments
 (0)