port smoke to go
This commit is contained in:
parent
b0a9d64f4a
commit
9ed44bfd75
20 changed files with 2118 additions and 1573 deletions
205
internal/smoketest/scenarios_repodir_test.go
Normal file
205
internal/smoketest/scenarios_repodir_test.go
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
//go:build smoke
|
||||
|
||||
package smoketest
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// testWorkspaceRun ports scenario_workspace_run. Ships the throwaway
|
||||
// git repo to a fresh VM and reads the marker file from the guest.
|
||||
func testWorkspaceRun(t *testing.T) {
|
||||
out := mustBanger(t, "vm", "run", "--rm", repoDir, "--", "cat", "/root/repo/smoke-file.txt")
|
||||
wantContains(t, out, "smoke-workspace-marker", "workspace vm run guest read")
|
||||
}
|
||||
|
||||
// testWorkspaceDryrun ports scenario_workspace_dryrun. `--dry-run`
|
||||
// lists the tracked files and the resolved transfer mode without
|
||||
// creating a VM.
|
||||
func testWorkspaceDryrun(t *testing.T) {
|
||||
out := mustBanger(t, "vm", "run", "--dry-run", repoDir)
|
||||
wantContains(t, out, "smoke-file.txt", "dry-run file list")
|
||||
wantContains(t, out, "mode: tracked only", "dry-run mode line")
|
||||
}
|
||||
|
||||
// testIncludeUntracked ports scenario_include_untracked. Drops an
|
||||
// untracked file in the fixture and asserts --include-untracked picks
|
||||
// it up. The cleanup hook removes the file even if the scenario fails
|
||||
// so downstream repodir scenarios see the original tree.
|
||||
func testIncludeUntracked(t *testing.T) {
|
||||
untracked := filepath.Join(repoDir, "smoke-untracked.txt")
|
||||
if err := os.WriteFile(untracked, []byte("untracked-marker\n"), 0o644); err != nil {
|
||||
t.Fatalf("write untracked file: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = os.Remove(untracked) })
|
||||
|
||||
out := mustBanger(t, "vm", "run", "--rm", "--include-untracked", repoDir,
|
||||
"--", "cat", "/root/repo/smoke-untracked.txt")
|
||||
wantContains(t, out, "untracked-marker", "include-untracked guest read")
|
||||
}
|
||||
|
||||
// testWorkspaceExport ports scenario_workspace_export. Round-trips a
|
||||
// guest-side edit back out as a patch via `vm workspace export`.
|
||||
func testWorkspaceExport(t *testing.T) {
|
||||
const name = "smoke-export"
|
||||
vmCreate(t, name, "--image", "debian-bookworm")
|
||||
mustBanger(t, "vm", "workspace", "prepare", name, repoDir)
|
||||
mustBanger(t, "vm", "ssh", name, "--", "sh", "-c",
|
||||
"echo guest-edit > /root/repo/new-guest-file.txt")
|
||||
|
||||
patch := filepath.Join(runtimeDir, "smoke-export.diff")
|
||||
mustBanger(t, "vm", "workspace", "export", name, "--output", patch)
|
||||
|
||||
st, err := os.Stat(patch)
|
||||
if err != nil {
|
||||
t.Fatalf("export: stat patch %s: %v", patch, err)
|
||||
}
|
||||
if st.Size() == 0 {
|
||||
t.Fatalf("export: patch file empty at %s", patch)
|
||||
}
|
||||
body, err := os.ReadFile(patch)
|
||||
if err != nil {
|
||||
t.Fatalf("export: read patch: %v", err)
|
||||
}
|
||||
wantContains(t, string(body), "new-guest-file.txt", "export: patch must reference new-guest-file.txt")
|
||||
}
|
||||
|
||||
// testWorkspaceFullCopy ports scenario_workspace_full_copy. Verifies
|
||||
// the alternate transfer path (--mode full_copy) lands the same fixture
|
||||
// in the guest.
|
||||
func testWorkspaceFullCopy(t *testing.T) {
|
||||
const name = "smoke-fc"
|
||||
vmCreate(t, name)
|
||||
mustBanger(t, "vm", "workspace", "prepare", name, repoDir, "--mode", "full_copy")
|
||||
|
||||
out := mustBanger(t, "vm", "ssh", name, "--", "cat", "/root/repo/smoke-file.txt")
|
||||
wantContains(t, out, "smoke-workspace-marker", "full_copy: marker missing in guest")
|
||||
}
|
||||
|
||||
// testWorkspaceBasecommit ports scenario_workspace_basecommit. Confirms
|
||||
// that `vm workspace export` without --base-commit captures only the
|
||||
// working-copy diff, while --base-commit also captures guest-side
|
||||
// commits made on top of HEAD.
|
||||
func testWorkspaceBasecommit(t *testing.T) {
|
||||
const name = "smoke-basecommit"
|
||||
vmCreate(t, name)
|
||||
mustBanger(t, "vm", "workspace", "prepare", name, repoDir)
|
||||
|
||||
baseSHA := strings.TrimSpace(mustBanger(t, "vm", "ssh", name, "--",
|
||||
"sh", "-c", "cd /root/repo && git rev-parse HEAD"))
|
||||
if len(baseSHA) != 40 {
|
||||
t.Fatalf("export base: bad base sha: %q", baseSHA)
|
||||
}
|
||||
|
||||
mustBanger(t, "vm", "ssh", name, "--", "sh", "-c",
|
||||
"cd /root/repo && "+
|
||||
"git -c user.email=smoke@smoke -c user.name=smoke checkout -b smoke-branch >/dev/null 2>&1 && "+
|
||||
"echo committed-marker > smoke-committed.txt && "+
|
||||
"git add smoke-committed.txt && "+
|
||||
"git -c user.email=smoke@smoke -c user.name=smoke commit -q -m 'guest side'")
|
||||
|
||||
plain := filepath.Join(runtimeDir, "smoke-plain.diff")
|
||||
mustBanger(t, "vm", "workspace", "export", name, "--output", plain)
|
||||
if body, err := os.ReadFile(plain); err == nil {
|
||||
wantNotContains(t, string(body), "smoke-committed.txt",
|
||||
"export base: plain export must NOT capture guest-side commit")
|
||||
}
|
||||
|
||||
base := filepath.Join(runtimeDir, "smoke-base.diff")
|
||||
mustBanger(t, "vm", "workspace", "export", name, "--base-commit", baseSHA, "--output", base)
|
||||
st, err := os.Stat(base)
|
||||
if err != nil || st.Size() == 0 {
|
||||
t.Fatalf("export base: --base-commit patch empty/missing: stat=%v err=%v", st, err)
|
||||
}
|
||||
body, _ := os.ReadFile(base)
|
||||
wantContains(t, string(body), "smoke-committed.txt",
|
||||
"export base: --base-commit patch must include committed marker")
|
||||
}
|
||||
|
||||
// testWorkspaceRestart ports scenario_workspace_restart. Verifies the
|
||||
// workspace marker survives a stop/start cycle (rootfs persistence).
|
||||
func testWorkspaceRestart(t *testing.T) {
|
||||
const name = "smoke-wsrestart"
|
||||
vmCreate(t, name)
|
||||
mustBanger(t, "vm", "workspace", "prepare", name, repoDir)
|
||||
|
||||
pre := mustBanger(t, "vm", "ssh", name, "--", "cat", "/root/repo/smoke-file.txt")
|
||||
wantContains(t, pre, "smoke-workspace-marker", "workspace stop/start: pre-cycle marker")
|
||||
|
||||
mustBanger(t, "vm", "stop", name)
|
||||
mustBanger(t, "vm", "start", name)
|
||||
waitForSSH(t, name)
|
||||
|
||||
post := mustBanger(t, "vm", "ssh", name, "--", "cat", "/root/repo/smoke-file.txt")
|
||||
wantContains(t, post, "smoke-workspace-marker", "workspace stop/start: post-cycle marker")
|
||||
}
|
||||
|
||||
// testVMExec ports scenario_vm_exec. The longest scenario in the suite
|
||||
// — covers auto-cd, exit-code propagation, stale-workspace detection,
|
||||
// --auto-prepare resync, and the not-running refusal. The repodir
|
||||
// commit added mid-scenario is rolled back via t.Cleanup so subsequent
|
||||
// repodir-chain scenarios see the original fixture state.
|
||||
func testVMExec(t *testing.T) {
|
||||
const name = "smoke-exec"
|
||||
vmCreate(t, name)
|
||||
mustBanger(t, "vm", "workspace", "prepare", name, repoDir)
|
||||
|
||||
show := mustBanger(t, "vm", "show", name)
|
||||
wantContains(t, show, `"guest_path": "/root/repo"`,
|
||||
"vm exec: workspace.guest_path not persisted")
|
||||
|
||||
out := mustBanger(t, "vm", "exec", name, "--", "cat", "smoke-file.txt")
|
||||
wantContains(t, out, "smoke-workspace-marker", "vm exec: workspace marker")
|
||||
|
||||
if got := strings.TrimSpace(mustBanger(t, "vm", "exec", name, "--", "pwd")); got != "/root/repo" {
|
||||
t.Fatalf("vm exec: pwd got %q, want /root/repo (auto-cd didn't happen)", got)
|
||||
}
|
||||
|
||||
res := banger(t, "vm", "exec", name, "--", "sh", "-c", "exit 17")
|
||||
wantExit(t, res, 17, "vm exec: exit-code propagation")
|
||||
|
||||
// Advance host HEAD so the workspace goes stale, register the
|
||||
// rollback before mutating so a Fatal anywhere below still
|
||||
// restores the fixture.
|
||||
t.Cleanup(func() {
|
||||
cmd := exec.Command("git", "reset", "--hard", "HEAD~1", "-q")
|
||||
cmd.Dir = repoDir
|
||||
_ = cmd.Run()
|
||||
})
|
||||
for _, args := range [][]string{
|
||||
{"sh", "-c", "echo post-prepare-marker > smoke-exec-new.txt"},
|
||||
{"git", "add", "smoke-exec-new.txt"},
|
||||
{"git", "commit", "-q", "-m", "add smoke-exec-new.txt after prepare"},
|
||||
} {
|
||||
cmd := exec.Command(args[0], args[1:]...)
|
||||
cmd.Dir = repoDir
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("vm exec: stage host commit: %s: %v\n%s", args, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
stale := banger(t, "vm", "exec", name, "--", "ls", "smoke-exec-new.txt")
|
||||
if stale.rc == 0 {
|
||||
t.Fatalf("vm exec: stale workspace already had the new file (dirty path didn't take effect)")
|
||||
}
|
||||
wantContains(t, stale.stderr, "workspace stale", "vm exec: stale-workspace warning on stderr")
|
||||
wantContains(t, stale.stderr, "--auto-prepare", "vm exec: stale warning must mention --auto-prepare")
|
||||
|
||||
auto := mustBanger(t, "vm", "exec", name, "--auto-prepare", "--", "cat", "smoke-exec-new.txt")
|
||||
wantContains(t, auto, "post-prepare-marker", "vm exec: --auto-prepare didn't re-sync new file")
|
||||
|
||||
clean := banger(t, "vm", "exec", name, "--", "true")
|
||||
wantExit(t, clean, 0, "vm exec: post-auto-prepare run")
|
||||
wantNotContains(t, clean.stderr, "workspace stale", "vm exec: stale warning persisted after --auto-prepare")
|
||||
|
||||
mustBanger(t, "vm", "stop", name)
|
||||
stopped := banger(t, "vm", "exec", name, "--", "true")
|
||||
if stopped.rc == 0 {
|
||||
t.Fatalf("vm exec: exec on stopped VM unexpectedly succeeded")
|
||||
}
|
||||
wantContains(t, stopped.stderr, "not running", "vm exec: stopped-VM error message")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue