| name | test-script |
| description | Use when writing or modifying .txt test scripts in testdata/script/ for git-spice - covers txtar format, end-to-end testing, ShamHub forge simulation, interactive prompts, and golden file comparisons for branch operations and stack workflows. |
Test Scripts
Overview
Write test scripts for git-spice commands as .txt files in txtar format.
Test scripts verify end-to-end behavior and are stored in testdata/script/.
Quick Reference
| Task | Command/Pattern |
|---|---|
| Run specific test | mise run test:script --run $name |
| Update golden files | mise run test:script --run $name --update |
| Create branch | gs branch create <name> -m <msg> |
| Make commit | gs commit create -m <msg> |
| Compare stdout | cmp stdout $WORK/golden/file.txt |
| Partial match | stdout 'expected substring' |
| Check file exists | exists filename |
| Compare with env vars | cmpenv stdout $WORK/golden/file.txt |
| Test interactive | env ROBOT_INPUT=$WORK/robot.golden ROBOT_OUTPUT=$WORK/robot.actual |
Common Mistakes
Avoid these errors when writing test scripts:
❌ Using shell operations: Test scripts don't support
>,>>,2>,|, or command substitution. Usecmp stdout,cmp stderr,stdout, andstderrinstead.❌ Using
catorls: Useexists filenameto check existence,cmp have_file want_fileto compare contents.❌ Forgetting
asandat: Always freeze time and author for deterministic Git hashes.❌ Using
git checkout -b: Usegs branch createinstead (unless testing non-git-spice scenarios).❌ Empty golden files with
(empty): Just leave content blank after-- path --.
Writing a test script
Test scripts are plain text files with a series of commands. Most test scripts follow this general structure:
# Brief description of what the test does.
# Wrap comments at 80 characters.
as 'Test <test@example.com>'
at '2025-10-18T21:28:29Z'
# Setup
cd repo
git init
git commit --allow-empty -m 'Initial commit'
gs repo init
# (Optional) ShamHub setup for forge interactions
shamhub init
shamhub register alice
shamhub new origin alice/example.git
git push origin main
# Login as user
env SHAMHUB_USERNAME=alice
gs auth login
# ...
some command
cmp stdout $WORK/golden/output.txt
-- repo/file.txt --
file inside repo
-- repo/path/to/another-file.txt --
another file inside repo
-- extra/supporting-file.txt --
supporting file outside repo.
may be copied into repo during test.
-- golden/output.txt --
expected output for comparison
Notes:
Deterministic Git hashes
To get Git SHAs that are consistent across test runs, always freeze time and author at the start of the script using the
atandascommands.as 'Test <test@example.com>' at '2025-10-18T21:28:29Z'The
atshould always use the current time at the moment of writing the test script.Repository initialization
Initialize the Git repository and git-spice within it:
cd repo git init git commit --allow-empty -m 'Initial commit' gs repo initShamHub setup
For tests involving forge interactions, we use a simulation server called ShamHub. To set it up, use the
shamhubcommands:shamhub init shamhub register alice shamhub new origin alice/example.git git push origin main env SHAMHUB_USERNAME=alice gs auth login
Following setup, use git-spice via gs commands to perform actions.
Test script style
Naming
Follow the convention:
<command>_<scenario>.txtWhere<command>is the git-spice command being tested insnake_case, and<scenario>describes the specific test case. Examples:repo_sync_conflict.txtstack_submit_with_labels.txt
If the test script is a regression test for a specific issue, include the issue number in the name:
issue123_stack_rebase_does_not_do_a_thing.txt
Comments
Use comments (
#) to explain the purpose of each step. Wrap comments at 80 characters for readability.If the test script is a regression test for a specific issue, include a comment with the issue link at the top.
Common tasks
Creating branches
gs branch create <branch-name> -m <commit-msg>
This will create a new branch and commit all staged changes to it with the given commit message.
See gs branch create --help for more options.
Never use manual git checkout -b
unless specifically testing a non-git-spice branch creation scenario.
Making commits
gs commit create -m <commit-msg>
This will commit all staged changes to the current branch with the given commit message.
See gs commit create --help for more options.
Supporting files
Files needed by test scripts are defined
at the end of the script using -- path/to/file -- syntax.
For each such section,
everything after the -- path/to/file -- line
until the next -- path/to/another/file -- line, or the end of the file,
is treated as the contents of that file.
-- repo/feature.txt --
Initial content of feature.txt
-- extra/feature-new.txt --
Updated content of feature.txt
-- golden/output.txt --
Expected output of the command
Using golden files
If the expected output of any command is stored in a supporting file,
put it inside a golden/ subdirectory in the test script,
and refer to it using $WORK/golden/filename.
Tip:
For outputs that we do not control (e.g. git commit hashes),
save time by putting placeholder values in the golden files,
then run the test script with the --update flag to auto-update them.
(This only works for cmp comparisons, not cmpenv or cmpenvJSON.)
Verifying git state
View git graph:
git graph --branches cmp stdout $WORK/golden/graph.txtView git status:
git status --porcelain cmp stdout $WORK/golden/status.txtVerify git-spice branch relationships:
gs ls cmp stderr $WORK/golden/ls.txtVerify git-spice branch and commit details:
gs ll cmp stderr $WORK/golden/ll.txt
Verifying command output
Matching entire stdout
run some command cmp stdout $WORK/golden/output.txtMatching stdout partially
run some command stdout 'expected output substring'Matching entire stderr
run some command cmp stderr $WORK/golden/error.txtMatching stderr partially
run some command stderr 'expected error message substring'Comparing with environment variable substitutions
run some command cmpenv stdout $WORK/golden/output.txt cmpenv stderr $WORK/golden/error.txt(
$FOOplaceholders in the golden file will be replaced with their actual values from the test environment.)Comparing JSON output with environment variable substitutions
run some command --json cmpenvJSON stdout $WORK/golden/output.json(
$FOOplaceholders in the golden JSON file will be replaced with their actual values from the test environment.)
Testing Interactive Prompts
For commands with interactive prompts,
Declare a golden fixture file with pre-defined answers for each expected prompt. These are JSON-encoded answers separated by
===lines, with>comments that contain the rendered prompt text.-- robot.golden -- > Enter your name: "Alice" === > Choose an option: > 1) Option A > 2) Option B 2Before invoking the interactive command, set the
ROBOT_INPUTenvironment variable to point to the fixture file, andROBOT_OUTPUTto capture rendered prompts.env ROBOT_INPUT=$WORK/robot.golden ROBOT_OUTPUT=$WORK/robot.actualRun the command that prompts for input.
gs some interactive commandAfter the command completes, compare the captured output file with the golden fixture file.
cmp $WORK/robot.actual $WORK/robot.golden(Use
cmpenvif environment variable substitutions are needed.)
Testing opening browser URLs
For commands that open browser URLs,
set BROWSER_RECORDER_FILE to a file path
that will capture the URLs that were requested to be opened.
env BROWSER_RECORDER_FILE=$WORK/browser.log
gs branch submit --web
Verify the recorded URLs against the golden copy:
cmpenv $WORK/browser.log $WORK/golden/browser.log
Running test scripts
Run a specific test script:
mise run test:script --run $nameWhere
$nameis the script name without.txtextension. For example, to runtestdata/script/repo_sync_conflict.txt:mise run test:script --run repo_sync_conflictAuto-update golden files:
If a test script fails because the expected output has changed intentionally, use the
--updateflag to update golden files:mise run test:script --run $name --updateNote: This only works for
cmpcomparisons.cmpenvandcmpenvJSONdo not support--updatemode.
ShamHub
ShamHub is a simulated forge server (e.g., GitHub/GitLab) used for testing git-spice's forge interactions.
Outside of using git-spice (gs) commands,
ShamHub interactions are available through the shamhub command
defined at internal/forge/shamhub/cli.go.
Common shamhub commands:
shamhub init: Initialize ShamHub server, set environment variables.shamhub register <username>: Register users on ShamHub.shamhub new <remote> <owner/repo>: Create a new ShamHub repository and add as remote in the current Git repository.shamhub fork <owner/repo> <fork-owner>: Create a fork of a ShamHub repository under a different user.shamhub clone <owner/repo> <dir>: Clone an existing ShamHub repository into the given directory.shamhub merge [-prune] [-squash] <owner/repo> <pr>: Merge a Shamhub Change Request (Pull Request/Merge Request). Use-pruneto delete the source branch after merging. Use-squashto squash commits when merging.shamhub reject <owner/repo> <pr>: Reject a ShamHub Change Request and close it.shamhub dump changes/comments: Dump ShamHub state to stdout for verification. (Usecmp,cmpenv, andcmpenvJSONto verify output.)
Reference
For detailed command reference,
see testdata/script/README.md in the repository.