diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8382c03 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Tests + +on: + push: + branches: ["**"] + pull_request: + branches: ["**"] + +jobs: + bats: + name: bats (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - uses: actions/checkout@v4 + + - name: Install bats-core (Ubuntu) + if: runner.os == 'Linux' + run: | + git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core + sudo /tmp/bats-core/install.sh /usr/local + + - name: Install bats-core (macOS) + if: runner.os == 'macOS' + run: brew install bats-core + + - name: Run tests + run: bats tests/ diff --git a/ccm.sh b/ccm.sh index 2d1be07..ff4b8b1 100755 --- a/ccm.sh +++ b/ccm.sh @@ -102,9 +102,18 @@ detect_platform() { # Returns: Prints the absolute path to .claude.json # Usage: config_path=$(get_claude_config_path) get_claude_config_path() { + # Honor CLAUDE_CONFIG_DIR if set (supports non-standard config directories) + if [[ -n "${CLAUDE_CONFIG_DIR:-}" ]]; then + local env_config="${CLAUDE_CONFIG_DIR}/.claude.json" + if [[ -f "$env_config" ]]; then + echo "$env_config" + return + fi + fi + local primary_config="$HOME/.claude/.claude.json" local fallback_config="$HOME/.claude.json" - + # Check primary location first if [[ -f "$primary_config" ]]; then # Verify it has valid oauthAccount structure @@ -113,7 +122,7 @@ get_claude_config_path() { return fi fi - + # Fallback to standard location echo "$fallback_config" } diff --git a/package.json b/package.json index a17fa95..d5edfd0 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,8 @@ "ccm.sh", "README.md", "LICENSE" - ] + ], + "scripts": { + "test": "bats tests/" + } } diff --git a/tests/get_claude_config_path.bats b/tests/get_claude_config_path.bats new file mode 100644 index 0000000..57d6a6e --- /dev/null +++ b/tests/get_claude_config_path.bats @@ -0,0 +1,83 @@ +#!/usr/bin/env bats +# Tests for get_claude_config_path, focused on CLAUDE_CONFIG_DIR support + +setup() { + # Set HOME before sourcing so readonly globals (BACKUP_DIR etc.) use TEST_HOME + TEST_HOME="$(mktemp -d)" + export HOME="$TEST_HOME" + unset CLAUDE_CONFIG_DIR + + # BASH_SOURCE guard in ccm.sh prevents main() from running when sourced + # shellcheck source=../ccm.sh + source "${BATS_TEST_DIRNAME}/../ccm.sh" +} + +teardown() { + rm -rf "$TEST_HOME" +} + +# --------------------------------------------------------------------------- +# CLAUDE_CONFIG_DIR behaviour +# --------------------------------------------------------------------------- + +@test "returns CLAUDE_CONFIG_DIR/.claude.json when env var is set and file exists" { + local custom_dir="$TEST_HOME/.claude-custom" + mkdir -p "$custom_dir" + printf '{"oauthAccount":{"emailAddress":"test@example.com"}}\n' \ + > "$custom_dir/.claude.json" + + export CLAUDE_CONFIG_DIR="$custom_dir" + run get_claude_config_path + + [ "$status" -eq 0 ] + [ "$output" = "$custom_dir/.claude.json" ] +} + +@test "falls through to primary when CLAUDE_CONFIG_DIR is set but .claude.json is absent" { + local custom_dir="$TEST_HOME/.claude-empty" + mkdir -p "$custom_dir" + # No .claude.json in custom_dir — should fall through + + mkdir -p "$TEST_HOME/.claude" + printf '{"oauthAccount":{"emailAddress":"primary@example.com"}}\n' \ + > "$TEST_HOME/.claude/.claude.json" + + export CLAUDE_CONFIG_DIR="$custom_dir" + run get_claude_config_path + + [ "$status" -eq 0 ] + [ "$output" = "$TEST_HOME/.claude/.claude.json" ] +} + +# --------------------------------------------------------------------------- +# Original behaviour (CLAUDE_CONFIG_DIR unset) +# --------------------------------------------------------------------------- + +@test "returns ~/.claude/.claude.json when CLAUDE_CONFIG_DIR is unset and primary exists" { + mkdir -p "$TEST_HOME/.claude" + printf '{"oauthAccount":{"emailAddress":"primary@example.com"}}\n' \ + > "$TEST_HOME/.claude/.claude.json" + + run get_claude_config_path + + [ "$status" -eq 0 ] + [ "$output" = "$TEST_HOME/.claude/.claude.json" ] +} + +@test "skips primary when it has no oauthAccount and returns ~/.claude.json fallback" { + mkdir -p "$TEST_HOME/.claude" + printf '{"someOtherField":true}\n' > "$TEST_HOME/.claude/.claude.json" + + run get_claude_config_path + + [ "$status" -eq 0 ] + [ "$output" = "$TEST_HOME/.claude.json" ] +} + +@test "returns ~/.claude.json fallback when CLAUDE_CONFIG_DIR is unset and primary is missing" { + # Neither ~/.claude/.claude.json nor CLAUDE_CONFIG_DIR — pure fallback path + run get_claude_config_path + + [ "$status" -eq 0 ] + [ "$output" = "$TEST_HOME/.claude.json" ] +}