From b9fe62d687f4b33fd06f5d172dee7472d9d8812c Mon Sep 17 00:00:00 2001 From: Robert Emmett Date: Mon, 22 Dec 2025 09:18:20 -0700 Subject: [PATCH 1/2] fix: delete existing destination file before rename in DiskObjectStorage Add check to delete destination file if it exists before moving file in RenameAsync method. This prevents IOException when attempting to rename/move a file to a path that already exists. --- src/Centeva.ObjectStorage/Builtin/DiskObjectStorage.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Centeva.ObjectStorage/Builtin/DiskObjectStorage.cs b/src/Centeva.ObjectStorage/Builtin/DiskObjectStorage.cs index 923a22d..d9b272c 100644 --- a/src/Centeva.ObjectStorage/Builtin/DiskObjectStorage.cs +++ b/src/Centeva.ObjectStorage/Builtin/DiskObjectStorage.cs @@ -103,6 +103,8 @@ public Task RenameAsync(StoragePath sourcePath, StoragePath destinationPath, Can if (File.Exists(filePath)) { + if (File.Exists(newFilePath)) + File.Delete(newFilePath); File.Move(filePath, newFilePath); } From 7892688ea4fe48f9a016b4e51c3150047e78d0ab Mon Sep 17 00:00:00 2001 From: Robert Emmett Date: Mon, 22 Dec 2025 12:05:16 -0700 Subject: [PATCH 2/2] Adds unit test for RenameAsync when the destination file already exists --- .../CommonObjectStorageTests.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/Centeva.ObjectStorage.IntegrationTests/CommonObjectStorageTests.cs b/test/Centeva.ObjectStorage.IntegrationTests/CommonObjectStorageTests.cs index fc35f0b..6afd7f6 100644 --- a/test/Centeva.ObjectStorage.IntegrationTests/CommonObjectStorageTests.cs +++ b/test/Centeva.ObjectStorage.IntegrationTests/CommonObjectStorageTests.cs @@ -245,6 +245,39 @@ public async Task RenameAsync_RenamesObject() content.ShouldBe(_testFileContent); } + [Fact] + public async Task RenameAsync_WithExistingDestination_OverwritesDestination() + { + // Arrange + var originalPath = await WriteToRandomPathAsync(); + var destinationPath = RandomStoragePath(); + + // Create a copy at the destination path first + var differentContent = "Different content that should be overwritten."; + await _sut.WriteAsync(destinationPath, new MemoryStream(Encoding.UTF8.GetBytes(differentContent))); + + // Verify both files exist before rename + (await _sut.ExistsAsync(originalPath)).ShouldBeTrue(); + (await _sut.ExistsAsync(destinationPath)).ShouldBeTrue(); + + // Act + await _sut.RenameAsync(originalPath, destinationPath); + + // Assert + // Check that the original object no longer exists + (await _sut.ExistsAsync(originalPath)).ShouldBeFalse(); + + // Check that the destination object still exists + (await _sut.ExistsAsync(destinationPath)).ShouldBeTrue(); + + // Check that the destination content matches the original content + using var stream = await _sut.OpenReadAsync(destinationPath); + stream.ShouldNotBeNull(); + using var reader = new StreamReader(stream!); + var content = await reader.ReadToEndAsync(); + content.ShouldBe(_testFileContent); + } + [Fact] public async Task GetAsync_RetrievesStorageEntry() {