From 805b635e37086acc80550089294d2e903ba8e588 Mon Sep 17 00:00:00 2001 From: raghulraja Date: Tue, 25 Nov 2025 19:29:08 +0530 Subject: [PATCH] 504-[Feature]: Add delete functionality on filesystem and delete storagestate file in case of issue --- .../StorageStateUserManagerModuleTests.cs | 48 +++++++++++++++++++ .../StorageStateUserManagerModule.cs | 43 ++++++++++++++--- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/testengine.user.storagestate.tests/StorageStateUserManagerModuleTests.cs b/src/testengine.user.storagestate.tests/StorageStateUserManagerModuleTests.cs index 92a5ba768..7c3864fb5 100644 --- a/src/testengine.user.storagestate.tests/StorageStateUserManagerModuleTests.cs +++ b/src/testengine.user.storagestate.tests/StorageStateUserManagerModuleTests.cs @@ -7,7 +7,11 @@ using Microsoft.PowerApps.TestEngine.System; using Microsoft.PowerApps.TestEngine.TestInfra; using Microsoft.PowerApps.TestEngine.Tests.Helpers; +using Microsoft.PowerApps.TestEngine.Users; using Moq; +using System; +using System.IO; +using System.Threading.Tasks; using Xunit; namespace testengine.user.storagestate.tests @@ -237,5 +241,49 @@ public async Task ProtectandUnprotect() Assert.Equal(DATA, unprotected); } } + + [Fact] + public void Protect_Should_Delete_File_When_Encryption_Fails() + { + // Arrange + var testFile = "test-state.json"; + var testContent = "{\"cookies\":[{\"name\":\"test\",\"value\":\"data\"}]}\""; + + MockFileSystem.Setup(f => f.ReadAllText(testFile)).Returns(testContent); + MockFileSystem.Setup(f => f.WriteTextToFile(testFile, It.IsAny(), true)) + .Throws(new System.Security.Cryptography.CryptographicException("Encryption failed")); + MockFileSystem.Setup(f => f.Delete(testFile)); + + var userManager = new StorageStateUserManagerModule(); + + // Act & Assert + Assert.Throws(() => + userManager.Protect(MockFileSystem.Object, testFile)); + + // Verify file was deleted in finally block + MockFileSystem.Verify(f => f.Delete(testFile), Times.Once, + "File should be deleted when encryption fails"); + } + + [Fact] + public void Protect_Should_Delete_File_When_ReadAllText_Fails() + { + // Arrange + var testFile = "test-state.json"; + + MockFileSystem.Setup(f => f.ReadAllText(testFile)) + .Throws(new IOException("Failed to read file")); + MockFileSystem.Setup(f => f.Delete(testFile)); + + var userManager = new StorageStateUserManagerModule(); + + // Act & Assert + Assert.Throws(() => + userManager.Protect(MockFileSystem.Object, testFile)); + + // Verify file was deleted in finally block + MockFileSystem.Verify(f => f.Delete(testFile), Times.Once, + "File should be deleted when read fails"); + } } } diff --git a/src/testengine.user.storagestate/StorageStateUserManagerModule.cs b/src/testengine.user.storagestate/StorageStateUserManagerModule.cs index 6c6f46889..e4e6486fe 100644 --- a/src/testengine.user.storagestate/StorageStateUserManagerModule.cs +++ b/src/testengine.user.storagestate/StorageStateUserManagerModule.cs @@ -220,15 +220,43 @@ public async Task LoginAsUserAsync( CallbackErrorFound = async () => { var stateFile = Path.Combine(Location, "state.json"); - await context.StorageStateAsync(new BrowserContextStorageStateOptions { Path = stateFile }); - Protect(fileSystem, stateFile); + try + { + await context.StorageStateAsync(new BrowserContextStorageStateOptions { Path = stateFile }); + Protect(fileSystem, stateFile); + logger.LogInformation("Storage state saved saved successfully"); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to save storage state after error"); + // Ensure cleanup happens even if storage state save fails + if (fileSystem.FileExists(stateFile)) + { + fileSystem.Delete(stateFile); + logger.LogDebug($"Deleted unprotected state file: {stateFile}"); + } + } }, CallbackDesiredUrlFound = async (match) => { - var stateFile = Path.Combine(Location, "state.json"); - await context.StorageStateAsync(new BrowserContextStorageStateOptions { Path = stateFile }); - Protect(fileSystem, stateFile); + try + { + await context.StorageStateAsync(new BrowserContextStorageStateOptions { Path = stateFile }); + Protect(fileSystem, stateFile); + logger.LogInformation("Storage state saved successfully"); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to save storage state after successful login"); + // Ensure cleanup happens even if storage state save fails + if (fileSystem.FileExists(stateFile)) + { + fileSystem.Delete(stateFile); + logger.LogDebug($"Deleted unprotected state file: {stateFile}"); + } + throw; + } }, CallbackRedirectRequiredFound = !UseStaticContext ? null : async (matchPage) => { @@ -244,8 +272,9 @@ public async Task LoginAsUserAsync( } } } - catch + catch (Exception ex) { + logger.LogWarning(ex, "Failed to redirect to desired URL"); } }, Module = this @@ -269,7 +298,7 @@ public async Task LoginAsUserAsync( logger.LogError(ex, "Error waiting for login"); } - if (!state.FoundMatch || !state.IsError) + if (!state.FoundMatch && !state.IsError) { logger.LogDebug($"Desired page not found, waiting {DateTime.Now.Subtract(started).TotalSeconds}"); System.Threading.Thread.Sleep(1000);