lacs-project / lacs-project/sysknife
release-rehearsal.test.sh reads its pass condition out of a fixed /tmp path, so a run that could not write reports as a refusal
- Dominant language
- Rust
- Stars
- 12
- Forks
- 19
- Avg merge
- 18h 57m
- Merged PRs (30d)
- 116
Description
`tests/release/release-rehearsal.test.sh` proves that the rehearsal script
refuses a publishing mode. It writes the refusal to a fixed path under `/tmp`
and then greps that path for the pass condition:
```
$ git rev-parse --short=8 HEAD
61b3a878
$ sed -n '38,42p' tests/release/release-rehearsal.test.sh
if "$rehearsal" --publish >/tmp/sysknife-rehearsal-publish.out 2>&1; then
printf 'FAIL: rehearsal accepted a publishing mode\n' >&2
exit 1
fi
grep -Fq 'never publishes' /tmp/sysknife-rehearsal-publish.out
```
When the redirect fails, bash never runs the command, and the failure is
indistinguishable from the refusal this test is written to observe. The `if`
takes the false branch, which is the passing branch, and line 42 then reads a
file this run did not write.
## Why it matters
Measured, replaying the shape at `:38-42` against a file the process cannot
write:
```
$ f=$(mktemp /tmp/sk-demo-XXXX.out); printf 'rehearsal never publishes\n' > "$f"; chmod 444 "$f"; if echo 'this run really ran' > "$f" 2>&1; then echo "if-branch: TRUE (test would FAIL loudly)"; else echo "if-branch: FALSE (test treats this as the expected refusal)"; fi; grep -Fq 'never publishes' "$f" && echo "line 42 assertion: SATISFIED"; cat "$f"; rm -f "$f"
/bin/bash: line 1: /tmp/sk-demo-wtD2.out: Permission denied
if-branch: FALSE (test treats this as the expected refusal)
line 42 assertion: SATISFIED
rehearsal never publishes
```
`scripts/release_rehearsal.sh` was never invoked, and the contract test reported
that it correctly refuses to publish. That is a guard answering a question it
could not ask, which is the shape this repo files issues about in its own CI.
Two ways to arrive there, neither exotic. Another account on a shared runner or
a shared developer box owns `/tmp/sysknife-rehearsal-publish.out`, because the
name is fixed and predictable. Or `/tmp` is full or read-only, in which case
every gate that writes there fails in a way that reports as a pass here and as
an error elsewhere.
The file is also left behind. The test has no `trap`:
```
$ grep -c trap tests/release/release-rehearsal.test.sh
0
```
This is the only line in `tests/release/` that reads a fixed `/tmp` path back as
a pass condition:
```
$ grep -rn 'grep .*/tmp/' tests/release/
tests/release/release-rehearsal.test.sh:42:grep -Fq 'never publishes' /tmp/sysknife-rehearsal-publish.out
```
Six of the twenty-two scripts in that directory already use `mktemp`, so the
pattern to copy is in the same directory.
## Scope
- Give the test a private temporary directory (`mktemp -d`) and a
`trap 'rm -rf "$tmp"' EXIT`, and put the publish output inside it.
- Separate "the command ran and refused" from "the command never ran". Capture
the status explicitly rather than reading it off an `if` whose false branch
also covers a redirect failure. Assert that the output file is non-empty
before grepping it.
- Leave the assertion itself alone. `never publishes` is the right string and
the refusal is the right behaviour; only the plumbing that observes it moves.
Do not fix this by adding `|| exit 1` to the redirect. That closes the one arrival
path you thought of and leaves the shape intact for the next one.
## Tests first
Write the negative case before the fix, because the point of the change is that
a specific wrong state now fails.
1. Pin the fixed path: make the destination unwritable for the running user and
run the test as it stands. It passes. Paste that.
2. Apply the fix. The same setup now fails, and the message names the write
rather than the missing string.
3. Then prove the positive case still holds: an ordinary run passes, and the
temporary directory is gone afterwards.
Step 1 is the one that matters. A version of this change that only adds `mktemp`
and a trap is tidier and proves nothing, because the run that could not write
still reads as a refusal.
## Difficulty
`easy`. One file, about six lines, and the hard part is the test in step 1
rather than the fix. No Rust, no VM, no daemon, no credentials.
## Getting started
[CONTRIBUTING.md](https://github.com/lacs-project/sysknife/blob/main/CONTRIBUTING.md)
has the build and test commands. No CLA and no copyright waiver. The project is MIT.
Contributor guide
Research direction
Start with tests/release/release-rehearsal.test.sh at lines 38-42 and compare the mktemp pattern in the other tests/release scripts. Run the test with the fixed destination unwritable to establish the false pass, then use the commands in CONTRIBUTING.md for the normal run. Done means the test distinguishes a failed redirect from refusal, asserts non-empty output, passes normally, and removes its temporary directory.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bash
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100