diff --git a/home/.chezmoitemplates/apm/mcp-server.yml.tmpl b/home/.chezmoitemplates/apm/mcp-server.yml.tmpl index 860e641..5b9df41 100644 --- a/home/.chezmoitemplates/apm/mcp-server.yml.tmpl +++ b/home/.chezmoitemplates/apm/mcp-server.yml.tmpl @@ -1,8 +1,8 @@ -- name: {{ index . "name" }} +- name: "{{ index . "name" }}" registry: {{ index . "registry" }} transport: {{ index . "transport" }} {{- if hasKey . "url" }} - url: {{ index . "url" }} + url: "{{ index . "url" }}" {{- end }} {{- if hasKey . "headers" }} headers: @@ -11,7 +11,7 @@ {{- end }} {{- end }} {{- if hasKey . "command" }} - command: {{ index . "command" }} + command: "{{ index . "command" }}" {{- end }} {{- if hasKey . "args" }} args: diff --git a/home/dot_apm/apm.yml.tmpl b/home/dot_apm/apm.yml.tmpl index d178edc..1b31ece 100644 --- a/home/dot_apm/apm.yml.tmpl +++ b/home/dot_apm/apm.yml.tmpl @@ -20,11 +20,11 @@ dependencies: {{- with .skills }} skills: {{- range . }} - - {{ . }} + - "{{ . }}" {{- end }} {{- end }} {{- else }} - - {{ . }} + - "{{ . }}" {{- end }} {{ end }} {{ end -}} diff --git a/home/dot_config/git/config.tmpl b/home/dot_config/git/config.tmpl index 4967678..6e9a93f 100644 --- a/home/dot_config/git/config.tmpl +++ b/home/dot_config/git/config.tmpl @@ -1,6 +1,6 @@ [user] - name = {{ .git.name }} - email = {{ .git.email }} + name = "{{ .git.name }}" + email = "{{ .git.email }}" [core] editor = code --wait pager = delta diff --git a/home/dot_config/mise/config.toml.tmpl b/home/dot_config/mise/config.toml.tmpl index 88b2267..838a67f 100644 --- a/home/dot_config/mise/config.toml.tmpl +++ b/home/dot_config/mise/config.toml.tmpl @@ -9,4 +9,4 @@ [settings] idiomatic_version_file_enable_tools = ["node", "python", "pnpm"] -trusted_config_paths = ["/"] +trusted_config_paths = ["~"] diff --git a/tests/template/agent-instructions.bats b/tests/template/agent-instructions.bats index 5548539..cf6ad50 100644 --- a/tests/template/agent-instructions.bats +++ b/tests/template/agent-instructions.bats @@ -3,20 +3,20 @@ # @brief Template rendering tests for shared agent instruction markdown. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" CLAUDE_TEMPLATE="$DOTFILES_ROOT/home/dot_claude/CLAUDE.md.tmpl" OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" @test "agent instruction templates render shared markdown" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" <"$CLAUDE_TEMPLATE" + run render_chezmoi_template "$CLAUDE_TEMPLATE" assert_success assert_line --index 0 '# Claude Code Settings' assert_line --regexp '^### GitHub CLI$' assert_line 'Use `gh` CLI for all GitHub interactions. Never clone repositories to read code.' - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" <"$OPENCODE_TEMPLATE" + run render_chezmoi_template "$OPENCODE_TEMPLATE" assert_success assert_line --index 0 '# Claude Code Settings' @@ -36,7 +36,7 @@ OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" } @test "agent instruction templates render Graphify guidance" { - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" <"$CLAUDE_TEMPLATE" + run render_chezmoi_template "$CLAUDE_TEMPLATE" assert_success assert_line '## Graphify' @@ -44,7 +44,7 @@ OPENCODE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/opencode/AGENTS.md.tmpl" assert_line --partial 'graphify query' assert_line --partial 'GRAPH_REPORT.md' - run mise exec -- chezmoi execute-template --source "$SOURCE_DIR" <"$OPENCODE_TEMPLATE" + run render_chezmoi_template "$OPENCODE_TEMPLATE" assert_success assert_line '## Graphify' diff --git a/tests/template/apm-config.bats b/tests/template/apm-config.bats index 6318249..ee0cb8d 100644 --- a/tests/template/apm-config.bats +++ b/tests/template/apm-config.bats @@ -3,31 +3,23 @@ # @brief Template rendering tests for home/dot_apm/apm.yml.tmpl. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" APM_TEMPLATE="$DOTFILES_ROOT/home/dot_apm/apm.yml.tmpl" -PERSONAL_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' -WORK_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":true}' - -render_apm_template() { - local data="$1" - - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$data" <"$APM_TEMPLATE" -} @test "personal APM config renders only shared MCP servers" { - run render_apm_template "$PERSONAL_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" assert_success - assert_line ' - name: grep' + assert_line ' - name: "grep"' assert_line ' registry: false' - assert_line ' url: https://mcp.grep.app' + assert_line ' url: "https://mcp.grep.app"' refute_line --regexp 'name: figma' refute_line --regexp 'name: jira' } @test "personal APM config renders personal target" { - run render_apm_template "$PERSONAL_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" assert_success assert_line ' - claude' @@ -38,36 +30,36 @@ render_apm_template() { } @test "personal APM config renders shared and personal APM packages" { - run render_apm_template "$PERSONAL_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$PERSONAL_DATA" assert_success - assert_line --partial ' - obra/superpowers' - assert_line --partial ' - JuliusBrussee/caveman' - assert_line --partial ' - anthropics/claude-plugins-official/plugins/skill-creator' - assert_line --partial ' - schpet/linear-cli' - assert_line --partial ' - tavily-ai/skills' + assert_line --partial ' - "obra/superpowers' + assert_line --partial ' - "JuliusBrussee/caveman' + assert_line --partial ' - "anthropics/claude-plugins-official/plugins/skill-creator' + assert_line --partial ' - "schpet/linear-cli' + assert_line --partial ' - "tavily-ai/skills' assert_line --partial ' - git: JuliusBrussee/skills' assert_line ' skills:' - assert_line ' - grill-me' - assert_line ' - junior-to-senior' + assert_line ' - "grill-me"' + assert_line ' - "junior-to-senior"' refute_line --partial 'interface-kit' refute_line --partial 'loop-factory' refute_line --partial 'context-canary' } @test "work APM config renders Figma and Jira MCP servers" { - run render_apm_template "$WORK_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" assert_success - assert_line ' - name: grep' - assert_line ' - name: figma' - assert_line ' url: https://mcp.figma.com/mcp' - assert_line ' - name: jira' - assert_line ' url: https://mcp.atlassian.com/v1/mcp' + assert_line ' - name: "grep"' + assert_line ' - name: "figma"' + assert_line ' url: "https://mcp.figma.com/mcp"' + assert_line ' - name: "jira"' + assert_line ' url: "https://mcp.atlassian.com/v1/mcp"' } @test "work APM config renders work target" { - run render_apm_template "$WORK_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" assert_success assert_line ' - copilot' @@ -78,12 +70,12 @@ render_apm_template() { } @test "work APM config renders shared APM packages without personal packages" { - run render_apm_template "$WORK_DATA" + run render_chezmoi_template "$APM_TEMPLATE" "$WORK_DATA" assert_success - assert_line --partial ' - obra/superpowers' - assert_line --partial ' - JuliusBrussee/caveman' - assert_line --partial ' - anthropics/claude-plugins-official/plugins/skill-creator' + assert_line --partial ' - "obra/superpowers' + assert_line --partial ' - "JuliusBrussee/caveman' + assert_line --partial ' - "anthropics/claude-plugins-official/plugins/skill-creator' refute_line --partial ' - schpet/linear-cli' refute_line --partial ' - tavily-ai/skills' refute_line --partial 'JuliusBrussee/skills' diff --git a/tests/template/apm-lockfile.bats b/tests/template/apm-lockfile.bats index 6e349bf..6f07973 100644 --- a/tests/template/apm-lockfile.bats +++ b/tests/template/apm-lockfile.bats @@ -3,26 +3,22 @@ # @brief apm.lock.yaml.tmpl selects the correct per-context lockfile. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" TMPL="$DOTFILES_ROOT/home/dot_apm/apm.lock.yaml.tmpl" PERSONAL='{"personal":true,"work":false}' WORK='{"personal":false,"work":true}' -render() { - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$1" <"$TMPL" -} - @test "personal context renders the personal lockfile" { personal_first="$(grep -m1 resolved_commit "$DOTFILES_ROOT/home/.chezmoitemplates/apm/apm.lock.personal.yaml")" - run render "$PERSONAL" + run render_chezmoi_template "$TMPL" "$PERSONAL" assert_success assert_line --regexp '^lockfile_version' assert_line "$personal_first" } @test "work context renders the work lockfile" { - run render "$WORK" + run render_chezmoi_template "$TMPL" "$WORK" assert_success assert_line --regexp '^lockfile_version' } diff --git a/tests/template/chezmoi-helpers.bats b/tests/template/chezmoi-helpers.bats index 96759b9..9b4a8d6 100644 --- a/tests/template/chezmoi-helpers.bats +++ b/tests/template/chezmoi-helpers.bats @@ -3,42 +3,33 @@ # @brief Template rendering tests for chezmoi helper templates. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" ACTIVE_GROUPS_TMPL="$DOTFILES_ROOT/home/.chezmoitemplates/lib/chezmoi/active-groups.json.tmpl" ACTIVE_GROUP_VALUES_TMPL="$DOTFILES_ROOT/home/.chezmoitemplates/lib/chezmoi/active-group-values.json.tmpl" -DARWIN_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' -WORK_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":true}' SHARED_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":false}' BOTH_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":true}' APM_VALUES_BY_GROUP='{"shared":["graphify"],"personal":["claude","agent-skills"],"work":["copilot"]}' APM_VALUES_SPARSE='{"shared":["graphify"]}' -render_helper() { - local template="$1" - local data="$2" - - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$data" <"$template" -} - @test "active-groups returns shared and personal for personal context" { - run render_helper "$ACTIVE_GROUPS_TMPL" "$DARWIN_DATA" + run render_chezmoi_template "$ACTIVE_GROUPS_TMPL" "$DARWIN_DATA" assert_success assert_output '["shared","personal"]' } @test "active-groups returns shared and work for work context" { - run render_helper "$ACTIVE_GROUPS_TMPL" "$WORK_DATA" + run render_chezmoi_template "$ACTIVE_GROUPS_TMPL" "$WORK_DATA" assert_success assert_output '["shared","work"]' } @test "active-groups returns only shared when neither personal nor work" { - run render_helper "$ACTIVE_GROUPS_TMPL" "$SHARED_DATA" + run render_chezmoi_template "$ACTIVE_GROUPS_TMPL" "$SHARED_DATA" assert_success assert_output '["shared"]' @@ -48,7 +39,7 @@ render_helper() { local data data=$(printf '{"ctx":{"personal":true,"work":false},"valuesByGroup":%s}' "$APM_VALUES_BY_GROUP") - run render_helper "$ACTIVE_GROUP_VALUES_TMPL" "$data" + run render_chezmoi_template "$ACTIVE_GROUP_VALUES_TMPL" "$data" assert_success assert_output '["graphify","claude","agent-skills"]' @@ -58,7 +49,7 @@ render_helper() { local data data=$(printf '{"ctx":{"personal":false,"work":true},"valuesByGroup":%s}' "$APM_VALUES_BY_GROUP") - run render_helper "$ACTIVE_GROUP_VALUES_TMPL" "$data" + run render_chezmoi_template "$ACTIVE_GROUP_VALUES_TMPL" "$data" assert_success assert_output '["graphify","copilot"]' @@ -68,14 +59,14 @@ render_helper() { local data data=$(printf '{"ctx":{"personal":false,"work":false},"valuesByGroup":%s}' "$APM_VALUES_BY_GROUP") - run render_helper "$ACTIVE_GROUP_VALUES_TMPL" "$data" + run render_chezmoi_template "$ACTIVE_GROUP_VALUES_TMPL" "$data" assert_success assert_output '["graphify"]' } @test "active-groups returns shared, personal, and work when both are true" { - run render_helper "$ACTIVE_GROUPS_TMPL" "$BOTH_DATA" + run render_chezmoi_template "$ACTIVE_GROUPS_TMPL" "$BOTH_DATA" assert_success assert_output '["shared","personal","work"]' @@ -85,7 +76,7 @@ render_helper() { local data data=$(printf '{"ctx":{"personal":true,"work":false},"valuesByGroup":%s}' "$APM_VALUES_SPARSE") - run render_helper "$ACTIVE_GROUP_VALUES_TMPL" "$data" + run render_chezmoi_template "$ACTIVE_GROUP_VALUES_TMPL" "$data" assert_success assert_output '["graphify"]' diff --git a/tests/template/darwin-install-scripts.bats b/tests/template/darwin-install-scripts.bats index c1e2859..987e67b 100644 --- a/tests/template/darwin-install-scripts.bats +++ b/tests/template/darwin-install-scripts.bats @@ -3,29 +3,16 @@ # @brief Template rendering tests for Darwin chezmoi install scripts. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" -DARWIN_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' -WORK_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":true}' EMPTY_APM_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false,"apm":{"targets":{"shared":[],"personal":[],"work":[]}}}' PACKAGE_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_02_install-packages.sh.tmpl" UV_TOOLS_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_03_install-uv-tools.sh.tmpl" GRAPHIFY_TEMPLATE="$DOTFILES_ROOT/home/.chezmoiscripts/darwin/run_onchange_08_install-graphify-skills.sh.tmpl" -render_template() { - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$DARWIN_DATA" <"$1" -} - -render_template_with_data() { - local template="$1" - local data="$2" - - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$data" <"$template" -} - @test "darwin install script templates render with bash shebang" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do - run render_template "$template" + run render_chezmoi_template "$template" "$DARWIN_DATA" assert_success [ "${lines[0]}" = "#!/usr/bin/env bash" ] done @@ -45,13 +32,13 @@ render_template_with_data() { @test "rendered darwin install scripts are syntactically valid bash" { for template in "$DOTFILES_ROOT"/home/.chezmoiscripts/darwin/*.tmpl; do - rendered="$(render_template "$template")" + rendered="$(render_chezmoi_template "$template" "$DARWIN_DATA")" printf '%s\n' "$rendered" | bash -n done } @test "personal package template keeps personal tools and omits work apps" { - run render_template_with_data "$PACKAGE_TEMPLATE" "$DARWIN_DATA" + run render_chezmoi_template "$PACKAGE_TEMPLATE" "$DARWIN_DATA" assert_success assert_line 'brew "mas"' @@ -65,7 +52,7 @@ render_template_with_data() { } @test "work package template renders approved work apps" { - run render_template_with_data "$PACKAGE_TEMPLATE" "$WORK_DATA" + run render_chezmoi_template "$PACKAGE_TEMPLATE" "$WORK_DATA" assert_success assert_line 'brew "mas"' @@ -83,7 +70,7 @@ render_template_with_data() { } @test "personal uv tools template renders Graphify and Tavily CLI tools" { - run render_template_with_data "$UV_TOOLS_TEMPLATE" "$DARWIN_DATA" + run render_chezmoi_template "$UV_TOOLS_TEMPLATE" "$DARWIN_DATA" assert_success assert_line 'UV_TOOLS=(' @@ -93,7 +80,7 @@ render_template_with_data() { } @test "work uv tools template has no personal uv tools" { - run render_template_with_data "$UV_TOOLS_TEMPLATE" "$WORK_DATA" + run render_chezmoi_template "$UV_TOOLS_TEMPLATE" "$WORK_DATA" assert_success refute_line --partial '"graphifyy"' @@ -103,7 +90,7 @@ render_template_with_data() { } @test "personal Graphify template renders agent and Claude platforms" { - run render_template_with_data "$GRAPHIFY_TEMPLATE" "$DARWIN_DATA" + run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$DARWIN_DATA" assert_success assert_line 'GRAPHIFY_PLATFORMS=(' @@ -113,7 +100,7 @@ render_template_with_data() { } @test "work Graphify template renders no personal platforms" { - run render_template_with_data "$GRAPHIFY_TEMPLATE" "$WORK_DATA" + run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$WORK_DATA" assert_success refute_line --partial '"agents"' @@ -123,7 +110,7 @@ render_template_with_data() { } @test "empty APM target groups render no Graphify commands" { - run render_template_with_data "$GRAPHIFY_TEMPLATE" "$EMPTY_APM_DATA" + run render_chezmoi_template "$GRAPHIFY_TEMPLATE" "$EMPTY_APM_DATA" assert_success refute_line 'GRAPHIFY_PLATFORMS=(' diff --git a/tests/template/mise-config.bats b/tests/template/mise-config.bats index 3378710..9c810cc 100644 --- a/tests/template/mise-config.bats +++ b/tests/template/mise-config.bats @@ -3,20 +3,12 @@ # @brief Template rendering tests for home/dot_config/mise/config.toml.tmpl. load '../test_helpers/load.bash' +load '../test_helpers/templates.bash' -SOURCE_DIR="$DOTFILES_ROOT/home" MISE_TEMPLATE="$DOTFILES_ROOT/home/dot_config/mise/config.toml.tmpl" -PERSONAL_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' -WORK_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":true}' - -render_mise_template() { - local data="$1" - - mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$data" <"$MISE_TEMPLATE" -} @test "mise config installs APM for personal hosts" { - run render_mise_template "$PERSONAL_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$PERSONAL_DATA" assert_success assert_line '"github:microsoft/apm" = "latest"' @@ -31,35 +23,35 @@ render_mise_template() { } @test "mise config installs Linear CLI for personal hosts" { - run render_mise_template "$PERSONAL_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$PERSONAL_DATA" assert_success assert_line '"npm:@schpet/linear-cli" = "latest"' } @test "mise config installs uv for personal hosts" { - run render_mise_template "$PERSONAL_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$PERSONAL_DATA" assert_success assert_line '"uv" = "latest"' } @test "mise config installs APM for work hosts" { - run render_mise_template "$WORK_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$WORK_DATA" assert_success assert_line '"github:microsoft/apm" = "latest"' } @test "mise config does not install Linear CLI for work hosts" { - run render_mise_template "$WORK_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$WORK_DATA" assert_success refute_line '"npm:@schpet/linear-cli" = "latest"' } @test "mise config does not install uv for work hosts" { - run render_mise_template "$WORK_DATA" + run render_chezmoi_template "$MISE_TEMPLATE" "$WORK_DATA" assert_success refute_line '"uv" = "latest"' diff --git a/tests/test_helpers/templates.bash b/tests/test_helpers/templates.bash new file mode 100644 index 0000000..5280284 --- /dev/null +++ b/tests/test_helpers/templates.bash @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# @file templates.bash +# @brief Shared chezmoi template-rendering helpers for tests/template/*.bats. +# Provides the source dir, the common per-context override-data payloads, +# and a single render function so individual test files stop redefining +# their own copies. +# +# Load AFTER load.bash so DOTFILES_ROOT is already exported: +# load '../test_helpers/load.bash' +# load '../test_helpers/templates.bash' + +# Chezmoi source directory shared by every template test. +SOURCE_DIR="$DOTFILES_ROOT/home" +export SOURCE_DIR + +# Per-context override-data payloads passed to `chezmoi execute-template`. +PERSONAL_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' +WORK_DATA='{"chezmoi":{"os":"darwin"},"personal":false,"work":true}' +DARWIN_DATA='{"chezmoi":{"os":"darwin"},"personal":true,"work":false}' +export PERSONAL_DATA WORK_DATA DARWIN_DATA + +# @description Render a chezmoi template from SOURCE_DIR. +# @arg $1 string Absolute path to the template file. +# @arg $2 string Optional JSON override-data payload. Omit to render with defaults. +render_chezmoi_template() { + local template="$1" data="${2:-}" + + if [ -n "$data" ]; then + mise exec -- chezmoi execute-template --source "$SOURCE_DIR" --override-data "$data" <"$template" + else + mise exec -- chezmoi execute-template --source "$SOURCE_DIR" <"$template" + fi +} diff --git a/tests/unit/scripts/refresh-apm-locks.bats b/tests/unit/scripts/refresh-apm-locks.bats new file mode 100644 index 0000000..3e345b5 --- /dev/null +++ b/tests/unit/scripts/refresh-apm-locks.bats @@ -0,0 +1,99 @@ +#!/usr/bin/env bats +# @file tests/unit/scripts/refresh-apm-locks.bats +# @brief Behavior tests for scripts/refresh-apm-locks.sh lockfile refresh flow. +# Stubs mise, chezmoi, apm, and prettier on PATH and runs an isolated +# copy of the script so the committed lockfiles are never touched. + +load '../../test_helpers/load.bash' + +setup() { + export CALL_FILE="$BATS_TEST_TMPDIR/calls" + BIN="$BATS_TEST_TMPDIR/bin" + mkdir -p "$BIN" + + # mise resolves pinned tool paths to our stubs. + cat >"$BIN/mise" <>"$CALL_FILE" +if [ "\$1" = which ]; then echo "$BIN/\$2"; exit 0; fi +exit 0 +STUB + + # chezmoi apply only needs to create the target ~/.apm directory. + cat >"$BIN/chezmoi" <>"$CALL_FILE" +mkdir -p "\${@: -1}" +exit 0 +STUB + + # apm lock --update --global writes the user-scope lockfile under \$HOME/.apm. + cat >"$BIN/apm" <>"$CALL_FILE" +mkdir -p "\$HOME/.apm" +printf 'lockfile_version: 1\n# stub lock\n' >"\$HOME/.apm/apm.lock.yaml" +exit 0 +STUB + + # prettier records the files it was asked to format. + cat >"$BIN/prettier" <>"$CALL_FILE" +exit 0 +STUB + + chmod +x "$BIN"/* + export PATH="$BIN:$PATH" + + # Isolated repo copy so cp writes lockfiles into the temp tree, never the real repo. + REPO="$BATS_TEST_TMPDIR/repo" + mkdir -p "$REPO/scripts" "$REPO/home/.chezmoitemplates/apm" "$REPO/.github/actions/write-chezmoi-config" + cp "$DOTFILES_ROOT/scripts/refresh-apm-locks.sh" "$REPO/scripts/refresh-apm-locks.sh" + cp "$DOTFILES_ROOT/scripts/apm-lib.sh" "$REPO/scripts/apm-lib.sh" + cat >"$REPO/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" <<'STUB' +#!/usr/bin/env bash +exit 0 +STUB + chmod +x "$REPO/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" + + LOCK_DIR="$REPO/home/.chezmoitemplates/apm" +} + +@test "personal context writes the personal lockfile and formats it" { + run bash "$REPO/scripts/refresh-apm-locks.sh" personal + assert_success + assert_line --partial "wrote home/.chezmoitemplates/apm/apm.lock.personal.yaml" + + assert_file_exists "$LOCK_DIR/apm.lock.personal.yaml" + assert_file_contains "$LOCK_DIR/apm.lock.personal.yaml" 'lockfile_version: 1' + + run cat "$CALL_FILE" + assert_line --partial "prettier --write $LOCK_DIR/apm.lock.personal.yaml" +} + +@test "work context writes the work lockfile only" { + run bash "$REPO/scripts/refresh-apm-locks.sh" work + assert_success + + assert_file_exists "$LOCK_DIR/apm.lock.work.yaml" + assert_file_not_exists "$LOCK_DIR/apm.lock.personal.yaml" +} + +@test "no arguments refreshes both contexts" { + run bash "$REPO/scripts/refresh-apm-locks.sh" + assert_success + + assert_file_exists "$LOCK_DIR/apm.lock.personal.yaml" + assert_file_exists "$LOCK_DIR/apm.lock.work.yaml" + + run cat "$CALL_FILE" + assert_line --partial "prettier --write $LOCK_DIR/apm.lock.personal.yaml $LOCK_DIR/apm.lock.work.yaml" +} + +@test "rejects an unknown context before writing anything" { + run bash "$REPO/scripts/refresh-apm-locks.sh" bogus + assert_failure + assert_line --partial "Unsupported context: bogus" + assert_file_not_exists "$LOCK_DIR/apm.lock.bogus.yaml" +} diff --git a/tests/unit/statusline-git.bats b/tests/unit/statusline-git.bats new file mode 100644 index 0000000..75c5173 --- /dev/null +++ b/tests/unit/statusline-git.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats +# @file statusline-git.bats +# @brief Exercises the git branch/staged/modified segment and the rate-limit bar +# in home/dot_claude/statusline-command.sh against a REAL repo. The shared +# statusline fixtures use a nonexistent current_dir, so `cd "$current_dir"` +# fails there and this code path is otherwise never reached. + +load '../test_helpers/load.bash' + +STATUSLINE="$DOTFILES_ROOT/home/dot_claude/statusline-command.sh" + +setup() { + # Real repo on the branch "feature-x" with one staged and one modified file. + REPO="$BATS_TEST_TMPDIR/myrepo" + mkdir -p "$REPO" + git -C "$REPO" init -q -b feature-x + git -C "$REPO" config user.email "test@example.com" + git -C "$REPO" config user.name "Test" + printf 'initial\n' >"$REPO/tracked.txt" + git -C "$REPO" add tracked.txt + git -C "$REPO" commit -q -m init + printf 'changed\n' >>"$REPO/tracked.txt" # tracked.txt modified, unstaged -> ~1 + printf 'new\n' >"$REPO/staged.txt" + git -C "$REPO" add staged.txt # staged.txt added to index -> +1 + + # Payload mirroring Claude Code's runtime contract, pointed at the real repo. + INPUT="$BATS_TEST_TMPDIR/input.json" + printf '{"model":{"display_name":"Sonnet 4.6"},"context_window":{"used_percentage":17},"workspace":{"current_dir":"%s"},"rate_limits":{"five_hour":{"used_percentage":50,"resets_at":1747339200}}}' "$REPO" >"$INPUT" +} + +@test "renders branch with staged and modified counts" { + run sh "$STATUSLINE" <"$INPUT" + assert_success + assert_line --partial 'myrepo' + assert_line --partial 'feature-x' + assert_line --partial '+1' + assert_line --partial '~1' +} + +@test "renders the rate-limit bar for the given percentage" { + run sh "$STATUSLINE" <"$INPUT" + assert_success + assert_line --partial '5h' + assert_line --partial '█████░░░░░' + assert_line --partial '50%' +} diff --git a/tests/unit/write-chezmoi-config.bats b/tests/unit/write-chezmoi-config.bats index ea2a21e..063efd3 100644 --- a/tests/unit/write-chezmoi-config.bats +++ b/tests/unit/write-chezmoi-config.bats @@ -10,12 +10,8 @@ load '../test_helpers/load.bash' SCRIPT="$DOTFILES_ROOT/.github/actions/write-chezmoi-config/write-chezmoi-config.sh" setup() { - TMPHOME="$(mktemp -d)" - export HOME="$TMPHOME" -} - -teardown() { - rm -rf "$TMPHOME" + export HOME="$BATS_TEST_TMPDIR/home" + mkdir -p "$HOME" } @test "personal context: exits success and writes chezmoi.yaml" {