Skip to content

Commit 90808d2

Browse files
committed
[fix] Improve upload resilience coverage
Cover FileSlicer cancellation cleanup paths and address the async disposal warning reported by SonarCloud. Made-with: Cursor
1 parent 0c83f26 commit 90808d2

3 files changed

Lines changed: 53 additions & 3 deletions

File tree

src/ByteSync.Client/Services/Communications/Transfers/Uploading/FileSlicer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ private async Task<bool> TryWriteSliceAsync(FileUploaderSlice fileUploaderSlice)
169169
}
170170
catch (ChannelClosedException) when (_exceptionOccurred.WaitOne(0))
171171
{
172-
fileUploaderSlice.MemoryStream.Dispose();
172+
await fileUploaderSlice.MemoryStream.DisposeAsync();
173173

174174
return false;
175175
}

src/ByteSync.Client/Services/Communications/Transfers/Uploading/UploadAttemptTimeoutPolicy.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static int ComputeTimeoutSeconds(long sliceLengthBytes, int attempt, int
1919
}
2020

2121
var staleChunkPenalty = ComputeStaleChunkPenaltySeconds(sliceLengthBytes, currentChunkSizeBytes);
22-
timeoutSec += (attempt - 1) * RetryGrowthSeconds + staleChunkPenalty;
22+
timeoutSec += (long)(attempt - 1) * RetryGrowthSeconds + staleChunkPenalty;
2323

2424
return (int)Math.Clamp(timeoutSec, AttemptTimeoutFloorSeconds, AttemptTimeoutCeilingSeconds);
2525
}

tests/ByteSync.Client.UnitTests/Services/Communications/Transfers/Uploading/FileSlicerTests.cs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,57 @@ public async Task SliceAndEncryptAsync_WithNoSlices_ShouldCompleteNormally()
168168
// Channel should be completed
169169
_availableSlices.Reader.Completion.IsCompleted.Should().BeTrue();
170170
}
171-
171+
172+
[Test]
173+
public async Task SliceAndEncryptAsync_WhenChannelClosesAfterSliceCreated_ShouldDisposeSlice()
174+
{
175+
// Arrange
176+
var stream = new MemoryStream(new byte[128]);
177+
var slice = new FileUploaderSlice(1, stream);
178+
179+
_mockSlicerEncrypter.Setup(x => x.SliceAndEncrypt())
180+
.Callback(() =>
181+
{
182+
_exceptionOccurred.Set();
183+
_availableSlices.Writer.TryComplete();
184+
})
185+
.ReturnsAsync(slice);
186+
187+
// Act
188+
await _fileSlicer.SliceAndEncryptAsync(_sharedFileDefinition, _progressState);
189+
190+
// Assert
191+
_progressState.TotalCreatedSlices.Should().Be(1);
192+
var readDisposedStream = () => _ = stream.Length;
193+
readDisposedStream.Should().Throw<ObjectDisposedException>();
194+
}
195+
196+
[Test]
197+
public async Task SliceAndEncryptAdaptiveAsync_WhenChannelClosesAfterSliceCreated_ShouldDisposeSlice()
198+
{
199+
// Arrange
200+
var stream = new MemoryStream(new byte[128]);
201+
var slice = new FileUploaderSlice(1, stream);
202+
203+
_mockAdaptiveController.Setup(x => x.CurrentChunkSizeBytes).Returns(64 * 1024);
204+
_mockAdaptiveController.Setup(x => x.GetNextChunkSizeBytes()).Returns(64 * 1024);
205+
_mockSlicerEncrypter.Setup(x => x.SliceAndEncrypt())
206+
.Callback(() =>
207+
{
208+
_exceptionOccurred.Set();
209+
_availableSlices.Writer.TryComplete();
210+
})
211+
.ReturnsAsync(slice);
212+
213+
// Act
214+
await _fileSlicer.SliceAndEncryptAdaptiveAsync(_sharedFileDefinition, _progressState);
215+
216+
// Assert
217+
_progressState.TotalCreatedSlices.Should().Be(1);
218+
var readDisposedStream = () => _ = stream.Length;
219+
readDisposedStream.Should().Throw<ObjectDisposedException>();
220+
}
221+
172222
[Test]
173223
public async Task SliceAndEncryptAsync_WithMultipleSlices_ShouldProcessAllSlices()
174224
{

0 commit comments

Comments
 (0)