test(smoke): end-to-end smoke driving the real CLI against the containerized fake

This commit is contained in:
2026-08-29 16:28:32 -05:00
parent 9b08589092
commit 5feac3cec4
4 changed files with 197 additions and 6 deletions
+5
View File
@@ -0,0 +1,5 @@
# mgit connection env file (copy to ~/.config/mgit/env and chmod 600).
# Only GITEA_URL and GITEA_KEY are used; both are required. The file must
# be 0600 or stricter — mgit refuses looser files before reading them.
GITEA_URL=https://git.knownelement.com
GITEA_KEY=your-gitea-api-token
+15 -6
View File
@@ -133,20 +133,29 @@ type FailSpec struct {
// New starts a fake on a random port authenticating as login.
func New(token, login string) *Server {
s := newServer(token, login)
mux := http.NewServeMux()
mux.HandleFunc("/", s.handler)
s.srv = httptest.NewServer(mux)
s.srv = httptest.NewServer(s.Handler())
s.URL = s.srv.URL
return s
}
// NewState builds an unstarted fake: seed it with the Add* methods, then
// serve Handler() however the process needs (the smoke run does).
func NewState(token, login string) *Server {
return newServer(token, login)
}
// Handler returns the fake's HTTP handler.
func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", s.handler)
return mux
}
// ListenAndServe runs the fake on a fixed address (the smoke run boots it
// in a container). An empty token disables auth checking.
func ListenAndServe(addr, token string) error {
s := newServer(token, "ukrrs")
mux := http.NewServeMux()
mux.HandleFunc("/", s.handler)
return http.ListenAndServe(addr, mux)
return http.ListenAndServe(addr, s.Handler())
}
func newServer(token, login string) *Server {
+26
View File
@@ -0,0 +1,26 @@
// Command fakegitea boots the in-memory fake Gitea on a fixed address
// for the smoke run (see smoke/smoke.sh), seeded with the MOPAC repo the
// script drives. The real forge is never contacted.
package main
import (
"flag"
"log"
"net/http"
"os"
"git.knownelement.com/ukrrs/mopac-gitea-go/internal/fakegitea"
)
func main() {
addr := flag.String("addr", ":8602", "listen address")
flag.Parse()
key := os.Getenv("FAKE_KEY")
if key == "" {
key = "smoke-gitea-token-0123456789"
}
s := fakegitea.NewState(key, "ukrrs")
s.AddRepo("ukrrs", "MOPAC", "main", "main", "feat/quota")
log.Printf("fakegitea listening on %s", *addr)
log.Fatal(http.ListenAndServe(*addr, s.Handler()))
}
Executable
+151
View File
@@ -0,0 +1,151 @@
#!/bin/sh
# End-to-end smoke for mgit: builds the CLI in the Docker builder, boots
# the FAKE Gitea in a container on 127.0.0.1:8602, drives the real
# binary from the host through a 0600 env file, and asserts the full
# command surface (repo create/list/show, branch list, commit status,
# PR create/list/show/merge, -o json, exit codes) plus token redaction.
# No real forge is ever contacted. Only the exact container ID spawned
# here is removed.
set -e
cd "$(dirname "$0")/.."
IMAGE="golang@sha256:e8c859f5632dcfde7b32d2012b4351728f6437930887c2f6a91ea242459e5514"
PORT=8602
KEY="smoke-gitea-token-0123456789"
CID=""
cleanup() {
if [ -n "$CID" ]; then
docker rm -f "$CID" >/dev/null 2>&1 || true
fi
rm -rf .smoke
}
trap cleanup EXIT INT TERM
mkdir -p .smoke
umask 077
echo "--- build CLI + fake (docker builder)"
docker run --rm -v "$PWD:/h" -w /h \
-u "$(id -u):$(id -g)" -e HOME=/tmp -e GOFLAGS=-buildvcs=false \
"$IMAGE" sh -c 'go build -o bin/mgit ./cmd/mgit && go build -o bin/fakegitea ./smoke/fakegitea'
echo "--- boot fake Gitea (container, port $PORT)"
CID=$(docker run -d --rm \
-v "$PWD:/h" -w /h \
-u "$(id -u):$(id -g)" -e HOME=/tmp \
-p 127.0.0.1:$PORT:8602 \
-e FAKE_KEY="$KEY" \
"$IMAGE" /h/bin/fakegitea -addr :8602)
# wait for the fake to answer (any HTTP response, even 401, proves it is up)
i=0
until [ -n "$CID" ] && [ "$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null)" = "true" ] && \
python3 -c "
import urllib.request, urllib.error
req = urllib.request.Request('http://127.0.0.1:$PORT/api/v1/user/repos',
headers={'Authorization': 'token probe'})
try:
urllib.request.urlopen(req, timeout=2)
except urllib.error.HTTPError:
raise SystemExit(0)
except Exception:
raise SystemExit(1)
raise SystemExit(0)
"; do
i=$((i+1))
if [ "$i" -ge 90 ]; then
echo "smoke: fake Gitea did not come up; logs:" >&2
docker logs "$CID" >&2 || true
exit 1
fi
sleep 1
done
printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=%s\n' "$PORT" "$KEY" > .smoke/env
chmod 600 .smoke/env
mgit() { ./bin/mgit --config .smoke/env "$@"; }
echo "--- repo create + list + show"
mgit repo create -n vertical-x --desc "one vertical" --private --auto-init > .smoke/repo.out
grep -q "created repo ukrrs/vertical-x (private)" .smoke/repo.out
mgit repo list > .smoke/repos.out
grep -q "ukrrs/MOPAC" .smoke/repos.out
grep -q "ukrrs/vertical-x" .smoke/repos.out
mgit repo list -o json | python3 -c 'import json,sys
repos = json.load(sys.stdin)
assert any(r["full_name"] == "ukrrs/vertical-x" for r in repos), repos'
mgit repo show ukrrs/MOPAC > .smoke/reposhow.out
grep -q "default branch: main" .smoke/reposhow.out
echo "--- branch list"
mgit branch list ukrrs/MOPAC > .smoke/branches.out
grep -q "main" .smoke/branches.out
grep -q "feat/quota" .smoke/branches.out
echo "--- commit status (report + combined)"
mgit status create ukrrs/MOPAC feat/quota --state pending --context ci/lint \
--description "lint queued" > .smoke/st1.out
grep -q "pending" .smoke/st1.out
mgit status create ukrrs/MOPAC feat/quota --state success --context ci/lint \
--description "lint clean" > .smoke/st2.out
grep -q "success" .smoke/st2.out
mgit status list ukrrs/MOPAC feat/quota > .smoke/statuses.out
grep -q "combined success (1 checks)" .smoke/statuses.out
grep -q "ci/lint" .smoke/statuses.out
echo "--- pr create (body from file) + list + show + merge"
cat > .smoke/body.md <<'EOF'
## Scope
- smoke body
EOF
mgit pr create ukrrs/MOPAC --title "Smoke: quota accounting" \
--body .smoke/body.md --base main --head feat/quota -o json > .smoke/pr.out
grep -q '"number":' .smoke/pr.out
mgit pr list ukrrs/MOPAC > .smoke/prs.out
grep -q "Smoke: quota accounting" .smoke/prs.out
grep -q "main..feat/quota" .smoke/prs.out
mgit pr show ukrrs/MOPAC 1 > .smoke/prshow.out
grep -q "smoke body" .smoke/prshow.out
mgit pr merge ukrrs/MOPAC 1 --do squash > .smoke/merge.out
grep -q "merged pull request #1 (squash)" .smoke/merge.out
mgit pr list ukrrs/MOPAC --state closed > .smoke/prsclosed.out
grep -q "Smoke: quota accounting" .smoke/prsclosed.out
mgit pr list ukrrs/MOPAC > .smoke/prsopen.out
if grep -q "Smoke" .smoke/prsopen.out; then
echo "smoke: merged PR still listed as open" >&2; exit 1
fi
echo "--- failure path: missing repo (exit 2, one-line stderr with http code)"
if mgit repo show ukrrs/absent > .smoke/missing.out 2> .smoke/missing.err; then
echo "smoke: missing repo should fail" >&2; exit 1
fi
grep -q "http 404" .smoke/missing.err
[ "$(wc -l < .smoke/missing.err)" -eq 1 ] || { echo "smoke: stderr not one line" >&2; exit 1; }
[ ! -s .smoke/missing.out ] || { echo "smoke: stdout not empty on failure" >&2; exit 1; }
echo "--- failure path: wrong token (exit 2, no token material in stderr)"
printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=wrong-token-abcdef\n' "$PORT" > .smoke/bad.env
chmod 600 .smoke/bad.env
if ./bin/mgit --config .smoke/bad.env repo list 2> .smoke/bad.err; then
echo "smoke: wrong token should fail" >&2; exit 1
fi
grep -q "http 401" .smoke/bad.err
echo "--- failure path: loose env file (exit 1)"
printf 'GITEA_URL=http://127.0.0.1:%s\nGITEA_KEY=%s\n' "$PORT" "$KEY" > .smoke/loose.env
chmod 644 .smoke/loose.env
if ./bin/mgit --config .smoke/loose.env repo list 2> .smoke/loose.err; then
echo "smoke: loose env file should be refused" >&2; exit 1
fi
grep -q "insecure mode" .smoke/loose.err
echo "--- redaction: no token material in any captured output"
for f in .smoke/*.out .smoke/*.err; do
grep -qF "$KEY" "$f" && { echo "smoke: token leaked into $f" >&2; exit 1; }
done
grep -qF "wrong-token-abcdef" .smoke/bad.err && { echo "smoke: wrong token echoed" >&2; exit 1; }
echo "smoke: OK"