Skip to content

Commit 266d5f1

Browse files
authored
GCP pubsub conformance tests (#76)
1 parent e9ceeac commit 266d5f1

File tree

63 files changed

+2565
-1196
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2565
-1196
lines changed

blob/blob-gcp/src/main/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStore.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.salesforce.multicloudj.common.exceptions.UnknownException;
4343
import com.salesforce.multicloudj.common.gcp.CommonErrorCodeMapping;
4444
import com.salesforce.multicloudj.common.gcp.GcpConstants;
45+
import com.salesforce.multicloudj.common.gcp.GcpCredentialsProvider;
4546
import com.salesforce.multicloudj.common.provider.Provider;
4647
import lombok.Getter;
4748
import org.apache.http.HttpHost;

blob/blob-gcp/src/main/java/com/salesforce/multicloudj/blob/gcp/GcpCredentialsProvider.java renamed to multicloudj-common-gcp/src/main/java/com/salesforce/multicloudj/common/gcp/GcpCredentialsProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.salesforce.multicloudj.blob.gcp;
1+
package com.salesforce.multicloudj.common.gcp;
22

33
import com.google.auth.Credentials;
44
import com.google.auth.oauth2.AccessToken;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.salesforce.multicloudj.common.gcp;
2+
3+
import static org.junit.jupiter.api.Assertions.assertNotNull;
4+
import static org.junit.jupiter.api.Assertions.assertNull;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import com.google.auth.Credentials;
9+
import com.google.auth.oauth2.GoogleCredentials;
10+
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
11+
import com.salesforce.multicloudj.sts.model.CredentialsType;
12+
import com.salesforce.multicloudj.sts.model.StsCredentials;
13+
import org.junit.jupiter.api.Test;
14+
15+
public class GcpCredentialsProviderTest {
16+
17+
@Test
18+
void testGetCredentialsWithNullOverrider() {
19+
Credentials credentials = GcpCredentialsProvider.getCredentials(null);
20+
assertNull(credentials, "Credentials should be null when overrider is null");
21+
}
22+
23+
@Test
24+
void testGetCredentialsWithNullType() {
25+
CredentialsOverrider overrider = new CredentialsOverrider.Builder(null).build();
26+
Credentials credentials = GcpCredentialsProvider.getCredentials(overrider);
27+
assertNull(credentials, "Credentials should be null when type is null");
28+
}
29+
30+
@Test
31+
void testGetCredentialsWithSessionType() {
32+
// Arrange
33+
String testToken = "test-security-token-12345";
34+
StsCredentials stsCredentials = new StsCredentials(
35+
"test-access-key-id",
36+
"test-access-key-secret",
37+
testToken
38+
);
39+
40+
CredentialsOverrider overrider = new CredentialsOverrider.Builder(CredentialsType.SESSION)
41+
.withSessionCredentials(stsCredentials)
42+
.build();
43+
44+
// Act
45+
Credentials credentials = GcpCredentialsProvider.getCredentials(overrider);
46+
47+
// Assert
48+
assertNotNull(credentials, "Credentials should not be null");
49+
assertTrue(credentials instanceof GoogleCredentials,
50+
"Credentials should be GoogleCredentials");
51+
}
52+
53+
@Test
54+
void testGetCredentialsWithSessionTypeAndNullSessionCredentials() {
55+
// Arrange
56+
CredentialsOverrider overrider = new CredentialsOverrider.Builder(CredentialsType.SESSION)
57+
.build();
58+
59+
// Act & Assert - Should throw NullPointerException when sessionCredentials is null
60+
assertThrows(NullPointerException.class, () -> {
61+
GcpCredentialsProvider.getCredentials(overrider);
62+
}, "NullPointerException expected when sessionCredentials is null");
63+
}
64+
}
65+
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
package com.salesforce.multicloudj.sts.gcp;
1+
package com.salesforce.multicloudj.common.gcp.util;
22

33
import com.google.auth.oauth2.AccessToken;
44
import com.google.auth.oauth2.GoogleCredentials;
55

66
import java.util.Date;
77

88
/**
9-
* Factory for creating mock {@link GoogleCredentials} instances that can be used in
10-
* playback-mode tests where no real Application Default Credentials (ADC) exist.
9+
* Factory for creating mock {@link GoogleCredentials} instances for playback-mode tests.
10+
* The credentials override {@code refreshAccessToken()} to return a static long-lived token,
11+
* preventing external calls during test playback.
1112
*/
1213
public final class MockGoogleCredentialsFactory {
1314

@@ -16,21 +17,13 @@ private MockGoogleCredentialsFactory() {
1617
}
1718

1819
/**
19-
* Returns a simple {@link GoogleCredentials} object backed by a fixed, non-expiring
20-
* {@link AccessToken}.
20+
* Creates mock credentials with a long-lived access token for testing.
2121
*/
2222
public static GoogleCredentials createMockCredentials() {
2323
// 100 years in the future
2424
Date futureDate = new Date(System.currentTimeMillis() + 100L * 365 * 24 * 60 * 60 * 1000);
2525
AccessToken token = new AccessToken("mock-gcp-oauth2-token", futureDate);
2626

27-
/*
28-
* GoogleCredentials.create(AccessToken) returns an immutable credential that cannot
29-
* refresh – calling refreshAccessToken() throws an IllegalStateException. The client
30-
* libraries used by GcpStsIT will call refreshAccessToken() even when we have already
31-
* supplied a long-lived token, so here we provide a small subclass that simply returns
32-
* the same static AccessToken every time it’s asked to refresh.
33-
*/
3427
return new GoogleCredentials() {
3528
/** cached static token – never expires within the 100-year window */
3629
private final AccessToken staticToken = token;
@@ -42,4 +35,5 @@ public AccessToken refreshAccessToken() {
4235
}
4336
};
4437
}
45-
}
38+
}
39+

multicloudj-common/src/test/java/com/salesforce/multicloudj/common/util/common/TestsUtil.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.github.tomakehurst.wiremock.extension.Parameters;
88
import com.github.tomakehurst.wiremock.extension.StubMappingTransformer;
99
import com.github.tomakehurst.wiremock.matching.ContentPattern;
10+
import com.github.tomakehurst.wiremock.matching.MatchesJsonPathPattern;
1011
import com.github.tomakehurst.wiremock.matching.RegexPattern;
1112
import com.github.tomakehurst.wiremock.matching.RequestPattern;
1213
import com.github.tomakehurst.wiremock.recording.RecordSpecBuilder;
@@ -25,6 +26,7 @@ public class TestsUtil {
2526
private static Logger logger = LoggerFactory.getLogger(TestsUtil.class);
2627
static WireMockServer wireMockServer;
2728
public static final String WIREMOCK_HOST = "localhost";
29+
private static List<StubMappingTransformer> loadedTransformers = new ArrayList<>();
2830

2931
public static class TruncateRequestBodyTransformer extends StubMappingTransformer {
3032

@@ -69,6 +71,23 @@ public String getName() {
6971
public static void startWireMockServer(String rootDir, int port, String... extensionInstances) {
7072
boolean isRecordingEnabled = System.getProperty("record") != null;
7173
logger.info("Recording enabled: {}", isRecordingEnabled);
74+
75+
// Create extensions list with default transformer
76+
List<StubMappingTransformer> extensions = new ArrayList<>();
77+
extensions.add(new TruncateRequestBodyTransformer());
78+
79+
// Load additional extensions if provided
80+
for (String extensionClass : extensionInstances) {
81+
try {
82+
Class<?> clazz = Class.forName(extensionClass);
83+
StubMappingTransformer transformer = (StubMappingTransformer) clazz.getDeclaredConstructor().newInstance();
84+
extensions.add(transformer);
85+
logger.info("Loaded WireMock extension: {}", extensionClass);
86+
} catch (Exception e) {
87+
logger.warn("Failed to load WireMock extension: {}", extensionClass, e);
88+
}
89+
}
90+
7291
wireMockServer = new WireMockServer(WireMockConfiguration.options()
7392
.httpsPort(port)
7493
.port(port+1) // http port
@@ -78,7 +97,7 @@ public static void startWireMockServer(String rootDir, int port, String... exten
7897
.gzipDisabled(true)
7998
.useChunkedTransferEncoding(Options.ChunkedEncodingPolicy.NEVER)
8099
.filenameTemplate("{{request.method}}-{{randomValue length=10}}.json")
81-
.extensions(new TruncateRequestBodyTransformer())
100+
.extensions(extensions.toArray(new StubMappingTransformer[0]))
82101
.enableBrowserProxying(true));
83102
wireMockServer.start();
84103
}
@@ -97,6 +116,15 @@ public static void startWireMockRecording(String targetEndpoint) {
97116
.transformerParameters(Parameters.from(Map.of(TRUNCATE_MATCHER_REQUST_BODY_OVER,4096*2)))
98117
.chooseBodyMatchTypeAutomatically(true, false, false)
99118
.makeStubsPersistent(true);
119+
120+
// Apply transformers during recording
121+
if (!loadedTransformers.isEmpty()) {
122+
String[] transformerNames = loadedTransformers.stream()
123+
.map(StubMappingTransformer::getName)
124+
.toArray(String[]::new);
125+
recordSpec = recordSpec.transformers(transformerNames);
126+
}
127+
100128
if (isRecordingEnabled) {
101129
wireMockServer.startRecording(recordSpec);
102130
}

pubsub/pubsub-aws/src/java/com/salesforce/multicloudj/pubsub/aws/AwsSubscription.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package com.salesforce.multicloudj.pubsub.aws;
22

3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.concurrent.CompletableFuture;
7+
38
import com.salesforce.multicloudj.common.aws.AwsConstants;
49
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
10+
import com.salesforce.multicloudj.pubsub.batcher.Batcher;
511
import com.salesforce.multicloudj.pubsub.driver.AbstractSubscription;
612
import com.salesforce.multicloudj.pubsub.driver.AckID;
713
import com.salesforce.multicloudj.pubsub.driver.Message;
814

9-
import java.util.Collections;
10-
import java.util.List;
11-
import java.util.Map;
12-
import java.util.concurrent.CompletableFuture;
13-
1415
public class AwsSubscription extends AbstractSubscription<AwsSubscription> {
1516

1617
public AwsSubscription() {
@@ -40,12 +41,6 @@ protected void doSendNacks(List<AckID> ackIDs) {
4041
// TODO: Implement AWS SQS negative acknowledgment
4142
}
4243

43-
@Override
44-
protected String getMessageId(AckID ackID) {
45-
// TODO: Implement AWS-specific message ID extraction
46-
return ackID != null ? ackID.toString() : null;
47-
}
48-
4944
@Override
5045
public void sendNack(AckID ackID) {
5146
}
@@ -85,6 +80,12 @@ protected List<Message> doReceiveBatch(int batchSize) {
8580
return Collections.emptyList();
8681
}
8782

83+
@Override
84+
protected Batcher.Options createAckBatcherOptions() {
85+
// AWS implementation not yet complete
86+
throw new UnsupportedOperationException("AWS PubSub implementation not yet complete");
87+
}
88+
8889
public Builder builder() {
8990
return new Builder();
9091
}

pubsub/pubsub-client/src/main/java/com/salesforce/multicloudj/pubsub/client/ProviderSupplier.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package com.salesforce.multicloudj.pubsub.client;
22

3-
import com.salesforce.multicloudj.pubsub.driver.AbstractTopic;
4-
import com.salesforce.multicloudj.pubsub.driver.AbstractSubscription;
5-
63
import java.util.ArrayList;
74
import java.util.List;
85
import java.util.ServiceLoader;
96

7+
import com.salesforce.multicloudj.pubsub.driver.AbstractTopic;
8+
import com.salesforce.multicloudj.pubsub.driver.AbstractSubscription;
9+
1010
/**
1111
* This class will attempt to find a PubSub provider in the classpath by providerId.
1212
*

pubsub/pubsub-client/src/main/java/com/salesforce/multicloudj/pubsub/client/SubscriptionClient.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package com.salesforce.multicloudj.pubsub.client;
22

3+
import java.net.URI;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.concurrent.CompletableFuture;
7+
38
import com.salesforce.multicloudj.common.exceptions.ExceptionHandler;
49
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
510
import com.salesforce.multicloudj.pubsub.driver.AbstractSubscription;
611
import com.salesforce.multicloudj.pubsub.driver.AckID;
712
import com.salesforce.multicloudj.pubsub.driver.Message;
813
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
914

10-
import java.net.URI;
11-
import java.time.Duration;
12-
import java.util.List;
13-
import java.util.Map;
14-
import java.util.concurrent.CompletableFuture;
15-
1615
/**
1716
* High-level client for receiving messages from a pubsub subscription.
1817
*

pubsub/pubsub-client/src/main/java/com/salesforce/multicloudj/pubsub/client/TopicClient.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.salesforce.multicloudj.pubsub.client;
22

3+
import java.net.URI;
4+
35
import com.salesforce.multicloudj.common.exceptions.ExceptionHandler;
46
import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException;
5-
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
67
import com.salesforce.multicloudj.pubsub.driver.AbstractTopic;
78
import com.salesforce.multicloudj.pubsub.driver.Message;
8-
9-
import java.net.URI;
10-
import java.util.List;
11-
import java.util.concurrent.CompletableFuture;
9+
import com.salesforce.multicloudj.sts.model.CredentialsOverrider;
1210

1311
/**
1412
* High-level client for publishing messages to a pubsub topic.

0 commit comments

Comments
 (0)