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
|
|
@ -21,9 +21,10 @@ import (
|
|||
// - Fail silently. Completion is advisory; any error path returns an
|
||||
// empty suggestion list rather than propagating to the user.
|
||||
|
||||
// completionListerFunc is the seam used by tests to avoid touching a
|
||||
// real daemon socket.
|
||||
var completionListerFunc = func(ctx context.Context, socketPath, method string) ([]string, error) {
|
||||
// defaultCompletionLister + defaultCompletionSessionLister back the
|
||||
// corresponding *deps fields; tests inject their own fakes via the
|
||||
// struct instead of mutating package-level vars.
|
||||
func defaultCompletionLister(ctx context.Context, socketPath, method string) ([]string, error) {
|
||||
switch method {
|
||||
case "vm.list":
|
||||
result, err := rpc.Call[api.VMListResult](ctx, socketPath, method, api.Empty{})
|
||||
|
|
@ -65,9 +66,7 @@ var completionListerFunc = func(ctx context.Context, socketPath, method string)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// completionSessionListerFunc is the seam for guest-session name lookups
|
||||
// scoped to a VM.
|
||||
var completionSessionListerFunc = func(ctx context.Context, socketPath, vmIDOrName string) ([]string, error) {
|
||||
func defaultCompletionSessionLister(ctx context.Context, socketPath, vmIDOrName string) ([]string, error) {
|
||||
result, err := rpc.Call[api.GuestSessionListResult](ctx, socketPath, "guest.session.list", api.VMRefParams{IDOrName: vmIDOrName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -84,12 +83,12 @@ var completionSessionListerFunc = func(ctx context.Context, socketPath, vmIDOrNa
|
|||
// daemonSocketForCompletion returns the socket path IFF the daemon is
|
||||
// already running. Returns "", false when no daemon is up — completion
|
||||
// callers use this as the bail signal.
|
||||
func daemonSocketForCompletion(ctx context.Context) (string, bool) {
|
||||
func (d *deps) daemonSocketForCompletion(ctx context.Context) (string, bool) {
|
||||
layout, err := paths.Resolve()
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
if _, err := daemonPingFunc(ctx, layout.SocketPath); err != nil {
|
||||
if _, err := d.daemonPing(ctx, layout.SocketPath); err != nil {
|
||||
return "", false
|
||||
}
|
||||
return layout.SocketPath, true
|
||||
|
|
@ -119,12 +118,12 @@ func hasPrefix(s, prefix string) bool {
|
|||
return len(s) >= len(prefix) && s[:len(prefix)] == prefix
|
||||
}
|
||||
|
||||
func completeVMNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := daemonSocketForCompletion(cmd.Context())
|
||||
func (d *deps) completeVMNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := d.daemonSocketForCompletion(cmd.Context())
|
||||
if !ok {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
names, err := completionListerFunc(cmd.Context(), socket, "vm.list")
|
||||
names, err := d.completionLister(cmd.Context(), socket, "vm.list")
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
@ -134,45 +133,45 @@ func completeVMNames(cmd *cobra.Command, args []string, toComplete string) ([]st
|
|||
// completeVMNameOnlyAtPos0 restricts VM-name completion to the first
|
||||
// positional argument. Used by commands like `vm ssh <vm> [ssh args...]`
|
||||
// where args after pos 0 are free-form.
|
||||
func completeVMNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func (d *deps) completeVMNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) > 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return completeVMNames(cmd, args, toComplete)
|
||||
return d.completeVMNames(cmd, args, toComplete)
|
||||
}
|
||||
|
||||
func completeImageNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func (d *deps) completeImageNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) > 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return completeImageNames(cmd, args, toComplete)
|
||||
return d.completeImageNames(cmd, args, toComplete)
|
||||
}
|
||||
|
||||
func completeKernelNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func (d *deps) completeKernelNameOnlyAtPos0(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) > 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return completeKernelNames(cmd, args, toComplete)
|
||||
return d.completeKernelNames(cmd, args, toComplete)
|
||||
}
|
||||
|
||||
func completeImageNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := daemonSocketForCompletion(cmd.Context())
|
||||
func (d *deps) completeImageNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := d.daemonSocketForCompletion(cmd.Context())
|
||||
if !ok {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
names, err := completionListerFunc(cmd.Context(), socket, "image.list")
|
||||
names, err := d.completionLister(cmd.Context(), socket, "image.list")
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return filterPrefix(names, args, toComplete), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func completeKernelNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := daemonSocketForCompletion(cmd.Context())
|
||||
func (d *deps) completeKernelNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
socket, ok := d.daemonSocketForCompletion(cmd.Context())
|
||||
if !ok {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
names, err := completionListerFunc(cmd.Context(), socket, "kernel.list")
|
||||
names, err := d.completionLister(cmd.Context(), socket, "kernel.list")
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
@ -182,16 +181,16 @@ func completeKernelNames(cmd *cobra.Command, args []string, toComplete string) (
|
|||
// completeSessionNames handles `... <vm> <session>` commands: pos 0
|
||||
// completes VMs, pos 1 completes sessions owned by args[0], pos 2+ is
|
||||
// silent.
|
||||
func completeSessionNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func (d *deps) completeSessionNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return completeVMNames(cmd, args, toComplete)
|
||||
return d.completeVMNames(cmd, args, toComplete)
|
||||
case 1:
|
||||
socket, ok := daemonSocketForCompletion(cmd.Context())
|
||||
socket, ok := d.daemonSocketForCompletion(cmd.Context())
|
||||
if !ok {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
names, err := completionSessionListerFunc(cmd.Context(), socket, args[0])
|
||||
names, err := d.completionSessionLister(cmd.Context(), socket, args[0])
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue