1package cli
2
3import (
4 "bytes"
5 "context"
6 "testing"
7
8 climocks "github.com/bnema/gordon/internal/adapters/in/cli/mocks"
9 "github.com/bnema/gordon/internal/adapters/in/cli/remote"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/mock"
12 "github.com/stretchr/testify/require"
13)
14
15func TestResolveAttachmentImage_OneTarget(t *testing.T) {
16 cpMock := climocks.NewMockControlPlane(t)
17 cpMock.EXPECT().FindAttachmentTargetsByImage(context.Background(), "postgres").Return([]string{"app.example.com"}, nil).Once()
18 cpMock.EXPECT().GetStatus(context.Background()).Return(&remote.Status{RegistryDomain: "registry.example.com"}, nil).Once()
19
20 registry, imageName, targets, err := resolveAttachmentImage(context.Background(), cpMock, "postgres")
21
22 require.NoError(t, err)
23 assert.Equal(t, "registry.example.com", registry)
24 assert.Equal(t, "postgres", imageName)
25 assert.Equal(t, []string{"app.example.com"}, targets)
26}
27
28func TestResolveAttachmentImage_MultipleTargets(t *testing.T) {
29 cpMock := climocks.NewMockControlPlane(t)
30 cpMock.EXPECT().FindAttachmentTargetsByImage(context.Background(), "redis").Return([]string{"app.example.com", "backend"}, nil).Once()
31 cpMock.EXPECT().GetStatus(context.Background()).Return(&remote.Status{RegistryDomain: "registry.example.com"}, nil).Once()
32
33 registry, imageName, targets, err := resolveAttachmentImage(context.Background(), cpMock, "redis")
34
35 require.NoError(t, err)
36 assert.Equal(t, "registry.example.com", registry)
37 assert.Equal(t, "redis", imageName)
38 assert.Equal(t, []string{"app.example.com", "backend"}, targets)
39}
40
41func TestResolveAttachmentImage_NotConfigured(t *testing.T) {
42 cpMock := climocks.NewMockControlPlane(t)
43 cpMock.EXPECT().FindAttachmentTargetsByImage(context.Background(), "postgres").Return(nil, nil).Once()
44
45 _, _, _, err := resolveAttachmentImage(context.Background(), cpMock, "postgres")
46
47 require.Error(t, err)
48 assert.Contains(t, err.Error(), "not configured as an attachment")
49}
50
51func TestAttachmentPushCmd_NoDeploy(t *testing.T) {
52 origResolveControlPlane := resolveControlPlaneFn
53 origDetermineVersion := determineVersionFn
54 origNewImageOps := newImageOpsFn
55 t.Cleanup(func() {
56 resolveControlPlaneFn = origResolveControlPlane
57 determineVersionFn = origDetermineVersion
58 newImageOpsFn = origNewImageOps
59 })
60
61 cpMock := climocks.NewMockControlPlane(t)
62 cpMock.EXPECT().FindAttachmentTargetsByImage(context.Background(), "postgres").Return([]string{"app.example.com"}, nil).Once()
63 cpMock.EXPECT().GetStatus(context.Background()).Return(&remote.Status{RegistryDomain: "registry.example.com"}, nil).Once()
64
65 resolveControlPlaneFn = func(string) (*controlPlaneHandle, error) {
66 return &controlPlaneHandle{plane: cpMock}, nil
67 }
68 determineVersionFn = func(context.Context, string) string { return "v1.2.3" }
69 pushCalled := false
70 newImageOpsFn = func() (pushImageOps, error) {
71 m := climocks.NewMockpushImageOps(t)
72 m.On("Tag", mock.Anything, mock.Anything, mock.Anything).Return(nil)
73 m.On("Exists", mock.Anything, mock.Anything).Return(true, nil)
74 m.On("Push", mock.Anything, mock.Anything).Run(func(mock.Arguments) {
75 pushCalled = true
76 }).Return(nil)
77 return m, nil
78 }
79
80 cmd := newAttachmentsPushCmd()
81 out := new(bytes.Buffer)
82 cmd.SetOut(out)
83 cmd.SetErr(out)
84 cmd.SetArgs([]string{"postgres"})
85
86 err := cmd.Execute()
87
88 require.NoError(t, err)
89 assert.True(t, pushCalled)
90 assert.Contains(t, out.String(), "Push complete")
91}
92
93func TestAttachmentPushCmd_TaggedImageInputBuildsValidRefs(t *testing.T) {
94 origResolveControlPlane := resolveControlPlaneFn
95 origDetermineVersion := determineVersionFn
96 origNewImageOps := newImageOpsFn
97 t.Cleanup(func() {
98 resolveControlPlaneFn = origResolveControlPlane
99 determineVersionFn = origDetermineVersion
100 newImageOpsFn = origNewImageOps
101 })
102
103 cpMock := climocks.NewMockControlPlane(t)
104 cpMock.EXPECT().FindAttachmentTargetsByImage(context.Background(), "postgres:18").Return([]string{"app.example.com"}, nil).Once()
105 cpMock.EXPECT().GetStatus(context.Background()).Return(&remote.Status{RegistryDomain: "registry.example.com"}, nil).Once()
106
107 resolveControlPlaneFn = func(string) (*controlPlaneHandle, error) {
108 return &controlPlaneHandle{plane: cpMock}, nil
109 }
110 determineVersionFn = func(context.Context, string) string { return "v1.2.3" }
111
112 var gotVersionRef string
113 var gotLatestRef string
114 newImageOpsFn = func() (pushImageOps, error) {
115 m := climocks.NewMockpushImageOps(t)
116 m.On("Tag", mock.Anything, mock.Anything, mock.Anything).Return(nil)
117 m.On("Exists", mock.Anything, mock.Anything).Return(true, nil)
118 m.On("Push", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
119 ref := args.Get(1).(string)
120 if gotVersionRef == "" {
121 gotVersionRef = ref
122 } else if gotLatestRef == "" {
123 gotLatestRef = ref
124 }
125 }).Return(nil)
126 return m, nil
127 }
128
129 cmd := newAttachmentsPushCmd()
130 cmd.SetOut(new(bytes.Buffer))
131 cmd.SetErr(new(bytes.Buffer))
132 cmd.SetArgs([]string{"postgres:18"})
133
134 err := cmd.Execute()
135
136 require.NoError(t, err)
137 assert.Equal(t, "registry.example.com/postgres:v1.2.3", gotVersionRef)
138 assert.Equal(t, "registry.example.com/postgres:latest", gotLatestRef)
139}
140
141func TestNormalizeAttachmentImageName_PreservesNamespaceWithoutRegistryHost(t *testing.T) {
142 got := normalizeAttachmentImageName("myorg/postgres:18")
143
144 assert.Equal(t, "myorg/postgres", got)
145}