44import com .salesforce .multicloudj .common .exceptions .SubstrateSdkException ;
55import com .salesforce .multicloudj .common .exceptions .UnAuthorizedException ;
66import com .salesforce .multicloudj .common .exceptions .UnknownException ;
7+ import org .apache .http .HttpRequestInterceptor ;
78import org .junit .jupiter .api .Test ;
89import org .junit .jupiter .params .ParameterizedTest ;
910import org .junit .jupiter .params .provider .MethodSource ;
1920import java .time .Instant ;
2021import java .util .Base64 ;
2122import java .util .Collections ;
23+ import java .util .List ;
2224import java .util .stream .Stream ;
2325
2426import static org .junit .jupiter .api .Assertions .assertEquals ;
27+ import static org .junit .jupiter .api .Assertions .assertFalse ;
28+ import static org .junit .jupiter .api .Assertions .assertInstanceOf ;
2529import static org .junit .jupiter .api .Assertions .assertNotNull ;
30+ import static org .junit .jupiter .api .Assertions .assertNull ;
2631import static org .junit .jupiter .api .Assertions .assertThrows ;
2732import static org .junit .jupiter .params .provider .Arguments .arguments ;
2833import static org .mockito .ArgumentMatchers .any ;
@@ -54,7 +59,6 @@ private AwsRegistry createRegistryWithMockEcrClient(EcrClient mockEcrClient) {
5459 AwsRegistry .Builder builder = new AwsRegistry .Builder ()
5560 .withRegion (TEST_REGION )
5661 .withRegistryEndpoint (TEST_REGISTRY_ENDPOINT );
57- builder .providerId (PROVIDER_ID );
5862 return new AwsRegistry (builder , mockEcrClient );
5963 }
6064
@@ -81,16 +85,6 @@ private static AwsServiceException awsException(String errorCode) {
8185 .build ();
8286 }
8387
84- static Stream <org .junit .jupiter .params .provider .Arguments > exceptionMappingProvider () { // NOSONAR - needed for @MethodSource resolution
85- return Stream .of (
86- arguments (new SubstrateSdkException ("test" ), SubstrateSdkException .class ),
87- arguments (awsException (AWS_ERROR_CODE_ACCESS_DENIED ), UnAuthorizedException .class ),
88- arguments (awsException (AWS_ERROR_CODE_UNAVAILABLE ), UnknownException .class ),
89- arguments (new IllegalArgumentException ("invalid" ), InvalidArgumentException .class ),
90- arguments (new RuntimeException ("unknown" ), UnknownException .class )
91- );
92- }
93-
9488 private void withMockedRegistry (RegistryTestAction action ) throws Exception {
9589 EcrClient mockEcrClient = mock (EcrClient .class );
9690 try (AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient )) {
@@ -99,62 +93,78 @@ private void withMockedRegistry(RegistryTestAction action) throws Exception {
9993 }
10094
10195 @ Test
102- void testBuilderAndBasicProperties () throws Exception {
96+ void testNoArgConstructor_CreatesInstanceWithDefaultBuilder () {
97+ AwsRegistry registry = new AwsRegistry ();
98+ assertNotNull (registry );
99+ assertNull (registry .getOciClient ());
100+ }
101+
102+ @ Test
103+ void testConstructor_WithBuilder_InitialisesFields () throws Exception {
103104 withMockedRegistry (registry -> {
104- assertNotNull (registry );
105105 assertEquals (PROVIDER_ID , registry .getProviderId ());
106106 assertEquals (AUTH_USERNAME , registry .getAuthUsername ());
107- assertNotNull (registry .builder ());
107+ assertNotNull (registry .getOciClient ());
108108 });
109109 }
110110
111+ @ Test
112+ void testBuilder_InstanceMethod_ReturnsNewBuilder () throws Exception {
113+ withMockedRegistry (registry -> assertNotNull (registry .builder ()));
114+ }
115+
116+ @ Test
117+ void testBuilder_WithRegion_GetRegion_RoundTrip () {
118+ AwsRegistry .Builder builder = new AwsRegistry .Builder ().withRegion (TEST_REGION );
119+ assertEquals (TEST_REGION , builder .getRegion ());
120+ }
121+
111122 @ ParameterizedTest
112123 @ NullAndEmptySource
113124 @ ValueSource (strings = {" " })
114- void testBuilder_InvalidRegion_ThrowsException (String region ) {
125+ void testBuilder_MissingEndpoint_ThrowsInvalidArgumentException (String endpoint ) {
115126 assertThrows (InvalidArgumentException .class , () ->
116- new AwsRegistry .Builder ()
117- .withRegion (region )
118- .withRegistryEndpoint (TEST_REGISTRY_ENDPOINT )
119- .build ()
120- );
127+ new AwsRegistry .Builder ()
128+ .withRegion (TEST_REGION )
129+ .withRegistryEndpoint (endpoint )
130+ .build ());
121131 }
122132
123- @ Test
124- void testGetAuthToken_EmptyAuthorizationData_ThrowsUnknownException () throws Exception {
125- EcrClient mockEcrClient = mock (EcrClient .class );
126- GetAuthorizationTokenResponse response = GetAuthorizationTokenResponse .builder ()
127- .authorizationData (Collections .emptyList ())
128- .build ();
129- when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class ))).thenReturn (response );
133+ @ ParameterizedTest
134+ @ NullAndEmptySource
135+ @ ValueSource (strings = {" " })
136+ void testBuilder_MissingRegion_ThrowsInvalidArgumentException (String region ) {
137+ assertThrows (InvalidArgumentException .class , () ->
138+ new AwsRegistry .Builder ()
139+ .withRegion (region )
140+ .withRegistryEndpoint (TEST_REGISTRY_ENDPOINT )
141+ .build ());
142+ }
130143
131- try (AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient )) {
132- UnknownException exception = assertThrows (UnknownException .class , registry ::getAuthToken );
133- assertEquals (ERR_EMPTY_AUTH_DATA , exception .getMessage ());
134- }
144+ @ Test
145+ void testGetInterceptors_ReturnsAuthStrippingInterceptor () throws Exception {
146+ withMockedRegistry (registry -> {
147+ List <HttpRequestInterceptor > interceptors = registry .getInterceptors ();
148+ assertFalse (interceptors .isEmpty ());
149+ assertInstanceOf (AuthStrippingInterceptor .class , interceptors .get (0 ));
150+ });
135151 }
136152
137153 @ Test
138- void testGetAuthToken_InvalidTokenFormat_ThrowsUnknownException () throws Exception {
139- EcrClient mockEcrClient = mock (EcrClient .class );
140- AuthorizationData authData = AuthorizationData .builder ()
141- .authorizationToken (Base64 .getEncoder ().encodeToString ("invalidtoken" .getBytes ()))
142- .expiresAt (Instant .now ().plusSeconds (TOKEN_VALIDITY_SECONDS ))
143- .build ();
144- when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
145- .thenReturn (tokenResponse (authData ));
154+ void testGetOciClient_ReturnsNonNull_WhenEndpointProvided () throws Exception {
155+ withMockedRegistry (registry -> assertNotNull (registry .getOciClient ()));
156+ }
146157
147- try ( AwsRegistry registry = createRegistryWithMockEcrClient ( mockEcrClient )) {
148- UnknownException exception = assertThrows ( UnknownException . class , registry :: getAuthToken );
149- assertEquals ( ERR_INVALID_TOKEN_FORMAT , exception . getMessage () );
150- }
158+ @ Test
159+ void testGetOciClient_ReturnsNull_WhenNoEndpoint () {
160+ AwsRegistry registry = new AwsRegistry ( );
161+ assertNull ( registry . getOciClient ());
151162 }
152163
153164 @ Test
154165 void testGetAuthToken_TokenCachedWithinHalfwayWindow_NoRefresh () throws Exception {
155166 String expectedToken = "cached-token" ;
156167 EcrClient mockEcrClient = mock (EcrClient .class );
157- // Token expires 12 hours from now — halfway point is 6 hours from now, so no refresh expected
158168 when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
159169 .thenReturn (tokenResponse (authDataWithExpiry (expectedToken , Instant .now ().plusSeconds (TOKEN_VALIDITY_SECONDS ))));
160170
@@ -171,7 +181,6 @@ void testGetAuthToken_TokenPastHalfwayPoint_Refreshes() throws Exception {
171181 String refreshedToken = "refreshed-token" ;
172182 EcrClient mockEcrClient = mock (EcrClient .class );
173183 when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
174- // Token already past halfway point (expired 1 second ago)
175184 .thenReturn (tokenResponse (authDataWithExpiry (firstToken , Instant .now ().minusSeconds (1 ))))
176185 .thenReturn (tokenResponse (authDataWithExpiry (refreshedToken , Instant .now ().plusSeconds (TOKEN_VALIDITY_SECONDS ))));
177186
@@ -182,12 +191,39 @@ void testGetAuthToken_TokenPastHalfwayPoint_Refreshes() throws Exception {
182191 }
183192 }
184193
194+ @ Test
195+ void testGetAuthToken_EmptyAuthorizationData_ThrowsUnknownException () throws Exception {
196+ EcrClient mockEcrClient = mock (EcrClient .class );
197+ when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
198+ .thenReturn (GetAuthorizationTokenResponse .builder ().authorizationData (Collections .emptyList ()).build ());
199+
200+ try (AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient )) {
201+ UnknownException ex = assertThrows (UnknownException .class , registry ::getAuthToken );
202+ assertEquals (ERR_EMPTY_AUTH_DATA , ex .getMessage ());
203+ }
204+ }
205+
206+ @ Test
207+ void testGetAuthToken_InvalidTokenFormat_ThrowsUnknownException () throws Exception {
208+ EcrClient mockEcrClient = mock (EcrClient .class );
209+ AuthorizationData authData = AuthorizationData .builder ()
210+ .authorizationToken (Base64 .getEncoder ().encodeToString ("invalidtoken" .getBytes ()))
211+ .expiresAt (Instant .now ().plusSeconds (TOKEN_VALIDITY_SECONDS ))
212+ .build ();
213+ when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
214+ .thenReturn (tokenResponse (authData ));
215+
216+ try (AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient )) {
217+ UnknownException ex = assertThrows (UnknownException .class , registry ::getAuthToken );
218+ assertEquals (ERR_INVALID_TOKEN_FORMAT , ex .getMessage ());
219+ }
220+ }
221+
185222 @ Test
186223 void testGetAuthToken_RefreshFails_FallsBackToCachedToken () throws Exception {
187224 String cachedToken = "still-valid-token" ;
188225 EcrClient mockEcrClient = mock (EcrClient .class );
189226 when (mockEcrClient .getAuthorizationToken (any (GetAuthorizationTokenRequest .class )))
190- // First call primes cache with past-halfway token, second call simulates transient failure
191227 .thenReturn (tokenResponse (authDataWithExpiry (cachedToken , Instant .now ().minusSeconds (1 ))))
192228 .thenThrow (awsException (AWS_ERROR_CODE_UNAVAILABLE ));
193229
@@ -204,11 +240,35 @@ void testGetAuthToken_RefreshFails_NoCachedToken_ThrowsUnknownException() throws
204240 .thenThrow (awsException (AWS_ERROR_CODE_UNAVAILABLE ));
205241
206242 try (AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient )) {
207- UnknownException exception = assertThrows (UnknownException .class , registry ::getAuthToken );
208- assertEquals (ERR_FAILED_AUTH_TOKEN , exception .getMessage ());
243+ UnknownException ex = assertThrows (UnknownException .class , registry ::getAuthToken );
244+ assertEquals (ERR_FAILED_AUTH_TOKEN , ex .getMessage ());
209245 }
210246 }
211247
248+ @ Test
249+ void testClose_WithOciAndEcrClient_ClosesAll () throws Exception {
250+ EcrClient mockEcrClient = mock (EcrClient .class );
251+ AwsRegistry registry = createRegistryWithMockEcrClient (mockEcrClient );
252+ registry .close ();
253+ verify (mockEcrClient ).close ();
254+ }
255+
256+ @ Test
257+ void testClose_WithNullEcrClient_NoError () throws Exception {
258+ AwsRegistry registry = new AwsRegistry ();
259+ registry .close (); // should not throw
260+ }
261+
262+ static Stream <org .junit .jupiter .params .provider .Arguments > exceptionMappingProvider () { // NOSONAR - needed for @MethodSource resolution
263+ return Stream .of (
264+ arguments (new SubstrateSdkException ("test" ), SubstrateSdkException .class ),
265+ arguments (awsException (AWS_ERROR_CODE_ACCESS_DENIED ), UnAuthorizedException .class ),
266+ arguments (awsException (AWS_ERROR_CODE_UNAVAILABLE ), UnknownException .class ),
267+ arguments (new IllegalArgumentException ("invalid" ), InvalidArgumentException .class ),
268+ arguments (new RuntimeException ("unknown" ), UnknownException .class )
269+ );
270+ }
271+
212272 @ ParameterizedTest
213273 @ MethodSource ("exceptionMappingProvider" )
214274 void testGetException (Throwable input , Class <? extends SubstrateSdkException > expected ) throws Exception {
0 commit comments