test(provisioner): direct unit tests for KI-013 backward-compat migrate path (#2482) #2494

Merged
agent-reviewer merged 3 commits from test/backward-compat-migrate-unit-tests into main 2026-06-10 14:08:28 +00:00
@@ -0,0 +1,201 @@
package provisioner
import (
"context"
"fmt"
"io"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
dockerimage "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// compatFakeDockerClient is a test double that implements dockerClient.
// It records every call and holds lightweight in-memory state for volumes
// and containers so tests can assert behaviour without a real daemon.
type compatFakeDockerClient struct {
volumes map[string]volume.Volume
containers map[string]container.InspectResponse
removeErr map[string]error
inspectErr map[string]error
calls []string
}
func newCompatFakeDockerClient() *compatFakeDockerClient {
return &compatFakeDockerClient{
volumes: make(map[string]volume.Volume),
containers: make(map[string]container.InspectResponse),
removeErr: make(map[string]error),
inspectErr: make(map[string]error),
}
}
func (f *compatFakeDockerClient) record(format string, args ...interface{}) {
f.calls = append(f.calls, fmt.Sprintf(format, args...))
}
func (f *compatFakeDockerClient) Close() error { return nil }
func (f *compatFakeDockerClient) ContainerCreate(ctx context.Context, config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *ocispec.Platform, containerName string) (container.CreateResponse, error) {
f.record("ContainerCreate:%s", containerName)
return container.CreateResponse{}, nil
}
func (f *compatFakeDockerClient) ContainerExecAttach(ctx context.Context, execID string, config container.ExecAttachOptions) (types.HijackedResponse, error) {
panic("ContainerExecAttach not expected")
}
func (f *compatFakeDockerClient) ContainerExecCreate(ctx context.Context, container string, config container.ExecOptions) (container.ExecCreateResponse, error) {
panic("ContainerExecCreate not expected")
}
func (f *compatFakeDockerClient) ContainerInspect(ctx context.Context, name string) (container.InspectResponse, error) {
f.record("ContainerInspect:%s", name)
if err, ok := f.inspectErr[name]; ok {
return container.InspectResponse{}, err
}
c, ok := f.containers[name]
if !ok {
return container.InspectResponse{}, fmt.Errorf("No such container: %s", name)
}
return c, nil
}
func (f *compatFakeDockerClient) ContainerList(ctx context.Context, options container.ListOptions) ([]container.Summary, error) {
panic("ContainerList not expected")
}
func (f *compatFakeDockerClient) ContainerLogs(ctx context.Context, container string, options container.LogsOptions) (io.ReadCloser, error) {
panic("ContainerLogs not expected")
}
func (f *compatFakeDockerClient) ContainerRemove(ctx context.Context, name string, options container.RemoveOptions) error {
f.record("ContainerRemove:%s", name)
if err, ok := f.removeErr[name]; ok {
return err
}
if _, ok := f.containers[name]; !ok {
return fmt.Errorf("No such container: %s", name)
}
delete(f.containers, name)
return nil
}
func (f *compatFakeDockerClient) ContainerRename(ctx context.Context, container, newContainerName string) error {
return nil
}
func (f *compatFakeDockerClient) ContainerStart(ctx context.Context, name string, options container.StartOptions) error {
f.record("ContainerStart:%s", name)
return nil
}
func (f *compatFakeDockerClient) ContainerWait(ctx context.Context, name string, condition container.WaitCondition) (
<-chan container.WaitResponse, <-chan error) {
f.record("ContainerWait:%s", name)
done := make(chan container.WaitResponse, 1)
errCh := make(chan error, 1)
done <- container.WaitResponse{StatusCode: 0}
close(done)
close(errCh)
return done, errCh
}
func (f *compatFakeDockerClient) CopyToContainer(ctx context.Context, container, path string, content io.Reader, options container.CopyToContainerOptions) error {
panic("CopyToContainer not expected")
}
func (f *compatFakeDockerClient) ImageInspect(ctx context.Context, image string, opts ...client.ImageInspectOption) (dockerimage.InspectResponse, error) {
panic("ImageInspect not expected")
}
func (f *compatFakeDockerClient) ImagePull(ctx context.Context, ref string, opts dockerimage.PullOptions) (io.ReadCloser, error) {
panic("ImagePull not expected")
}
func (f *compatFakeDockerClient) VolumeCreate(ctx context.Context, options volume.CreateOptions) (volume.Volume, error) {
f.record("VolumeCreate:%s", options.Name)
f.volumes[options.Name] = volume.Volume{Name: options.Name}
return f.volumes[options.Name], nil
}
func (f *compatFakeDockerClient) VolumeInspect(ctx context.Context, name string) (volume.Volume, error) {
f.record("VolumeInspect:%s", name)
v, ok := f.volumes[name]
if !ok {
return volume.Volume{}, fmt.Errorf("No such volume: %s", name)
}
return v, nil
}
func (f *compatFakeDockerClient) VolumeRemove(ctx context.Context, name string, force bool) error {
f.record("VolumeRemove:%s", name)
if _, ok := f.volumes[name]; !ok {
return fmt.Errorf("No such volume: %s", name)
}
delete(f.volumes, name)
return nil
}
// (c) RunningContainerName surfaces transient errors (not not-found).
func TestRunningContainerName_TransientError(t *testing.T) {
ctx := context.Background()
f := newCompatFakeDockerClient()
f.inspectErr[ContainerName("ws-abc123")] = fmt.Errorf("daemon socket EOF")
_, err := RunningContainerName(ctx, f, "ws-abc123")
if err == nil {
t.Fatalf("RunningContainerName transient error: expected error, got nil")
}
}
// (d) RemoveVolume removes only the target workspace's volumes.
func TestRemoveVolume_WorkspaceScoped(t *testing.T) {
ctx := context.Background()
f := newCompatFakeDockerClient()
wsA := "ws-abc123"
wsB := "ws-def456"
f.volumes[ConfigVolumeName(wsA)] = volume.Volume{Name: ConfigVolumeName(wsA)}
f.volumes[legacyConfigVolumeName(wsA)] = volume.Volume{Name: legacyConfigVolumeName(wsA)}
f.volumes[ConfigVolumeName(wsB)] = volume.Volume{Name: ConfigVolumeName(wsB)}
f.volumes[legacyConfigVolumeName(wsB)] = volume.Volume{Name: legacyConfigVolumeName(wsB)}
f.volumes[ClaudeSessionVolumeName(wsA)] = volume.Volume{Name: ClaudeSessionVolumeName(wsA)}
f.volumes[legacyClaudeSessionVolumeName(wsA)] = volume.Volume{Name: legacyClaudeSessionVolumeName(wsA)}
f.volumes[ClaudeSessionVolumeName(wsB)] = volume.Volume{Name: ClaudeSessionVolumeName(wsB)}
f.volumes[legacyClaudeSessionVolumeName(wsB)] = volume.Volume{Name: legacyClaudeSessionVolumeName(wsB)}
p := &Provisioner{cli: f}
if err := p.RemoveVolume(ctx, wsA); err != nil {
t.Fatalf("RemoveVolume scoped: unexpected error: %v", err)
}
// wsA volumes must be gone.
for _, v := range []string{ConfigVolumeName(wsA), legacyConfigVolumeName(wsA), ClaudeSessionVolumeName(wsA), legacyClaudeSessionVolumeName(wsA)} {
if _, ok := f.volumes[v]; ok {
t.Errorf("RemoveVolume scoped: expected %s to be removed", v)
}
}
// wsB volumes must remain.
for _, v := range []string{ConfigVolumeName(wsB), legacyConfigVolumeName(wsB), ClaudeSessionVolumeName(wsB), legacyClaudeSessionVolumeName(wsB)} {
if _, ok := f.volumes[v]; !ok {
t.Errorf("RemoveVolume scoped: expected %s to remain", v)
}
}
}
// (d) RemoveVolume returns error when neither new nor legacy config volume exists.
func TestRemoveVolume_BothMissing(t *testing.T) {
ctx := context.Background()
f := newCompatFakeDockerClient()
p := &Provisioner{cli: f}
err := p.RemoveVolume(ctx, "abcdefghijklmnopqrstuvwxyz")
if err == nil {
t.Fatalf("RemoveVolume both missing: expected error, got nil")
}
}