1
1
import logging
2
2
import unittest .mock as umock
3
- from typing import List
3
+ from typing import Generator , List
4
+
5
+ import pytest
4
6
5
- import helpers .hsystem as hsystem
6
7
import helpers .hunit_test as hunitest
7
8
import helpers .lib_tasks_docker_release as hltadore
8
9
import helpers .test .test_lib_tasks as httestlib
@@ -36,17 +37,23 @@ def _extract_commands_from_call(calls: List[umock._Call]) -> List[str]:
36
37
37
38
38
39
# #############################################################################
39
- # TestDockerBuildLocalImage1
40
+ # _DockerFlowTestHelper
40
41
# #############################################################################
41
42
42
43
43
- class TestDockerBuildLocalImage1 (hunitest .TestCase ):
44
+ class _DockerFlowTestHelper (hunitest .TestCase ):
45
+ """
46
+ Helper test class to perform common setup, teardown logic and assertion
47
+ checks for Docker flow tests.
48
+ """
44
49
45
- def setUp (self ) -> None :
46
- """
47
- Set up test environment and initialize all necessary mocks.
48
- """
49
- super ().setUp ()
50
+ @pytest .fixture (autouse = True )
51
+ def setup_teardown_test (self ) -> Generator :
52
+ self .set_up_test ()
53
+ yield
54
+ self .tear_down_test ()
55
+
56
+ def set_up_test (self ) -> None :
50
57
# Mock system calls.
51
58
self .system_patcher = umock .patch ("helpers.hsystem.system" )
52
59
self .mock_system = self .system_patcher .start ()
@@ -63,41 +70,78 @@ def setUp(self) -> None:
63
70
"helpers.lib_tasks_docker.docker_login"
64
71
)
65
72
self .mock_docker_login = self .docker_login_patcher .start ()
73
+ # Mock environment variable.
74
+ self .env_patcher = umock .patch .dict (
75
+ "os.environ" , {"CSFY_ECR_BASE_PATH" : "test.ecr.path" }
76
+ )
77
+ self .env_patcher .start ()
66
78
#
67
- self .user = hsystem .get_user_name ()
79
+ self .patchers = [
80
+ self .system_patcher ,
81
+ self .run_patcher ,
82
+ self .version_patcher ,
83
+ self .docker_login_patcher ,
84
+ self .env_patcher ,
85
+ ]
86
+ # Test inputs.
87
+ self .mock_ctx = httestlib ._build_mock_context_returning_ok ()
88
+ self .test_version = "1.0.0"
89
+ self .test_base_image = "test-registry.com/test-image"
90
+ self .test_multi_arch = "linux/amd64,linux/arm64"
68
91
69
- def tearDown (self ) -> None :
92
+ def tear_down_test (self ) -> None :
70
93
"""
71
94
Clean up test environment by stopping all mocks after each test case.
72
95
"""
73
- self .system_patcher .stop ()
74
- self .run_patcher .stop ()
75
- self .version_patcher .stop ()
76
- self .docker_login_patcher .stop ()
77
- super ().tearDown ()
96
+ for patcher in self .patchers :
97
+ patcher .stop ()
98
+
99
+ def _check_docker_command_output (
100
+ self , exp : str , call_args_list : List [umock ._Call ]
101
+ ) -> None :
102
+ """
103
+ Check that the sequence of commands from mock calls matches the
104
+ expected string.
105
+
106
+ :param exp: expected command string
107
+ :param call_args_list: list of mock call objects
108
+ """
109
+ actual_cmds = _extract_commands_from_call (call_args_list )
110
+ actual_cmds = "\n " .join (actual_cmds )
111
+ self .assert_equal (
112
+ actual_cmds ,
113
+ exp ,
114
+ purify_text = True ,
115
+ purify_expected_text = True ,
116
+ fuzzy_match = True ,
117
+ remove_lead_trail_empty_lines = True ,
118
+ dedent = True ,
119
+ )
78
120
79
- def test_docker_build_single_arch (self ) -> None :
121
+
122
+ # #############################################################################
123
+ # Test_docker_build_local_image1
124
+ # #############################################################################
125
+
126
+
127
+ class Test_docker_build_local_image1 (_DockerFlowTestHelper ):
128
+
129
+ def test_docker_build_local_image_single_arch (self ) -> None :
80
130
"""
81
131
Test building a local Docker image with single architecture.
82
132
83
133
This test verifies that the correct sequence of commands is
84
134
generated for building a local Docker image with single
85
135
architecture.
86
136
"""
87
- # Prepare inputs.
88
- mock_ctx = httestlib ._build_mock_context_returning_ok ()
89
- test_version = "1.0.0"
90
- test_base_image = "test-registry.com/test-image"
91
137
# Call tested function.
92
138
hltadore .docker_build_local_image (
93
- mock_ctx ,
94
- test_version ,
139
+ self . mock_ctx ,
140
+ self . test_version ,
95
141
cache = False ,
96
- base_image = test_base_image ,
142
+ base_image = self . test_base_image ,
97
143
poetry_mode = "update" ,
98
144
)
99
- actual_cmds = _extract_commands_from_call (self .mock_run .call_args_list )
100
- actual_cmds = "\n " .join (actual_cmds )
101
145
# The output is a list of strings, each representing a command.
102
146
exp = r"""
103
147
cp -f devops/docker_build/dockerignore.dev .dockerignore
@@ -114,41 +158,25 @@ def test_docker_build_single_arch(self) -> None:
114
158
cp -f pip_list.txt ./devops/docker_build/pip_list.txt
115
159
docker image ls test-registry.com/test-image:local-$USER_NAME-1.0.0
116
160
"""
117
- # Check output.
118
- self .assert_equal (
119
- actual_cmds ,
120
- exp ,
121
- purify_text = True ,
122
- fuzzy_match = True ,
123
- remove_lead_trail_empty_lines = True ,
124
- dedent = True ,
125
- )
161
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
126
162
127
- def test_docker_build_multi_arch (self ) -> None :
163
+ def test_docker_build_local_image_multi_arch (self ) -> None :
128
164
"""
129
165
Test building a local Docker image with multiple architectures.
130
166
131
167
This test verifies that the correct sequence of commands is
132
168
generated for building a local Docker image with multiple
133
169
architectures.
134
170
"""
135
- # Prepare inputs.
136
- mock_ctx = httestlib ._build_mock_context_returning_ok ()
137
- test_version = "1.0.0"
138
- test_base_image = "test-registry.com/test-image"
139
- test_multi_arch = "linux/amd64,linux/arm64"
140
171
# Call tested function.
141
172
hltadore .docker_build_local_image (
142
- mock_ctx ,
143
- test_version ,
173
+ self . mock_ctx ,
174
+ self . test_version ,
144
175
cache = False ,
145
- base_image = test_base_image ,
176
+ base_image = self . test_base_image ,
146
177
poetry_mode = "update" ,
147
- multi_arch = test_multi_arch ,
178
+ multi_arch = self . test_multi_arch ,
148
179
)
149
- actual_cmds = _extract_commands_from_call (self .mock_run .call_args_list )
150
- actual_cmds = "\n " .join (actual_cmds )
151
- # The output is a list of strings, each representing a command.
152
180
exp = r"""
153
181
cp -f devops/docker_build/dockerignore.dev .dockerignore
154
182
docker buildx create \
@@ -173,12 +201,148 @@ def test_docker_build_multi_arch(self) -> None:
173
201
cp -f pip_list.txt ./devops/docker_build/pip_list.txt
174
202
docker image ls test-registry.com/test-image:local-$USER_NAME-1.0.0
175
203
"""
176
- # Check output.
177
- self .assert_equal (
178
- actual_cmds ,
179
- exp ,
180
- purify_text = True ,
181
- fuzzy_match = True ,
182
- remove_lead_trail_empty_lines = True ,
183
- dedent = True ,
204
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
205
+
206
+
207
+ # #############################################################################
208
+ # Test_docker_build_prod_image1
209
+ # #############################################################################
210
+
211
+
212
+ class Test_docker_build_prod_image1 (_DockerFlowTestHelper ):
213
+
214
+ def test_docker_build_prod_image (self ) -> None :
215
+ """
216
+ Test building a prod Docker image with single architecture.
217
+
218
+ This test verifies that the correct sequence of commands is
219
+ generated for building a prod Docker image.
220
+ """
221
+ # Call tested function.
222
+ hltadore .docker_build_prod_image (
223
+ self .mock_ctx ,
224
+ self .test_version ,
225
+ base_image = self .test_base_image ,
226
+ cache = False ,
227
+ )
228
+ exp = r"""
229
+ cp -f devops/docker_build/dockerignore.prod .dockerignore
230
+ DOCKER_BUILDKIT=0 \
231
+ time \
232
+ docker build \
233
+ --no-cache \
234
+ --tag test-registry.com/test-image:prod-1.0.0 \
235
+ --file /app/devops/docker_build/prod.Dockerfile \
236
+ --build-arg VERSION=1.0.0 \
237
+ --build-arg ECR_BASE_PATH=test.ecr.path \
238
+ .
239
+ docker tag test-registry.com/test-image:prod-1.0.0 test-registry.com/test-image:prod
240
+ docker image ls test-registry.com/test-image:prod
241
+ """
242
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
243
+
244
+ def test_docker_build_multi_arch_prod_image (self ) -> None :
245
+ """
246
+ Test building a prod Docker image with multiple architectures.
247
+
248
+ This test verifies that the correct sequence of commands is
249
+ generated for building a multi-arch prod Docker image.
250
+ """
251
+ # Call tested function.
252
+ hltadore .docker_build_multi_arch_prod_image (
253
+ self .mock_ctx ,
254
+ self .test_version ,
255
+ base_image = self .test_base_image ,
256
+ cache = False ,
257
+ multi_arch = self .test_multi_arch ,
258
+ )
259
+ exp = r"""
260
+ cp -f devops/docker_build/dockerignore.prod .dockerignore
261
+ docker buildx create \
262
+ --name multiarch_builder \
263
+ --driver docker-container \
264
+ --bootstrap \
265
+ && \
266
+ docker buildx use multiarch_builder
267
+ tar -czh . | DOCKER_BUILDKIT=0 \
268
+ time \
269
+ docker buildx build \
270
+ --no-cache \
271
+ --push \
272
+ --platform linux/amd64,linux/arm64 \
273
+ --build-arg VERSION=1.0.0 --build-arg ECR_BASE_PATH=test.ecr.path \
274
+ --tag test-registry.com/test-image:prod-1.0.0 \
275
+ --file devops/docker_build/prod.Dockerfile \
276
+ -
277
+ docker pull test-registry.com/test-image:prod-1.0.0
278
+ docker image ls test-registry.com/test-image:prod-1.0.0
279
+ """
280
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
281
+
282
+ def test_docker_build_prod_image_with_candidate_tag (self ) -> None :
283
+ """
284
+ Test building a prod Docker image with candidate mode using tag.
285
+
286
+ This test verifies that the correct sequence of commands is
287
+ generated for building a prod image with candidate mode using
288
+ tag.
289
+ """
290
+ test_tag = "test_tag"
291
+ # Call tested function.
292
+ hltadore .docker_build_prod_image (
293
+ self .mock_ctx ,
294
+ self .test_version ,
295
+ base_image = self .test_base_image ,
296
+ cache = False ,
297
+ candidate = True ,
298
+ tag = test_tag ,
299
+ )
300
+ exp = r"""
301
+ cp -f devops/docker_build/dockerignore.prod .dockerignore
302
+ DOCKER_BUILDKIT=0 \
303
+ time \
304
+ docker build \
305
+ --no-cache \
306
+ --tag test-registry.com/test-image:prod-test_tag \
307
+ --file /app/devops/docker_build/prod.Dockerfile \
308
+ --build-arg VERSION=1.0.0 \
309
+ --build-arg ECR_BASE_PATH=test.ecr.path \
310
+ .
311
+ docker image ls test-registry.com/test-image:prod-test_tag
312
+ """
313
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
314
+
315
+ def test_docker_build_prod_image_with_candidate_user_tag (self ) -> None :
316
+ """
317
+ Test building a prod Docker image with candidate mode using user tag.
318
+
319
+ This test verifies that the correct sequence of commands is
320
+ generated for building a prod image with candidate mode using
321
+ user tag and tag.
322
+ """
323
+ test_user_tag = "test_user"
324
+ test_tag = "test_tag"
325
+ # Call tested function.
326
+ hltadore .docker_build_prod_image (
327
+ self .mock_ctx ,
328
+ self .test_version ,
329
+ base_image = self .test_base_image ,
330
+ cache = False ,
331
+ candidate = True ,
332
+ user_tag = test_user_tag ,
333
+ tag = test_tag ,
184
334
)
335
+ exp = r"""
336
+ cp -f devops/docker_build/dockerignore.prod .dockerignore
337
+ DOCKER_BUILDKIT=0 \
338
+ time \
339
+ docker build \
340
+ --no-cache \
341
+ --tag test-registry.com/test-image:prod-test_user-test_tag \
342
+ --file /app/devops/docker_build/prod.Dockerfile \
343
+ --build-arg VERSION=1.0.0 \
344
+ --build-arg ECR_BASE_PATH=test.ecr.path \
345
+ .
346
+ docker image ls test-registry.com/test-image:prod-test_user-test_tag
347
+ """
348
+ self ._check_docker_command_output (exp , self .mock_run .call_args_list )
0 commit comments