Skip to content

Commit f9d9113

Browse files
Fix lambda variable issue and create dedicated test file for decoder downloads
Co-authored-by: gunjansingh-msft <[email protected]>
1 parent d3d95ef commit f9d9113

File tree

3 files changed

+221
-138
lines changed

3 files changed

+221
-138
lines changed

sdk/storage/azure-storage-blob/src/main/java/com/azure/storage/blob/specialized/BlobAsyncClientBase.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,18 +1333,19 @@ Mono<BlobDownloadAsyncResponse> downloadStreamWithResponse(BlobRange range, Down
13331333
DownloadRetryOptions finalOptions = (options == null) ? new DownloadRetryOptions() : options;
13341334

13351335
// The first range should eagerly convert headers as they'll be used to create response types.
1336-
Context firstRangeContext = context == null
1336+
Context initialContext = context == null
13371337
? new Context("azure-eagerly-convert-headers", true)
13381338
: context.addData("azure-eagerly-convert-headers", true);
13391339

13401340
// Add structured message decoding context if enabled
1341+
final Context firstRangeContext;
13411342
if (contentValidationOptions != null
13421343
&& contentValidationOptions.isStructuredMessageValidationEnabled()) {
1343-
firstRangeContext = firstRangeContext.addData(
1344-
Constants.STRUCTURED_MESSAGE_DECODING_CONTEXT_KEY, true);
1345-
firstRangeContext = firstRangeContext.addData(
1346-
Constants.STRUCTURED_MESSAGE_VALIDATION_OPTIONS_CONTEXT_KEY,
1347-
contentValidationOptions);
1344+
firstRangeContext = initialContext
1345+
.addData(Constants.STRUCTURED_MESSAGE_DECODING_CONTEXT_KEY, true)
1346+
.addData(Constants.STRUCTURED_MESSAGE_VALIDATION_OPTIONS_CONTEXT_KEY, contentValidationOptions);
1347+
} else {
1348+
firstRangeContext = initialContext;
13481349
}
13491350

13501351
return downloadRange(finalRange, finalRequestConditions, finalRequestConditions.getIfMatch(), finalGetMD5,

sdk/storage/azure-storage-blob/src/test/java/com/azure/storage/blob/specialized/BlobBaseAsyncApiTests.java

Lines changed: 0 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -565,138 +565,6 @@ public void queryACFail(OffsetDateTime modified, OffsetDateTime unmodified, Stri
565565
StepVerifier.create(response).verifyError(BlobStorageException.class);
566566
}
567567

568-
@Test
569-
public void downloadStreamWithResponseContentValidation() throws IOException {
570-
byte[] randomData = getRandomByteArray(Constants.KB);
571-
StructuredMessageEncoder encoder
572-
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
573-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
574-
575-
Flux<ByteBuffer> input = Flux.just(encodedData);
576-
577-
DownloadContentValidationOptions validationOptions
578-
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
579-
580-
StepVerifier
581-
.create(bc.upload(input, null, true)
582-
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
583-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
584-
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
585-
.verifyComplete();
586-
}
587-
588-
@Test
589-
public void downloadStreamWithResponseContentValidationRange() throws IOException {
590-
byte[] randomData = getRandomByteArray(Constants.KB);
591-
StructuredMessageEncoder encoder
592-
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
593-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
594-
595-
Flux<ByteBuffer> input = Flux.just(encodedData);
596-
597-
DownloadContentValidationOptions validationOptions
598-
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
599-
600-
BlobRange range = new BlobRange(0, 512L);
601-
602-
StepVerifier.create(bc.upload(input, null, true)
603-
.then(bc.downloadStreamWithResponse(range, null, null, false, validationOptions))
604-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue()))).assertNext(r -> {
605-
assertNotNull(r);
606-
assertTrue(r.length > 0);
607-
}).verifyComplete();
608-
}
609-
610-
@Test
611-
public void downloadStreamWithResponseContentValidationLargeBlob() throws IOException {
612-
// Test with larger data to verify chunking works correctly
613-
byte[] randomData = getRandomByteArray(5 * Constants.KB);
614-
StructuredMessageEncoder encoder
615-
= new StructuredMessageEncoder(randomData.length, 1024, StructuredMessageFlags.STORAGE_CRC64);
616-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
617-
618-
Flux<ByteBuffer> input = Flux.just(encodedData);
619-
620-
DownloadContentValidationOptions validationOptions
621-
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
622-
623-
StepVerifier
624-
.create(bc.upload(input, null, true)
625-
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
626-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
627-
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
628-
.verifyComplete();
629-
}
630-
631-
@Test
632-
public void downloadStreamWithResponseContentValidationMultipleSegments() throws IOException {
633-
// Test with multiple segments to ensure all segments are decoded correctly
634-
byte[] randomData = getRandomByteArray(2 * Constants.KB);
635-
StructuredMessageEncoder encoder
636-
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
637-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
638-
639-
Flux<ByteBuffer> input = Flux.just(encodedData);
640-
641-
DownloadContentValidationOptions validationOptions
642-
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
643-
644-
StepVerifier
645-
.create(bc.upload(input, null, true)
646-
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
647-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
648-
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
649-
.verifyComplete();
650-
}
651-
652-
@Test
653-
public void downloadStreamWithResponseNoValidation() throws IOException {
654-
// Test that download works normally when validation is not enabled
655-
byte[] randomData = getRandomByteArray(Constants.KB);
656-
StructuredMessageEncoder encoder
657-
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
658-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
659-
660-
Flux<ByteBuffer> input = Flux.just(encodedData);
661-
662-
// No validation options - should download encoded data as-is
663-
StepVerifier
664-
.create(bc.upload(input, null, true)
665-
.then(bc.downloadStreamWithResponse(null, null, null, false))
666-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
667-
.assertNext(r -> {
668-
assertNotNull(r);
669-
// Should get encoded data, not decoded
670-
assertTrue(r.length > randomData.length); // Encoded data is larger
671-
})
672-
.verifyComplete();
673-
}
674-
675-
@Test
676-
public void downloadStreamWithResponseValidationDisabled() throws IOException {
677-
// Test with validation options but validation disabled
678-
byte[] randomData = getRandomByteArray(Constants.KB);
679-
StructuredMessageEncoder encoder
680-
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
681-
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
682-
683-
Flux<ByteBuffer> input = Flux.just(encodedData);
684-
685-
DownloadContentValidationOptions validationOptions
686-
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(false);
687-
688-
StepVerifier
689-
.create(bc.upload(input, null, true)
690-
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
691-
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
692-
.assertNext(r -> {
693-
assertNotNull(r);
694-
// Should get encoded data, not decoded
695-
assertTrue(r.length > randomData.length); // Encoded data is larger
696-
})
697-
.verifyComplete();
698-
}
699-
700568
@RequiredServiceVersion(clazz = BlobServiceVersion.class, min = "2024-08-04")
701569
@Test
702570
public void copyFromURLSourceErrorAndStatusCode() {
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.azure.storage.blob.specialized;
5+
6+
import com.azure.core.test.utils.TestUtils;
7+
import com.azure.core.util.FluxUtil;
8+
import com.azure.storage.blob.BlobAsyncClient;
9+
import com.azure.storage.blob.BlobTestBase;
10+
import com.azure.storage.blob.models.BlobRange;
11+
import com.azure.storage.common.DownloadContentValidationOptions;
12+
import com.azure.storage.common.implementation.Constants;
13+
import com.azure.storage.common.implementation.structuredmessage.StructuredMessageEncoder;
14+
import com.azure.storage.common.implementation.structuredmessage.StructuredMessageFlags;
15+
import org.junit.jupiter.api.BeforeEach;
16+
import org.junit.jupiter.api.Test;
17+
import reactor.core.publisher.Flux;
18+
import reactor.test.StepVerifier;
19+
20+
import java.io.IOException;
21+
import java.nio.ByteBuffer;
22+
23+
import static org.junit.jupiter.api.Assertions.assertNotNull;
24+
import static org.junit.jupiter.api.Assertions.assertTrue;
25+
26+
/**
27+
* Tests for structured message decoding during blob downloads using StorageContentValidationDecoderPolicy.
28+
* These tests verify that the pipeline policy correctly decodes structured messages when content validation is enabled.
29+
*/
30+
public class BlobMessageDecoderDownloadTests extends BlobTestBase {
31+
32+
private BlobAsyncClient bc;
33+
34+
@BeforeEach
35+
public void setup() {
36+
String blobName = generateBlobName();
37+
bc = ccAsync.getBlobAsyncClient(blobName);
38+
bc.upload(Flux.just(ByteBuffer.wrap(new byte[0])), null).block();
39+
}
40+
41+
@Test
42+
public void downloadStreamWithResponseContentValidation() throws IOException {
43+
byte[] randomData = getRandomByteArray(Constants.KB);
44+
StructuredMessageEncoder encoder
45+
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
46+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
47+
48+
Flux<ByteBuffer> input = Flux.just(encodedData);
49+
50+
DownloadContentValidationOptions validationOptions
51+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
52+
53+
StepVerifier
54+
.create(bc.upload(input, null, true)
55+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
56+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
57+
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
58+
.verifyComplete();
59+
}
60+
61+
@Test
62+
public void downloadStreamWithResponseContentValidationRange() throws IOException {
63+
byte[] randomData = getRandomByteArray(Constants.KB);
64+
StructuredMessageEncoder encoder
65+
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
66+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
67+
68+
Flux<ByteBuffer> input = Flux.just(encodedData);
69+
70+
DownloadContentValidationOptions validationOptions
71+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
72+
73+
BlobRange range = new BlobRange(0, 512L);
74+
75+
StepVerifier.create(bc.upload(input, null, true)
76+
.then(bc.downloadStreamWithResponse(range, null, null, false, validationOptions))
77+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue()))).assertNext(r -> {
78+
assertNotNull(r);
79+
assertTrue(r.length > 0);
80+
}).verifyComplete();
81+
}
82+
83+
@Test
84+
public void downloadStreamWithResponseContentValidationLargeBlob() throws IOException {
85+
// Test with larger data to verify chunking works correctly
86+
byte[] randomData = getRandomByteArray(5 * Constants.KB);
87+
StructuredMessageEncoder encoder
88+
= new StructuredMessageEncoder(randomData.length, 1024, StructuredMessageFlags.STORAGE_CRC64);
89+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
90+
91+
Flux<ByteBuffer> input = Flux.just(encodedData);
92+
93+
DownloadContentValidationOptions validationOptions
94+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
95+
96+
StepVerifier
97+
.create(bc.upload(input, null, true)
98+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
99+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
100+
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
101+
.verifyComplete();
102+
}
103+
104+
@Test
105+
public void downloadStreamWithResponseContentValidationMultipleSegments() throws IOException {
106+
// Test with multiple segments to ensure all segments are decoded correctly
107+
byte[] randomData = getRandomByteArray(2 * Constants.KB);
108+
StructuredMessageEncoder encoder
109+
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
110+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
111+
112+
Flux<ByteBuffer> input = Flux.just(encodedData);
113+
114+
DownloadContentValidationOptions validationOptions
115+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
116+
117+
StepVerifier
118+
.create(bc.upload(input, null, true)
119+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
120+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
121+
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
122+
.verifyComplete();
123+
}
124+
125+
@Test
126+
public void downloadStreamWithResponseNoValidation() throws IOException {
127+
// Test that download works normally when validation is not enabled
128+
byte[] randomData = getRandomByteArray(Constants.KB);
129+
StructuredMessageEncoder encoder
130+
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
131+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
132+
133+
Flux<ByteBuffer> input = Flux.just(encodedData);
134+
135+
// No validation options - should download encoded data as-is
136+
StepVerifier
137+
.create(bc.upload(input, null, true)
138+
.then(bc.downloadStreamWithResponse(null, null, null, false))
139+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
140+
.assertNext(r -> {
141+
assertNotNull(r);
142+
// Should get encoded data, not decoded
143+
assertTrue(r.length > randomData.length); // Encoded data is larger
144+
})
145+
.verifyComplete();
146+
}
147+
148+
@Test
149+
public void downloadStreamWithResponseValidationDisabled() throws IOException {
150+
// Test with validation options but validation disabled
151+
byte[] randomData = getRandomByteArray(Constants.KB);
152+
StructuredMessageEncoder encoder
153+
= new StructuredMessageEncoder(randomData.length, 512, StructuredMessageFlags.STORAGE_CRC64);
154+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
155+
156+
Flux<ByteBuffer> input = Flux.just(encodedData);
157+
158+
DownloadContentValidationOptions validationOptions
159+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(false);
160+
161+
StepVerifier
162+
.create(bc.upload(input, null, true)
163+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
164+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
165+
.assertNext(r -> {
166+
assertNotNull(r);
167+
// Should get encoded data, not decoded
168+
assertTrue(r.length > randomData.length); // Encoded data is larger
169+
})
170+
.verifyComplete();
171+
}
172+
173+
@Test
174+
public void downloadStreamWithResponseContentValidationSmallSegment() throws IOException {
175+
// Test with small segment size to ensure boundary conditions are handled
176+
byte[] randomData = getRandomByteArray(256);
177+
StructuredMessageEncoder encoder
178+
= new StructuredMessageEncoder(randomData.length, 128, StructuredMessageFlags.STORAGE_CRC64);
179+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
180+
181+
Flux<ByteBuffer> input = Flux.just(encodedData);
182+
183+
DownloadContentValidationOptions validationOptions
184+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
185+
186+
StepVerifier
187+
.create(bc.upload(input, null, true)
188+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
189+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
190+
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
191+
.verifyComplete();
192+
}
193+
194+
@Test
195+
public void downloadStreamWithResponseContentValidationVeryLargeBlob() throws IOException {
196+
// Test with very large data to verify chunking and policy work correctly with large blobs
197+
byte[] randomData = getRandomByteArray(10 * Constants.KB);
198+
StructuredMessageEncoder encoder
199+
= new StructuredMessageEncoder(randomData.length, 2048, StructuredMessageFlags.STORAGE_CRC64);
200+
ByteBuffer encodedData = encoder.encode(ByteBuffer.wrap(randomData));
201+
202+
Flux<ByteBuffer> input = Flux.just(encodedData);
203+
204+
DownloadContentValidationOptions validationOptions
205+
= new DownloadContentValidationOptions().setStructuredMessageValidationEnabled(true);
206+
207+
StepVerifier
208+
.create(bc.upload(input, null, true)
209+
.then(bc.downloadStreamWithResponse(null, null, null, false, validationOptions))
210+
.flatMap(r -> FluxUtil.collectBytesInByteBufferStream(r.getValue())))
211+
.assertNext(r -> TestUtils.assertArraysEqual(r, randomData))
212+
.verifyComplete();
213+
}
214+
}

0 commit comments

Comments
 (0)