diff --git a/blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsBlobStoreIT.java b/blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsBlobStoreIT.java
index 61e247c29..9b6beb6b5 100644
--- a/blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsBlobStoreIT.java
+++ b/blob/blob-aws/src/test/java/com/salesforce/multicloudj/blob/aws/AwsBlobStoreIT.java
@@ -57,6 +57,11 @@ public AbstractBlobStore createBlobStore(boolean useValidBucket, boolean useVali
return createBlobStore(bucketNameToUse, credentialsOverrider);
}
+ @Override
+ public boolean isObjectLockSupported() {
+ return true;
+ }
+
private AbstractBlobStore createBlobStore(final String bucketName, final CredentialsOverrider credentialsOverrider) {
AwsSessionCredentials awsCredentials = AwsSessionCredentials.create(
diff --git a/blob/blob-aws/src/test/resources/mappings/delete-5wi9bl80gj.json b/blob/blob-aws/src/test/resources/mappings/delete-5wi9bl80gj.json
new file mode 100644
index 000000000..78ba8d3ec
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/delete-5wi9bl80gj.json
@@ -0,0 +1,22 @@
+{
+ "id" : "9128ed99-d86c-4570-a55e-8ebc755dd931",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-compliance",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-delete-marker" : "true",
+ "x-amz-request-id" : "C23ABJCWFSRXW4K0",
+ "x-amz-id-2" : "MLoEc2sWyXmlIIhbwmOi+5CHcWSSZeb60O9zSrCBzSMEhMN6kmrl8/vy4iGCBvA6sRAkyujJ4zh8sptutw0ya7MRZj8aH/Kd",
+ "x-amz-version-id" : "0RgbnwcYl_V5zHPmusuDFt8VFbd3AupL",
+ "Date" : "Fri, 06 Feb 2026 20:00:16 GMT"
+ }
+ },
+ "uuid" : "9128ed99-d86c-4570-a55e-8ebc755dd931",
+ "persistent" : true,
+ "insertionIndex" : 614
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/delete-vkfpscvsg1.json b/blob/blob-aws/src/test/resources/mappings/delete-vkfpscvsg1.json
new file mode 100644
index 000000000..08617e2a0
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/delete-vkfpscvsg1.json
@@ -0,0 +1,22 @@
+{
+ "id" : "7198d55b-c61b-4ecb-a4e0-4fd6226a4add",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-governance",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-delete-marker" : "true",
+ "x-amz-request-id" : "1YEXQ12C579RW0KQ",
+ "x-amz-id-2" : "qyXEwb1T0zhsflhscoSkSYHfouB8AmoZdd65/TvtY2JTOW59XBWF0Q0JesbKQ5q3surPipEb9dNJ8Oiqh7cMlSV4G16Ffp+j",
+ "x-amz-version-id" : "XIyxjBTeuMrVLlQhZyBknIysvYH2fSGK",
+ "Date" : "Fri, 06 Feb 2026 20:00:19 GMT"
+ }
+ },
+ "uuid" : "7198d55b-c61b-4ecb-a4e0-4fd6226a4add",
+ "persistent" : true,
+ "insertionIndex" : 619
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/delete-zc3job12kj.json b/blob/blob-aws/src/test/resources/mappings/delete-zc3job12kj.json
new file mode 100644
index 000000000..d95fa9e15
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/delete-zc3job12kj.json
@@ -0,0 +1,22 @@
+{
+ "id" : "aa2145d4-1c1b-423d-924f-057d98ebbe16",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/no-lock",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-delete-marker" : "true",
+ "x-amz-request-id" : "E2QV4DT8PTVMRM4G",
+ "x-amz-id-2" : "jiiJxUakuDURxGBT51SfcjtaoO1IQDnPpJ74FDNrrIuUpoqRzfZ0UhW2oJMHoKq+vkd9+S779ozwgEHfaBcTMDZ4WolgiERE",
+ "x-amz-version-id" : "lMcN19fE1JZNwdtTafPoKlg90sChfKhN",
+ "Date" : "Fri, 06 Feb 2026 20:00:12 GMT"
+ }
+ },
+ "uuid" : "aa2145d4-1c1b-423d-924f-057d98ebbe16",
+ "persistent" : true,
+ "insertionIndex" : 608
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-6wbl1f0j9t.json b/blob/blob-aws/src/test/resources/mappings/get-6wbl1f0j9t.json
new file mode 100644
index 000000000..078e55a37
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-6wbl1f0j9t.json
@@ -0,0 +1,21 @@
+{
+ "id" : "611524a6-323a-4db2-876f-8ba87d7b18ef",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-compliance?retention",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "base64Body" : "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPFJldGVudGlvbiB4bWxucz0iaHR0cDovL3MzLmFtYXpvbmF3cy5jb20vZG9jLzIwMDYtMDMtMDEvIj48TW9kZT5DT01QTElBTkNFPC9Nb2RlPjxSZXRhaW5VbnRpbERhdGU+MjAyNi0wMi0wN1QyMDowMDoxMS45MTZaPC9SZXRhaW5VbnRpbERhdGU+PC9SZXRlbnRpb24+",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "7Y29EQQXX4YRQ62V",
+ "x-amz-id-2" : "SV0qkewBtQqyCSx7ItCA1I3gQ2TtIDuXE/hcEDQL/634+ZKIfbTszIcvvZbCdfzKhl3YtJHiNkStg4irE1AVP3p2ASZAKBTh",
+ "Date" : "Fri, 06 Feb 2026 20:00:13 GMT"
+ }
+ },
+ "uuid" : "611524a6-323a-4db2-876f-8ba87d7b18ef",
+ "persistent" : true,
+ "insertionIndex" : 616
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-dtkyrojxp0.json b/blob/blob-aws/src/test/resources/mappings/get-dtkyrojxp0.json
new file mode 100644
index 000000000..0931dca5a
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-dtkyrojxp0.json
@@ -0,0 +1,22 @@
+{
+ "id" : "6dcec650-0822-426b-9c7e-627c19a53f79",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_nonexistent",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/nonexistent?retention",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 404,
+ "body" : "\nNoSuchKeyThe specified key does not exist.conformance-tests/objectlock/nonexistentE2QS9BPQM508ZYTPTfte+PiDMU55Rd3ptoQW0bgwDAmEnkTpJkaAOmmag6CIRi+xPOE5w3F8ZtihAHOjhbGe8NQMP5v6Re9miaKhzwCubJcQh/Bw",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "E2QS9BPQM508ZYTP",
+ "x-amz-id-2" : "Tfte+PiDMU55Rd3ptoQW0bgwDAmEnkTpJkaAOmmag6CIRi+xPOE5w3F8ZtihAHOjhbGe8NQMP5v6Re9miaKhzwCubJcQh/Bw",
+ "Date" : "Fri, 06 Feb 2026 20:00:11 GMT",
+ "Content-Type" : "application/xml"
+ }
+ },
+ "uuid" : "6dcec650-0822-426b-9c7e-627c19a53f79",
+ "persistent" : true,
+ "insertionIndex" : 612
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-fcaqtgbgub.json b/blob/blob-aws/src/test/resources/mappings/get-fcaqtgbgub.json
new file mode 100644
index 000000000..a5f46b52e
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-fcaqtgbgub.json
@@ -0,0 +1,21 @@
+{
+ "id" : "e00b9056-fecb-4ec4-9769-847cee9f9da3",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-compliance?legal-hold",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "base64Body" : "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPExlZ2FsSG9sZCB4bWxucz0iaHR0cDovL3MzLmFtYXpvbmF3cy5jb20vZG9jLzIwMDYtMDMtMDEvIj48U3RhdHVzPk9GRjwvU3RhdHVzPjwvTGVnYWxIb2xkPg==",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "C23FABFN3SN6PG1B",
+ "x-amz-id-2" : "H4u8GFY0H5QZYjQl7FKqdV3EmLAnliaJAQDJ4/td8uC596pQ5wtszuVh48//gHrrM8PA/wrjy9eFcUeOJ6ORZAJ+61nf6KVt",
+ "Date" : "Fri, 06 Feb 2026 20:00:16 GMT"
+ }
+ },
+ "uuid" : "e00b9056-fecb-4ec4-9769-847cee9f9da3",
+ "persistent" : true,
+ "insertionIndex" : 615
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-qmwrx6fg5w.json b/blob/blob-aws/src/test/resources/mappings/get-qmwrx6fg5w.json
new file mode 100644
index 000000000..dd77b0b6f
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-qmwrx6fg5w.json
@@ -0,0 +1,21 @@
+{
+ "id" : "0dc7e8dd-9e38-45e4-b2bd-0368d9a13bd8",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-governance?legal-hold",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "base64Body" : "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPExlZ2FsSG9sZCB4bWxucz0iaHR0cDovL3MzLmFtYXpvbmF3cy5jb20vZG9jLzIwMDYtMDMtMDEvIj48U3RhdHVzPk9GRjwvU3RhdHVzPjwvTGVnYWxIb2xkPg==",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "WXBAJ5BCDB6PKFNQ",
+ "x-amz-id-2" : "wR/R6DkhCWPKkkDIWXU7h1EgU6+Yvnjj0SnvopHwN9tev1G0V94cTHqEUac2g9E3bahqNeatBn1tZvz52qqRUS9eqLPIBCom",
+ "Date" : "Fri, 06 Feb 2026 20:00:18 GMT"
+ }
+ },
+ "uuid" : "0dc7e8dd-9e38-45e4-b2bd-0368d9a13bd8",
+ "persistent" : true,
+ "insertionIndex" : 620
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-u6nywavkbn.json b/blob/blob-aws/src/test/resources/mappings/get-u6nywavkbn.json
new file mode 100644
index 000000000..17a3fda58
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-u6nywavkbn.json
@@ -0,0 +1,22 @@
+{
+ "id" : "dd95a9a9-9e6d-4953-9adc-89e7a0185f43",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/no-lock?retention",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 404,
+ "body" : "\nNoSuchObjectLockConfigurationThe specified object does not have a ObjectLock configuration7JHPTBAXJHCYX1R7nNGCN7quGVRkRpcCHDzMhZnUIqs60PXeC3IrbCDjfYuXtdV1YKseAxs4dSGEVSYzrDEIV8seTM8TjRP4XD5xUhjfvgdnOE7g",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "7JHPTBAXJHCYX1R7",
+ "x-amz-id-2" : "nNGCN7quGVRkRpcCHDzMhZnUIqs60PXeC3IrbCDjfYuXtdV1YKseAxs4dSGEVSYzrDEIV8seTM8TjRP4XD5xUhjfvgdnOE7g",
+ "Date" : "Fri, 06 Feb 2026 20:00:10 GMT",
+ "Content-Type" : "application/xml"
+ }
+ },
+ "uuid" : "dd95a9a9-9e6d-4953-9adc-89e7a0185f43",
+ "persistent" : true,
+ "insertionIndex" : 609
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/get-ymd9e7ep32.json b/blob/blob-aws/src/test/resources/mappings/get-ymd9e7ep32.json
new file mode 100644
index 000000000..b9d230ad6
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/get-ymd9e7ep32.json
@@ -0,0 +1,21 @@
+{
+ "id" : "2f00cb5c-d843-49db-b554-19207256f5e7",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-governance?retention",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "base64Body" : "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPFJldGVudGlvbiB4bWxucz0iaHR0cDovL3MzLmFtYXpvbmF3cy5jb20vZG9jLzIwMDYtMDMtMDEvIj48TW9kZT5HT1ZFUk5BTkNFPC9Nb2RlPjxSZXRhaW5VbnRpbERhdGU+MjAyNi0wMi0wN1QyMDowMDoxNS44OTFaPC9SZXRhaW5VbnRpbERhdGU+PC9SZXRlbnRpb24+",
+ "headers" : {
+ "Server" : "AmazonS3",
+ "x-amz-request-id" : "WXB2QJ1M2XQYR34A",
+ "x-amz-id-2" : "xrgTXDlXYgwvVDuRbOy4ZQijJkTu67qe36AIprLJHlLJ0rnqE7BXsSYlnQ8cc+BYbrc84pPr+wqqjAeMxau4a+BAkEUVsoJn",
+ "Date" : "Fri, 06 Feb 2026 20:00:18 GMT"
+ }
+ },
+ "uuid" : "2f00cb5c-d843-49db-b554-19207256f5e7",
+ "persistent" : true,
+ "insertionIndex" : 621
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/put-ievkn426z3.json b/blob/blob-aws/src/test/resources/mappings/put-ievkn426z3.json
new file mode 100644
index 000000000..45f0df537
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/put-ievkn426z3.json
@@ -0,0 +1,28 @@
+{
+ "id" : "59624aa0-ea9e-4ecc-97b0-9216a5ce865f",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-compliance",
+ "method" : "PUT",
+ "bodyPatterns" : [ {
+ "binaryEqualTo" : "T2JqZWN0IGxvY2sgcmV0ZW50aW9uIGNvbXBsaWFuY2UgdGVzdA=="
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "ETag" : "\"a0d246afc0f8baf1e7dd602c80efd967\"",
+ "x-amz-checksum-type" : "FULL_OBJECT",
+ "x-amz-request-id" : "7Y27F0JTG6BQ309P",
+ "x-amz-checksum-crc32c" : "4z8aNg==",
+ "x-amz-server-side-encryption" : "AES256",
+ "x-amz-id-2" : "33jV28bqt3rbRAgU4yEqZI+31fgfqmBWeh7SbWyoJpj18EIGEXpO+kFDBmGguMqnrIOe3XwvjaqrXm//cCQkIB8aTlVxshD5",
+ "x-amz-version-id" : "zLsUsFgmtnxTZCDrCMNEl40YqfTx..oB",
+ "Date" : "Fri, 06 Feb 2026 20:00:13 GMT"
+ }
+ },
+ "uuid" : "59624aa0-ea9e-4ecc-97b0-9216a5ce865f",
+ "persistent" : true,
+ "insertionIndex" : 617
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/put-riacevxmhn.json b/blob/blob-aws/src/test/resources/mappings/put-riacevxmhn.json
new file mode 100644
index 000000000..d7d60144d
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/put-riacevxmhn.json
@@ -0,0 +1,28 @@
+{
+ "id" : "48f566ce-94a6-4d4c-ac5f-5b8268399987",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/no-lock",
+ "method" : "PUT",
+ "bodyPatterns" : [ {
+ "binaryEqualTo" : "T2JqZWN0IHdpdGhvdXQgbG9jayB0ZXN0"
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "ETag" : "\"fdd2c398fd6d009c474f1f9a02d38ec5\"",
+ "x-amz-checksum-crc64nvme" : "59pOl5UH6tE=",
+ "x-amz-checksum-type" : "FULL_OBJECT",
+ "x-amz-request-id" : "7JHK68N47E4ZRWSQ",
+ "x-amz-server-side-encryption" : "AES256",
+ "x-amz-id-2" : "YScnzGFcPCL7GM0FLOaNQcXRX/dDB5qEl+SFmLQUHVURw6x9eRtZVc6u2EFBxFM4p+BNH9FBWnoUP80AaSlnVJL1JFYdnWYQ",
+ "x-amz-version-id" : "nMqKRM_DQzj1_w2Pn52cIMooegPvSHz5",
+ "Date" : "Fri, 06 Feb 2026 20:00:11 GMT"
+ }
+ },
+ "uuid" : "48f566ce-94a6-4d4c-ac5f-5b8268399987",
+ "persistent" : true,
+ "insertionIndex" : 610
+}
\ No newline at end of file
diff --git a/blob/blob-aws/src/test/resources/mappings/put-sp1enepgxu.json b/blob/blob-aws/src/test/resources/mappings/put-sp1enepgxu.json
new file mode 100644
index 000000000..a4bbf5cca
--- /dev/null
+++ b/blob/blob-aws/src/test/resources/mappings/put-sp1enepgxu.json
@@ -0,0 +1,28 @@
+{
+ "id" : "1dfcd8e4-17e1-4b89-b83e-6e8462167d65",
+ "name" : "chameleon-jcloud-versioned_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/chameleon-jcloud-versioned/conformance-tests/objectlock/retention-governance",
+ "method" : "PUT",
+ "bodyPatterns" : [ {
+ "binaryEqualTo" : "T2JqZWN0IGxvY2sgcmV0ZW50aW9uIGdvdmVybmFuY2UgdGVzdA=="
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "headers" : {
+ "Server" : "AmazonS3",
+ "ETag" : "\"7e7cee74a915a322ef6c75c3ab034d4f\"",
+ "x-amz-checksum-type" : "FULL_OBJECT",
+ "x-amz-request-id" : "0TJ4VZ9FH6KD5WK6",
+ "x-amz-checksum-crc32c" : "s67YvA==",
+ "x-amz-server-side-encryption" : "AES256",
+ "x-amz-id-2" : "hupN+V74krKCrybh1oxcxWN1YBCKITqnYb9Yn146ZfeTDsrMy+yNM6A2U1Q29wFFAp2gUGo2cuSYGUkRKzrsOw97LQDeUaoS",
+ "x-amz-version-id" : "gxWcsKRtCMgLuudI4nVJ4MPxZLnPJL1i",
+ "Date" : "Fri, 06 Feb 2026 20:00:17 GMT"
+ }
+ },
+ "uuid" : "1dfcd8e4-17e1-4b89-b83e-6e8462167d65",
+ "persistent" : true,
+ "insertionIndex" : 622
+}
\ No newline at end of file
diff --git a/blob/blob-client/src/test/java/com/salesforce/multicloudj/blob/client/AbstractBlobStoreIT.java b/blob/blob-client/src/test/java/com/salesforce/multicloudj/blob/client/AbstractBlobStoreIT.java
index 8023b3d39..703db12fc 100644
--- a/blob/blob-client/src/test/java/com/salesforce/multicloudj/blob/client/AbstractBlobStoreIT.java
+++ b/blob/blob-client/src/test/java/com/salesforce/multicloudj/blob/client/AbstractBlobStoreIT.java
@@ -17,7 +17,10 @@
import com.salesforce.multicloudj.blob.driver.MultipartUpload;
import com.salesforce.multicloudj.blob.driver.MultipartUploadRequest;
import com.salesforce.multicloudj.blob.driver.PresignedOperation;
+import com.salesforce.multicloudj.blob.driver.ObjectLockConfiguration;
+import com.salesforce.multicloudj.blob.driver.ObjectLockInfo;
import com.salesforce.multicloudj.blob.driver.PresignedUrlRequest;
+import com.salesforce.multicloudj.blob.driver.RetentionMode;
import com.salesforce.multicloudj.blob.driver.UploadPartResponse;
import com.salesforce.multicloudj.blob.driver.UploadRequest;
import com.salesforce.multicloudj.blob.driver.UploadResponse;
@@ -52,6 +55,7 @@
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -74,6 +78,23 @@ public interface Harness extends AutoCloseable {
// Method to create a blob driver
AbstractBlobStore createBlobStore(boolean useValidBucket, boolean useValidCredentials, boolean useVersionedBucket);
+ /**
+ * Creates a blob store, optionally with an object-lock-enabled bucket.
+ * Default implementation ignores useObjectLockBucket and delegates to the 3-arg overload.
+ * Override in providers that support object lock (e.g. AWS, GCP) to use an object-lock bucket when true.
+ */
+ default AbstractBlobStore createBlobStore(boolean useValidBucket, boolean useValidCredentials,
+ boolean useVersionedBucket, boolean useObjectLockBucket) {
+ return createBlobStore(useValidBucket, useValidCredentials, useVersionedBucket);
+ }
+
+ /**
+ * Whether this provider supports object lock (WORM). When false, object lock conformance tests are skipped.
+ */
+ default boolean isObjectLockSupported() {
+ return false;
+ }
+
// provide the BlobClient endpoint in provider
String getEndpoint();
@@ -122,13 +143,22 @@ default String computeChecksum(byte[] content) {
private static final String GCP_PROVIDER_ID = "gcp";
+ /**
+ * WireMock extension class names (e.g. StubMappingTransformer) to load for replay.
+ * Override to add provider-specific transformers (e.g. relax body matching for object lock).
+ */
+ protected String[] getWireMockExtensionClasses() {
+ return new String[0];
+ }
+
/**
* Initializes the WireMock server before all tests.
*/
@BeforeAll
public void initializeWireMockServer() {
harness = createHarness();
- TestsUtil.startWireMockServer("src/test/resources", harness.getPort());
+ TestsUtil.startWireMockServer(
+ "src/test/resources", harness.getPort(), getWireMockExtensionClasses());
}
/**
@@ -1801,6 +1831,126 @@ public TestConfig(String testName, String key, byte[] content, Map bucketClient.getObjectLock("conformance-tests/objectlock/nonexistent", null));
+ }
+
@Test
public void testGetVersionedMetadata() throws IOException {
diff --git a/blob/blob-gcp/src/test/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStoreIT.java b/blob/blob-gcp/src/test/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStoreIT.java
index eb9756d0c..0f8b9ba37 100644
--- a/blob/blob-gcp/src/test/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStoreIT.java
+++ b/blob/blob-gcp/src/test/java/com/salesforce/multicloudj/blob/gcp/GcpBlobStoreIT.java
@@ -19,9 +19,12 @@
import java.net.URI;
import java.util.concurrent.ThreadLocalRandom;
+import static com.salesforce.multicloudj.common.util.common.TestsUtil.WIREMOCK_HOST;
+
public class GcpBlobStoreIT extends AbstractBlobStoreIT {
private static final String endpoint = "https://storage.googleapis.com";
+
private static final String bucketName = "substrate-sdk-gcp-poc1-test-bucket";
private static final String versionedBucketName = "substrate-sdk-gcp-poc1-test-bucket-versioned";
private static final String nonExistentBucketName = "java-bucket-does-not-exist";
@@ -59,9 +62,22 @@ public AbstractBlobStore createBlobStore(boolean useValidBucket, boolean useVali
}
}
- private AbstractBlobStore createBlobStore(final String bucketName, final Credentials credentials){
+ @Override
+ public AbstractBlobStore createBlobStore(boolean useValidBucket, boolean useValidCredentials,
+ boolean useVersionedBucket, boolean useObjectLockBucket) {
+ // GCP retention/legal hold works on any bucket; use versioned bucket for object lock tests
+ return createBlobStore(useValidBucket, useValidCredentials, useVersionedBucket || useObjectLockBucket);
+ }
- HttpTransport httpTransport = TestsUtilGcp.getHttpTransport(port);
+ @Override
+ public boolean isObjectLockSupported() {
+ return true;
+ }
+
+ private AbstractBlobStore createBlobStore(final String bucketName, final Credentials credentials){
+ // Connect directly to WireMock HTTPS so it can record/replay the full request (no CONNECT tunnel)
+ String host = "https://" + WIREMOCK_HOST + ":" + port;
+ HttpTransport httpTransport = TestsUtilGcp.getHttpTransportDirect(port);
HttpTransportOptions transportOptions = HttpTransportOptions.newBuilder()
.setHttpTransportFactory(() -> httpTransport)
.build();
@@ -69,7 +85,7 @@ private AbstractBlobStore createBlobStore(final String bucketName, final Credent
Storage storage = StorageOptions.newBuilder()
.setTransportOptions(transportOptions)
.setCredentials(credentials)
- .setHost(endpoint)
+ .setHost(host)
.build().getService();
diff --git a/blob/blob-gcp/src/test/resources/mappings/delete-06fviw5cly.json b/blob/blob-gcp/src/test/resources/mappings/delete-06fviw5cly.json
new file mode 100644
index 000000000..8c63a905c
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/delete-06fviw5cly.json
@@ -0,0 +1,25 @@
+{
+ "id" : "68501692-9dee-40f4-a6a4-2ad61d5348f0",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "no-cache, no-store, max-age=0, must-revalidate",
+ "X-GUploader-UploadID" : "AJRbA5VnqE4Flo6Ko9OgVhLdGHoE7ppgXZLdDUQ35ds1YDsN71CLTIEWYo37pYVvlRAsYKW5qt2dV5M",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Pragma" : "no-cache",
+ "Expires" : "Mon, 01 Jan 1990 00:00:00 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:35 GMT",
+ "Content-Type" : "application/json"
+ }
+ },
+ "uuid" : "68501692-9dee-40f4-a6a4-2ad61d5348f0",
+ "persistent" : true,
+ "insertionIndex" : 951
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/delete-0ltakthvrn.json b/blob/blob-gcp/src/test/resources/mappings/delete-0ltakthvrn.json
new file mode 100644
index 000000000..6255b1c13
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/delete-0ltakthvrn.json
@@ -0,0 +1,25 @@
+{
+ "id" : "05f67bd6-ccdb-4804-b2a9-5ba3b57b55c9",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "no-cache, no-store, max-age=0, must-revalidate",
+ "X-GUploader-UploadID" : "AJRbA5Xt8Om5j4CLBydZSvMygGfONhIrvOm7F99bZUvY9C39-Erfw26SWzhJu-vnzd_kz8q-UcUqmJ4",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Pragma" : "no-cache",
+ "Expires" : "Mon, 01 Jan 1990 00:00:00 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Content-Type" : "application/json"
+ }
+ },
+ "uuid" : "05f67bd6-ccdb-4804-b2a9-5ba3b57b55c9",
+ "persistent" : true,
+ "insertionIndex" : 959
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/delete-sd5qoxlnch.json b/blob/blob-gcp/src/test/resources/mappings/delete-sd5qoxlnch.json
new file mode 100644
index 000000000..6d50c7fb5
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/delete-sd5qoxlnch.json
@@ -0,0 +1,25 @@
+{
+ "id" : "b19b6b38-398b-4ede-a8c8-7984025093a3",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance",
+ "method" : "DELETE"
+ },
+ "response" : {
+ "status" : 204,
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "no-cache, no-store, max-age=0, must-revalidate",
+ "X-GUploader-UploadID" : "AJRbA5UNz41TRRsiutNAc6zx3Mf9tDxuQiUgGYgDOuwxLumCHoUt-8mlZmytEHoRGh2EsG6CH4igVns",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Pragma" : "no-cache",
+ "Expires" : "Mon, 01 Jan 1990 00:00:00 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:38 GMT",
+ "Content-Type" : "application/json"
+ }
+ },
+ "uuid" : "b19b6b38-398b-4ede-a8c8-7984025093a3",
+ "persistent" : true,
+ "insertionIndex" : 965
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-abqrpeam2d.json b/blob/blob-gcp/src/test/resources/mappings/get-abqrpeam2d.json
new file mode 100644
index 000000000..7a146660b
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-abqrpeam2d.json
@@ -0,0 +1,29 @@
+{
+ "id" : "2162af26-cc0d-47f1-a434-912cb283feea",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/no-lock/1770406414100158\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock?generation=1770406414100158&alt=media\",\n \"name\": \"conformance-tests/objectlock/no-lock\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406414100158\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"24\",\n \"md5Hash\": \"/dLDmP1tAJxHTx+aAtOOxQ==\",\n \"crc32c\": \"sPg8aA==\",\n \"etag\": \"CL7NruvNxZIDEAE=\",\n \"timeCreated\": \"2026-02-06T19:33:34.157Z\",\n \"updated\": \"2026-02-06T19:33:34.157Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:34.157Z\",\n \"timeFinalized\": \"2026-02-06T19:33:34.157Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CL7NruvNxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5VQomY4Sc0OtRzow2WlDM663_Iu8emZ0_H3w2k26nnYDHMzAmCyuj6TP67QoblXJ1_OhqF4Tsk",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "2162af26-cc0d-47f1-a434-912cb283feea",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-no-lock",
+ "requiredScenarioState" : "Started",
+ "newScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-no-lock-2",
+ "insertionIndex" : 954
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-cndoimbgwu.json b/blob/blob-gcp/src/test/resources/mappings/get-cndoimbgwu.json
new file mode 100644
index 000000000..8b7d0d5ca
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-cndoimbgwu.json
@@ -0,0 +1,28 @@
+{
+ "id" : "37f83a89-470f-412e-aab7-9913a877cb96",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/retention-governance/1770406417555204\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance?generation=1770406417555204&alt=media\",\n \"name\": \"conformance-tests/objectlock/retention-governance\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406417555204\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"37\",\n \"md5Hash\": \"fnzudKkVoyLvbHXDqwNNTw==\",\n \"crc32c\": \"s67YvA==\",\n \"etag\": \"CIS+ge3NxZIDEAE=\",\n \"temporaryHold\": false,\n \"retentionExpirationTime\": \"2026-02-07T19:33:37.019Z\",\n \"retention\": {\n \"retainUntilTime\": \"2026-02-07T19:33:37.019Z\",\n \"mode\": \"Unlocked\"\n },\n \"timeCreated\": \"2026-02-06T19:33:37.611Z\",\n \"updated\": \"2026-02-06T19:33:37.611Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:37.611Z\",\n \"timeFinalized\": \"2026-02-06T19:33:37.611Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CIS+ge3NxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5VF9x3VnyQ4WLD_epeNQAN5UMA-Ew8sAP21OiTd3k2tF7EyU8JK-i3O0RgfHAU5xYpYuhQx2aA",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:38 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:38 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "37f83a89-470f-412e-aab7-9913a877cb96",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-governance",
+ "requiredScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-governance-2",
+ "insertionIndex" : 967
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-cxg2ljnmwq.json b/blob/blob-gcp/src/test/resources/mappings/get-cxg2ljnmwq.json
new file mode 100644
index 000000000..7c0814005
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-cxg2ljnmwq.json
@@ -0,0 +1,28 @@
+{
+ "id" : "ce7d8148-fc3a-4e76-8c3c-72b460b9dceb",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/retention-compliance/1770406416023931\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance?generation=1770406416023931&alt=media\",\n \"name\": \"conformance-tests/objectlock/retention-compliance\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406416023931\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"37\",\n \"md5Hash\": \"oNJGr8D4uvHn3WAsgO/ZZw==\",\n \"crc32c\": \"4z8aNg==\",\n \"etag\": \"CPuCpOzNxZIDEAE=\",\n \"temporaryHold\": false,\n \"retentionExpirationTime\": \"2026-02-07T19:33:35.525Z\",\n \"retention\": {\n \"retainUntilTime\": \"2026-02-07T19:33:35.525Z\",\n \"mode\": \"Locked\"\n },\n \"timeCreated\": \"2026-02-06T19:33:36.078Z\",\n \"updated\": \"2026-02-06T19:33:36.078Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:36.078Z\",\n \"timeFinalized\": \"2026-02-06T19:33:36.078Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CPuCpOzNxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5W6Gvhq3XgQKFPb8ILHh-M3IGkCXQ4X7ecjrBgYyMb3iT08-TkLP1Y_3QT3-TtD624355lgqdQ",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "ce7d8148-fc3a-4e76-8c3c-72b460b9dceb",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-compliance",
+ "requiredScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-compliance-2",
+ "insertionIndex" : 961
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-fjttdpp4cy.json b/blob/blob-gcp/src/test/resources/mappings/get-fjttdpp4cy.json
new file mode 100644
index 000000000..1ee77c4f9
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-fjttdpp4cy.json
@@ -0,0 +1,26 @@
+{
+ "id" : "bd288dbe-4abf-4024-9bde-8d42fb7033fc",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#bucket\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"name\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"projectNumber\": \"599653580068\",\n \"generation\": \"1758831414065126441\",\n \"metageneration\": \"3\",\n \"location\": \"US\",\n \"storageClass\": \"STANDARD\",\n \"etag\": \"CAM=\",\n \"timeCreated\": \"2025-09-25T20:16:54.302Z\",\n \"updated\": \"2026-02-06T05:13:44.227Z\",\n \"versioning\": {\n \"enabled\": true\n },\n \"objectRetention\": {\n \"mode\": \"Enabled\"\n },\n \"softDeletePolicy\": {\n \"retentionDurationSeconds\": \"604800\",\n \"effectiveTime\": \"2025-09-25T20:16:54.302Z\"\n },\n \"iamConfiguration\": {\n \"bucketPolicyOnly\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"uniformBucketLevelAccess\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"publicAccessPrevention\": \"inherited\"\n },\n \"locationType\": \"multi-region\",\n \"rpo\": \"DEFAULT\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CAM=",
+ "X-GUploader-UploadID" : "AJRbA5X_H-1GYvPlOS3BgVdMAXFbOgs8UOL2ZFsuaBTzGrCj_84tvGsKWzjuccmlu3xYH2s_eVb_gh0",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:38 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:38 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "bd288dbe-4abf-4024-9bde-8d42fb7033fc",
+ "persistent" : true,
+ "insertionIndex" : 966
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-iyvxgwu5eg.json b/blob/blob-gcp/src/test/resources/mappings/get-iyvxgwu5eg.json
new file mode 100644
index 000000000..eda5c14ed
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-iyvxgwu5eg.json
@@ -0,0 +1,26 @@
+{
+ "id" : "7469e521-aab6-4c45-a365-e69ce6b7f68f",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_nonexistent",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fnonexistent?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 404,
+ "body" : "{\n \"error\": {\n \"code\": 404,\n \"message\": \"No such object: substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/nonexistent\",\n \"errors\": [\n {\n \"message\": \"No such object: substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/nonexistent\",\n \"domain\": \"global\",\n \"reason\": \"notFound\"\n }\n ]\n }\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "no-cache, no-store, max-age=0, must-revalidate",
+ "X-GUploader-UploadID" : "AJRbA5WKRiz_nCfZspiX2H3eNMxZmIArvIbXmn3mVgug34h1TfKM3XFoRN0qRvkCtDvsp8QheHqAsKQ",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Pragma" : "no-cache",
+ "Expires" : "Mon, 01 Jan 1990 00:00:00 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:35 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "7469e521-aab6-4c45-a365-e69ce6b7f68f",
+ "persistent" : true,
+ "insertionIndex" : 957
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-lrhicy5wwb.json b/blob/blob-gcp/src/test/resources/mappings/get-lrhicy5wwb.json
new file mode 100644
index 000000000..27bf7af41
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-lrhicy5wwb.json
@@ -0,0 +1,28 @@
+{
+ "id" : "72e60f4d-b822-4c30-b347-766efdd0fe13",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_no-lock",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/no-lock/1770406414100158\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fno-lock?generation=1770406414100158&alt=media\",\n \"name\": \"conformance-tests/objectlock/no-lock\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406414100158\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"24\",\n \"md5Hash\": \"/dLDmP1tAJxHTx+aAtOOxQ==\",\n \"crc32c\": \"sPg8aA==\",\n \"etag\": \"CL7NruvNxZIDEAE=\",\n \"timeCreated\": \"2026-02-06T19:33:34.157Z\",\n \"updated\": \"2026-02-06T19:33:34.157Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:34.157Z\",\n \"timeFinalized\": \"2026-02-06T19:33:34.157Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CL7NruvNxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5UpHu-oO99KdoKMQGJ3n9eY4tjQUxaQ9eU75yo3kZtY4flBhX14FFAgyElHmg2On0dVL7I7sHE",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "72e60f4d-b822-4c30-b347-766efdd0fe13",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-no-lock",
+ "requiredScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-no-lock-2",
+ "insertionIndex" : 953
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-qhxkse0yvc.json b/blob/blob-gcp/src/test/resources/mappings/get-qhxkse0yvc.json
new file mode 100644
index 000000000..5cc8ae8c4
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-qhxkse0yvc.json
@@ -0,0 +1,26 @@
+{
+ "id" : "2f32abc0-b776-4d01-9bae-92f8ab4d882f",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#bucket\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"name\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"projectNumber\": \"599653580068\",\n \"generation\": \"1758831414065126441\",\n \"metageneration\": \"3\",\n \"location\": \"US\",\n \"storageClass\": \"STANDARD\",\n \"etag\": \"CAM=\",\n \"timeCreated\": \"2025-09-25T20:16:54.302Z\",\n \"updated\": \"2026-02-06T05:13:44.227Z\",\n \"versioning\": {\n \"enabled\": true\n },\n \"objectRetention\": {\n \"mode\": \"Enabled\"\n },\n \"softDeletePolicy\": {\n \"retentionDurationSeconds\": \"604800\",\n \"effectiveTime\": \"2025-09-25T20:16:54.302Z\"\n },\n \"iamConfiguration\": {\n \"bucketPolicyOnly\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"uniformBucketLevelAccess\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"publicAccessPrevention\": \"inherited\"\n },\n \"locationType\": \"multi-region\",\n \"rpo\": \"DEFAULT\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CAM=",
+ "X-GUploader-UploadID" : "AJRbA5XNjIAl6Gx7so-v5O6SrkywHkbHZDL621Fn0Sie8egiYxtxUoOu7zxIqe7_sfD2zj-WCouOaVg",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:34 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "2f32abc0-b776-4d01-9bae-92f8ab4d882f",
+ "persistent" : true,
+ "insertionIndex" : 952
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-swpt2dpzmm.json b/blob/blob-gcp/src/test/resources/mappings/get-swpt2dpzmm.json
new file mode 100644
index 000000000..e316f21ad
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-swpt2dpzmm.json
@@ -0,0 +1,26 @@
+{
+ "id" : "1aee5d74-db21-4556-9d6e-078702accafc",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#bucket\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"name\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"projectNumber\": \"599653580068\",\n \"generation\": \"1758831414065126441\",\n \"metageneration\": \"3\",\n \"location\": \"US\",\n \"storageClass\": \"STANDARD\",\n \"etag\": \"CAM=\",\n \"timeCreated\": \"2025-09-25T20:16:54.302Z\",\n \"updated\": \"2026-02-06T05:13:44.227Z\",\n \"versioning\": {\n \"enabled\": true\n },\n \"objectRetention\": {\n \"mode\": \"Enabled\"\n },\n \"softDeletePolicy\": {\n \"retentionDurationSeconds\": \"604800\",\n \"effectiveTime\": \"2025-09-25T20:16:54.302Z\"\n },\n \"iamConfiguration\": {\n \"bucketPolicyOnly\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"uniformBucketLevelAccess\": {\n \"enabled\": true,\n \"lockedTime\": \"2025-12-24T20:16:54.302Z\"\n },\n \"publicAccessPrevention\": \"inherited\"\n },\n \"locationType\": \"multi-region\",\n \"rpo\": \"DEFAULT\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CAM=",
+ "X-GUploader-UploadID" : "AJRbA5X6haVwoabWBqjlcoggaquVe9Yqz_3GMoC9gUEyBLEoC3nbBY_QM7r3rITejVy7sT9anNhTFwA",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "1aee5d74-db21-4556-9d6e-078702accafc",
+ "persistent" : true,
+ "insertionIndex" : 960
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-xe5wbrnidv.json b/blob/blob-gcp/src/test/resources/mappings/get-xe5wbrnidv.json
new file mode 100644
index 000000000..3cf695840
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-xe5wbrnidv.json
@@ -0,0 +1,29 @@
+{
+ "id" : "18d9f0ea-e6d6-4f4c-a47a-933ed0f5052b",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-compliance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/retention-compliance/1770406416023931\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-compliance?generation=1770406416023931&alt=media\",\n \"name\": \"conformance-tests/objectlock/retention-compliance\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406416023931\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"37\",\n \"md5Hash\": \"oNJGr8D4uvHn3WAsgO/ZZw==\",\n \"crc32c\": \"4z8aNg==\",\n \"etag\": \"CPuCpOzNxZIDEAE=\",\n \"temporaryHold\": false,\n \"retentionExpirationTime\": \"2026-02-07T19:33:35.525Z\",\n \"retention\": {\n \"retainUntilTime\": \"2026-02-07T19:33:35.525Z\",\n \"mode\": \"Locked\"\n },\n \"timeCreated\": \"2026-02-06T19:33:36.078Z\",\n \"updated\": \"2026-02-06T19:33:36.078Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:36.078Z\",\n \"timeFinalized\": \"2026-02-06T19:33:36.078Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CPuCpOzNxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5WBF8tgBlmVHOlQyakpvj9fBg2y6p7UpR_ch8EscSOIyqX0DLjvn0E25pi6bqED-1EkAqJKEGI",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:36 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "18d9f0ea-e6d6-4f4c-a47a-933ed0f5052b",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-compliance",
+ "requiredScenarioState" : "Started",
+ "newScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-compliance-2",
+ "insertionIndex" : 962
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/get-z4q5tmjzzk.json b/blob/blob-gcp/src/test/resources/mappings/get-z4q5tmjzzk.json
new file mode 100644
index 000000000..41f662a66
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/get-z4q5tmjzzk.json
@@ -0,0 +1,29 @@
+{
+ "id" : "50c6e5b9-44b5-46ed-be19-b5e845166ed3",
+ "name" : "storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o_conformance-tests_objectlock_retention-governance",
+ "request" : {
+ "url" : "/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance?projection=full",
+ "method" : "GET"
+ },
+ "response" : {
+ "status" : 200,
+ "body" : "{\n \"kind\": \"storage#object\",\n \"id\": \"substrate-sdk-gcp-poc1-test-bucket-versioned/conformance-tests/objectlock/retention-governance/1770406417555204\",\n \"selfLink\": \"https://www.googleapis.com/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance\",\n \"mediaLink\": \"https://storage.googleapis.com/download/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o/conformance-tests%2Fobjectlock%2Fretention-governance?generation=1770406417555204&alt=media\",\n \"name\": \"conformance-tests/objectlock/retention-governance\",\n \"bucket\": \"substrate-sdk-gcp-poc1-test-bucket-versioned\",\n \"generation\": \"1770406417555204\",\n \"metageneration\": \"1\",\n \"contentType\": \"application/octet-stream\",\n \"storageClass\": \"STANDARD\",\n \"size\": \"37\",\n \"md5Hash\": \"fnzudKkVoyLvbHXDqwNNTw==\",\n \"crc32c\": \"s67YvA==\",\n \"etag\": \"CIS+ge3NxZIDEAE=\",\n \"temporaryHold\": false,\n \"retentionExpirationTime\": \"2026-02-07T19:33:37.019Z\",\n \"retention\": {\n \"retainUntilTime\": \"2026-02-07T19:33:37.019Z\",\n \"mode\": \"Unlocked\"\n },\n \"timeCreated\": \"2026-02-06T19:33:37.611Z\",\n \"updated\": \"2026-02-06T19:33:37.611Z\",\n \"timeStorageClassUpdated\": \"2026-02-06T19:33:37.611Z\",\n \"timeFinalized\": \"2026-02-06T19:33:37.611Z\"\n}\n",
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "private, max-age=0, must-revalidate, no-transform",
+ "ETag" : "CIS+ge3NxZIDEAE=",
+ "X-GUploader-UploadID" : "AJRbA5XeOaLjkkYPDZx9yG7lJtO7tIj267LUkWkhg_zTIY-TGdhiP6myatUlqmgZ1Qs7WM42E-0yeUg",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Expires" : "Fri, 06 Feb 2026 19:33:37 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:37 GMT",
+ "Content-Type" : "application/json; charset=UTF-8"
+ }
+ },
+ "uuid" : "50c6e5b9-44b5-46ed-be19-b5e845166ed3",
+ "persistent" : true,
+ "scenarioName" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-governance",
+ "requiredScenarioState" : "Started",
+ "newScenarioState" : "scenario-1-storage-v1-b-substrate-sdk-gcp-poc1-test-bucket-versioned-o-conformance-tests-objectlock-retention-governance-2",
+ "insertionIndex" : 968
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/post-cnnqenruqx.json b/blob/blob-gcp/src/test/resources/mappings/post-cnnqenruqx.json
new file mode 100644
index 000000000..e3b415bcd
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/post-cnnqenruqx.json
@@ -0,0 +1 @@
+{"id":"80082217-34b4-4c0a-b2c2-7cabf8fef4a7","name":"upload_storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o","request":{"url":"/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/retention-compliance&uploadType=resumable","method":"POST","bodyPatterns":[{"matchesJsonPath":"$.bucket"},{"matchesJsonPath":"$.name"},{"matchesJsonPath":"$.retention"},{"matchesJsonPath":"$.retention.retainUntilTime"},{"matchesJsonPath":"$.temporaryHold"}]},"response":{"status":200,"headers":{"Alt-Svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000","Server":"UploadServer","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","X-GUploader-UploadID":"AJRbA5VF8v3jseLKUzkIcJA12AMsr26L-muFNHRsvSsf41FxRkk4YJZTh2jYi2XrIxgNA9a8BSusA4_iyGcDTIpxwuSEV6xbWdb1AB0mTlSPj4U","Vary":["Origin","X-Origin"],"Pragma":"no-cache","Expires":"Mon, 01 Jan 1990 00:00:00 GMT","Date":"Fri, 06 Feb 2026 19:33:35 GMT","Location":"https://storage.googleapis.com/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/retention-compliance&uploadType=resumable&upload_id=AJRbA5VF8v3jseLKUzkIcJA12AMsr26L-muFNHRsvSsf41FxRkk4YJZTh2jYi2XrIxgNA9a8BSusA4_iyGcDTIpxwuSEV6xbWdb1AB0mTlSPj4U","Content-Type":"text/plain; charset=utf-8"}},"uuid":"80082217-34b4-4c0a-b2c2-7cabf8fef4a7","persistent":true,"insertionIndex":963}
diff --git a/blob/blob-gcp/src/test/resources/mappings/post-cot0l9cfvf.json b/blob/blob-gcp/src/test/resources/mappings/post-cot0l9cfvf.json
new file mode 100644
index 000000000..2f75a1576
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/post-cot0l9cfvf.json
@@ -0,0 +1,31 @@
+{
+ "id" : "bfc84ff4-1426-46ac-ba48-ea4fb4ba356c",
+ "name" : "upload_storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o",
+ "request" : {
+ "url" : "/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/no-lock&uploadType=resumable",
+ "method" : "POST",
+ "bodyPatterns" : [ {
+ "equalToJson" : "{\"bucket\":\"substrate-sdk-gcp-poc1-test-bucket-versioned\",\"metadata\":{},\"name\":\"conformance-tests/objectlock/no-lock\"}",
+ "ignoreArrayOrder" : true,
+ "ignoreExtraElements" : false
+ } ]
+ },
+ "response" : {
+ "status" : 200,
+ "headers" : {
+ "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000",
+ "Server" : "UploadServer",
+ "Cache-Control" : "no-cache, no-store, max-age=0, must-revalidate",
+ "X-GUploader-UploadID" : "AJRbA5V4edHZ8BAunfxpBMnU7XAuhozzXoywxWxCjEmWKaNnn94F6RLc5IN_vf-4Z1s9RsU_8J9v7DLHDQ5BIoIwWhhMjtI63Gmv5xnKfmaQ2A",
+ "Vary" : [ "Origin", "X-Origin" ],
+ "Pragma" : "no-cache",
+ "Expires" : "Mon, 01 Jan 1990 00:00:00 GMT",
+ "Date" : "Fri, 06 Feb 2026 19:33:33 GMT",
+ "Location" : "https://storage.googleapis.com/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/no-lock&uploadType=resumable&upload_id=AJRbA5V4edHZ8BAunfxpBMnU7XAuhozzXoywxWxCjEmWKaNnn94F6RLc5IN_vf-4Z1s9RsU_8J9v7DLHDQ5BIoIwWhhMjtI63Gmv5xnKfmaQ2A",
+ "Content-Type" : "text/plain; charset=utf-8"
+ }
+ },
+ "uuid" : "bfc84ff4-1426-46ac-ba48-ea4fb4ba356c",
+ "persistent" : true,
+ "insertionIndex" : 955
+}
\ No newline at end of file
diff --git a/blob/blob-gcp/src/test/resources/mappings/post-txqfacbfx6.json b/blob/blob-gcp/src/test/resources/mappings/post-txqfacbfx6.json
new file mode 100644
index 000000000..a02f342cd
--- /dev/null
+++ b/blob/blob-gcp/src/test/resources/mappings/post-txqfacbfx6.json
@@ -0,0 +1 @@
+{"id":"ee8f0876-bee7-472d-9f51-f52d90726374","name":"upload_storage_v1_b_substrate-sdk-gcp-poc1-test-bucket-versioned_o","request":{"url":"/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/retention-governance&uploadType=resumable","method":"POST","bodyPatterns":[{"matchesJsonPath":"$.bucket"},{"matchesJsonPath":"$.name"},{"matchesJsonPath":"$.retention"},{"matchesJsonPath":"$.retention.retainUntilTime"},{"matchesJsonPath":"$.temporaryHold"}]},"response":{"status":200,"headers":{"Alt-Svc":"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000","Server":"UploadServer","Cache-Control":"no-cache, no-store, max-age=0, must-revalidate","X-GUploader-UploadID":"AJRbA5Wr0vjpL93q8X9pgaVbnglGrIcSxf1u2H4e7FQYqdIIHlvsLoVS4cBHQo0A5tW9l2p7kLaBP0shjW4-PRvOc5ZMyUk73rfDC5qK_vUsHQ","Vary":["Origin","X-Origin"],"Pragma":"no-cache","Expires":"Mon, 01 Jan 1990 00:00:00 GMT","Date":"Fri, 06 Feb 2026 19:33:37 GMT","Location":"https://storage.googleapis.com/upload/storage/v1/b/substrate-sdk-gcp-poc1-test-bucket-versioned/o?name=conformance-tests/objectlock/retention-governance&uploadType=resumable&upload_id=AJRbA5Wr0vjpL93q8X9pgaVbnglGrIcSxf1u2H4e7FQYqdIIHlvsLoVS4cBHQo0A5tW9l2p7kLaBP0shjW4-PRvOc5ZMyUk73rfDC5qK_vUsHQ","Content-Type":"text/plain; charset=utf-8"}},"uuid":"ee8f0876-bee7-472d-9f51-f52d90726374","persistent":true,"insertionIndex":969}
diff --git a/multicloudj-common-gcp/src/test/java/com/salesforce/multicloudj/common/gcp/util/TestsUtilGcp.java b/multicloudj-common-gcp/src/test/java/com/salesforce/multicloudj/common/gcp/util/TestsUtilGcp.java
index ea6e01833..10b30fc61 100644
--- a/multicloudj-common-gcp/src/test/java/com/salesforce/multicloudj/common/gcp/util/TestsUtilGcp.java
+++ b/multicloudj-common-gcp/src/test/java/com/salesforce/multicloudj/common/gcp/util/TestsUtilGcp.java
@@ -75,8 +75,10 @@ private static SSLContext createTrustAllSSLContext() {
}
/**
- * Gets an HttpTransport configured with a proxy to the WireMock server.
- * This allows HTTP/HTTPS traffic to be intercepted for testing.
+ * Gets an HttpTransport configured with a proxy to the WireMock server (HTTP port).
+ * Note: Using an HTTP proxy causes HTTPS requests to use CONNECT tunneling, so WireMock
+ * never sees the actual request body. Prefer {@link #getHttpTransportDirect(int)} for
+ * record/replay so requests hit WireMock's HTTPS port directly.
*
* @param port The base port for WireMock (proxy will use port+1)
* @return A configured HttpTransport
@@ -84,13 +86,8 @@ private static SSLContext createTrustAllSSLContext() {
*/
public static HttpTransport getHttpTransport(int port) {
try {
- // Get SSL socket factory that trusts all certificates
SSLSocketFactory sslSocketFactory = createTrustAllSSLContext().getSocketFactory();
-
- // Define proxy to WireMock
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(WIREMOCK_HOST, port + 1));
-
- // Set up HTTP transport with proxy and SSL settings
return new NetHttpTransport.Builder()
.setProxy(proxy)
.doNotValidateCertificate()
@@ -101,6 +98,26 @@ public static HttpTransport getHttpTransport(int port) {
}
}
+ /**
+ * Gets an HttpTransport that connects directly to WireMock's HTTPS port (no proxy).
+ * Use with StorageOptions.setHost("https://localhost:" + port) so the client talks
+ * to WireMock directly. WireMock can then record/replay the full request in both modes.
+ *
+ * @param port WireMock HTTPS port (same as harness.getPort())
+ * @return A configured HttpTransport with trust-all certs, no proxy
+ */
+ public static HttpTransport getHttpTransportDirect(int port) {
+ try {
+ SSLSocketFactory sslSocketFactory = createTrustAllSSLContext().getSocketFactory();
+ return new NetHttpTransport.Builder()
+ .doNotValidateCertificate()
+ .setSslSocketFactory(sslSocketFactory)
+ .build();
+ } catch (GeneralSecurityException e) {
+ throw new SecurityConfigurationException("Failed to configure transport", e);
+ }
+ }
+
/**
* Gets a transport channel provider configured with a proxy to the WireMock server.
* This allows HTTP/HTTPS traffic to be intercepted for testing.