package updater_test import ( "context" "errors" "testing" "github.com/docker/docker/api/types/swarm" "github.com/shcizo/package-updater/internal/discovery" "github.com/shcizo/package-updater/internal/updater" "github.com/stretchr/testify/require" ) type fakeSwarmDockerClient struct { inspectService swarm.Service inspectErr error updateErr error inspectCalled bool updateCalled bool updateCalledServiceID string updateCalledVersion swarm.Version updateCalledSpec swarm.ServiceSpec updateCalledOpts swarm.ServiceUpdateOptions } func (f *fakeSwarmDockerClient) ServiceInspectWithRaw(_ context.Context, _ string, _ swarm.ServiceInspectOptions) (swarm.Service, []byte, error) { f.inspectCalled = true return f.inspectService, nil, f.inspectErr } func (f *fakeSwarmDockerClient) ServiceUpdate(_ context.Context, serviceID string, version swarm.Version, spec swarm.ServiceSpec, opts swarm.ServiceUpdateOptions) (swarm.ServiceUpdateResponse, error) { f.updateCalled = true f.updateCalledServiceID = serviceID f.updateCalledVersion = version f.updateCalledSpec = spec f.updateCalledOpts = opts return swarm.ServiceUpdateResponse{}, f.updateErr } func mkInspectService(version uint64, image string) swarm.Service { return swarm.Service{ ID: "svc-myapp", Meta: swarm.Meta{Version: swarm.Version{Index: version}}, Spec: swarm.ServiceSpec{ Annotations: swarm.Annotations{Name: "myapp_web"}, TaskTemplate: swarm.TaskSpec{ContainerSpec: &swarm.ContainerSpec{Image: image}}, }, } } func TestSwarmExecutor_UpdatesImageAndVersion(t *testing.T) { fake := &fakeSwarmDockerClient{inspectService: mkInspectService(7, "registry.example.com/myapp:v1")} e := updater.NewSwarmExecutor(fake) job := discovery.Job{Service: "myapp_web", ServiceID: "svc-myapp", Image: "registry.example.com/myapp:v2"} err := e.Execute(context.Background(), job) require.NoError(t, err) require.True(t, fake.updateCalled) require.Equal(t, "svc-myapp", fake.updateCalledServiceID) require.Equal(t, uint64(7), fake.updateCalledVersion.Index) require.Equal(t, "registry.example.com/myapp:v2", fake.updateCalledSpec.TaskTemplate.ContainerSpec.Image) require.True(t, fake.updateCalledOpts.QueryRegistry) } func TestSwarmExecutor_InspectError(t *testing.T) { fake := &fakeSwarmDockerClient{inspectErr: errors.New("service not found")} e := updater.NewSwarmExecutor(fake) err := e.Execute(context.Background(), discovery.Job{ServiceID: "svc-missing", Image: "x:v2"}) require.Error(t, err) require.False(t, fake.updateCalled) } func TestSwarmExecutor_UpdateError(t *testing.T) { fake := &fakeSwarmDockerClient{ inspectService: mkInspectService(1, "registry.example.com/myapp:v1"), updateErr: errors.New("update rejected"), } e := updater.NewSwarmExecutor(fake) err := e.Execute(context.Background(), discovery.Job{ServiceID: "svc-myapp", Image: "registry.example.com/myapp:v2"}) require.Error(t, err) } func TestSwarmExecutor_RefusedJobIsNotExecuted(t *testing.T) { fake := &fakeSwarmDockerClient{} e := updater.NewSwarmExecutor(fake) job := discovery.Job{ServiceID: "svc-myapp", Image: "x:v2", Refused: true, RefusedReason: "not opted in"} err := e.Execute(context.Background(), job) require.Error(t, err) require.False(t, fake.inspectCalled) require.False(t, fake.updateCalled) }