cli + daemon: move test seams off package globals onto injected structs
CLI: introduce internal/cli.deps which owns every RPC/SSH/host-command seam the tree used to reach through mutable package vars. Command builders, orchestrators, and the completion helpers become methods on *deps. Tests construct their own deps per case, so fakes no longer leak across cases and tests are free to run in parallel. Daemon: move workspaceInspectRepoFunc + workspaceImportFunc onto the Daemon struct (workspaceInspectRepo / workspaceImport), mirroring the existing guestWaitForSSH / guestDial pattern. Workspace-prepare tests drop t.Parallel() guards now that they no longer mutate process-wide state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d38f580e00
commit
c42fcbe012
19 changed files with 664 additions and 733 deletions
|
|
@ -80,9 +80,9 @@ func (e ExitCodeError) Error() string {
|
|||
// - it sits inside a non-bare git repository,
|
||||
// - the repository has no submodules (unsupported in the shallow
|
||||
// overlay mode vm run uses).
|
||||
func vmRunPreflightRepo(ctx context.Context, rawPath string) (string, error) {
|
||||
func (d *deps) vmRunPreflightRepo(ctx context.Context, rawPath string) (string, error) {
|
||||
if strings.TrimSpace(rawPath) == "" {
|
||||
wd, err := cwdFunc()
|
||||
wd, err := d.cwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -131,9 +131,9 @@ func splitVMRunArgs(cmd *cobra.Command, args []string) (pathArgs, commandArgs []
|
|||
// for guest ssh, optionally materialise a workspace and kick off the
|
||||
// tooling bootstrap, then either attach interactively or run the
|
||||
// user's command and propagate its exit status.
|
||||
func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, stdin io.Reader, stdout, stderr io.Writer, params api.VMCreateParams, repo *vmRunRepo, command []string, removeOnExit bool) error {
|
||||
func (d *deps) runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, stdin io.Reader, stdout, stderr io.Writer, params api.VMCreateParams, repo *vmRunRepo, command []string, removeOnExit bool) error {
|
||||
progress := newVMRunProgressRenderer(stderr)
|
||||
vm, err := runVMCreate(ctx, socketPath, stderr, params)
|
||||
vm, err := d.runVMCreate(ctx, socketPath, stderr, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -155,7 +155,7 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
// doesn't abort the delete RPC.
|
||||
cleanupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := vmDeleteFunc(cleanupCtx, socketPath, vmRef); err != nil {
|
||||
if err := d.vmDelete(cleanupCtx, socketPath, vmRef); err != nil {
|
||||
printVMRunWarning(stderr, fmt.Sprintf("--rm cleanup failed: %v (leaked vm %q; delete manually)", err, vmRef))
|
||||
}
|
||||
}()
|
||||
|
|
@ -163,7 +163,7 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
sshAddress := net.JoinHostPort(vm.Runtime.GuestIP, "22")
|
||||
progress.render("waiting for guest ssh")
|
||||
sshCtx, cancelSSH := context.WithTimeout(ctx, vmRunSSHTimeout)
|
||||
if err := guestWaitForSSHFunc(sshCtx, sshAddress, cfg.SSHKeyPath, 250*time.Millisecond); err != nil {
|
||||
if err := d.guestWaitForSSH(sshCtx, sshAddress, cfg.SSHKeyPath, 250*time.Millisecond); err != nil {
|
||||
cancelSSH()
|
||||
// Surface parent-context cancellation (Ctrl-C, caller
|
||||
// timeout) as-is. Only the guest-side timeout needs the
|
||||
|
|
@ -193,7 +193,7 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
if strings.TrimSpace(repo.branchName) != "" {
|
||||
fromRef = repo.fromRef
|
||||
}
|
||||
prepared, err := vmWorkspacePrepareFunc(ctx, socketPath, api.VMWorkspacePrepareParams{
|
||||
prepared, err := d.vmWorkspacePrepare(ctx, socketPath, api.VMWorkspacePrepareParams{
|
||||
IDOrName: vmRef,
|
||||
SourcePath: repo.sourcePath,
|
||||
GuestPath: vmRunGuestDir(),
|
||||
|
|
@ -208,11 +208,11 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
// daemon side; grab what the tooling harness needs from its
|
||||
// result instead of re-inspecting here.
|
||||
if len(command) == 0 {
|
||||
client, err := guestDialFunc(ctx, sshAddress, cfg.SSHKeyPath)
|
||||
client, err := d.guestDial(ctx, sshAddress, cfg.SSHKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vm %q is running but guest ssh is unavailable: %w", vmRef, err)
|
||||
}
|
||||
if err := startVMRunToolingHarness(ctx, client, prepared.Workspace.RepoRoot, prepared.Workspace.RepoName, progress); err != nil {
|
||||
if err := d.startVMRunToolingHarness(ctx, client, prepared.Workspace.RepoRoot, prepared.Workspace.RepoName, progress); err != nil {
|
||||
printVMRunWarning(stderr, fmt.Sprintf("guest tooling bootstrap start failed: %v", err))
|
||||
}
|
||||
_ = client.Close()
|
||||
|
|
@ -224,7 +224,7 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
}
|
||||
if len(command) > 0 {
|
||||
progress.render("running command in guest")
|
||||
if err := sshExecFunc(ctx, stdin, stdout, stderr, sshArgs); err != nil {
|
||||
if err := d.sshExec(ctx, stdin, stdout, stderr, sshArgs); err != nil {
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) {
|
||||
return ExitCodeError{Code: exitErr.ExitCode()}
|
||||
|
|
@ -234,7 +234,7 @@ func runVMRun(ctx context.Context, socketPath string, cfg model.DaemonConfig, st
|
|||
return nil
|
||||
}
|
||||
progress.render("attaching to guest")
|
||||
return runSSHSession(ctx, socketPath, vmRef, stdin, stdout, stderr, sshArgs, removeOnExit)
|
||||
return d.runSSHSession(ctx, socketPath, vmRef, stdin, stdout, stderr, sshArgs, removeOnExit)
|
||||
}
|
||||
|
||||
func vmRunGuestDir() string {
|
||||
|
|
@ -253,11 +253,11 @@ func vmRunToolingHarnessLogPath(repoName string) string {
|
|||
// script inside the guest. repoRoot / repoName both come from the
|
||||
// daemon's workspace.prepare RPC response — the CLI no longer does
|
||||
// its own git inspection.
|
||||
func startVMRunToolingHarness(ctx context.Context, client vmRunGuestClient, repoRoot, repoName string, progress *vmRunProgressRenderer) error {
|
||||
func (d *deps) startVMRunToolingHarness(ctx context.Context, client vmRunGuestClient, repoRoot, repoName string, progress *vmRunProgressRenderer) error {
|
||||
if progress != nil {
|
||||
progress.render("starting guest tooling bootstrap")
|
||||
}
|
||||
plan := buildVMRunToolingPlanFunc(ctx, repoRoot)
|
||||
plan := d.buildVMRunToolingPlan(ctx, repoRoot)
|
||||
var uploadLog bytes.Buffer
|
||||
if err := client.UploadFile(ctx, vmRunToolingHarnessPath(repoName), 0o755, []byte(vmRunToolingHarnessScript(plan)), &uploadLog); err != nil {
|
||||
return formatVMRunStepError("upload guest tooling bootstrap", err, uploadLog.String())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue