Update tests (#1742)

This commit is contained in:
Leendert de Borst
2026-02-19 12:22:43 +01:00
parent 236de7670a
commit ea24c7e415
5 changed files with 88 additions and 46 deletions

View File

@@ -1,9 +1,11 @@
@if (_errors.Any())
{
@foreach (var error in _errors)
{
<AlertMessageError Message="@error" />
}
<div class="messages-container">
@foreach (var error in _errors)
{
<AlertMessageError Message="@error" />
}
</div>
}
@code {

View File

@@ -40,6 +40,7 @@ public sealed class DbService : IDisposable
private readonly ILogger<DbService> _logger;
private readonly GlobalNotificationService _globalNotificationService;
private readonly IStringLocalizer _sharedLocalizer;
private readonly CancellationTokenSource _backgroundSyncCts = new();
private SettingsService _settingsService = new();
private SqliteConnection? _sqlConnection;
private AliasClientDbContext _dbContext;
@@ -210,44 +211,79 @@ public sealed class DbService : IDisposable
// Set state to indicate background sync is pending
_state.UpdateState(DbServiceState.DatabaseStatus.BackgroundSyncPending);
// Capture cancellation token for this background operation
var cancellationToken = _backgroundSyncCts.Token;
// Fire and forget the background save operation
_ = Task.Run(async () =>
{
try
_ = Task.Run(
async () =>
{
// Prune expired items from trash before saving.
await PruneExpiredTrashItemsAsync();
// Make sure a public/private RSA encryption key exists before saving the database.
await GetOrCreateEncryptionKeyAsync();
var encryptedBase64String = await GetEncryptedDatabaseBase64String();
// Update state to show we're actively syncing
_state.UpdateState(DbServiceState.DatabaseStatus.SavingToServer);
// Save to webapi.
var success = await SaveToServerAsync(encryptedBase64String);
if (success)
try
{
_logger.LogInformation("Database successfully saved to server (background sync).");
if (cancellationToken.IsCancellationRequested || _disposed)
{
return;
}
// Prune expired items from trash before saving.
await PruneExpiredTrashItemsAsync();
if (cancellationToken.IsCancellationRequested || _disposed)
{
return;
}
// Make sure a public/private RSA encryption key exists before saving the database.
await GetOrCreateEncryptionKeyAsync();
if (cancellationToken.IsCancellationRequested || _disposed)
{
return;
}
var encryptedBase64String = await GetEncryptedDatabaseBase64String();
if (cancellationToken.IsCancellationRequested || _disposed)
{
return;
}
// Update state to show we're actively syncing
_state.UpdateState(DbServiceState.DatabaseStatus.SavingToServer);
// Save to webapi.
var success = await SaveToServerAsync(encryptedBase64String);
if (success)
{
_logger.LogInformation("Database successfully saved to server (background sync).");
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
else
{
_logger.LogWarning("Background sync to server failed.");
_globalNotificationService.AddErrorMessage(
"Failed to sync changes to server. Your changes are saved locally and will be synced on next refresh.");
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
}
catch (OperationCanceledException)
{
// Background sync was cancelled (e.g., during logout), this is expected
_logger.LogDebug("Background database sync was cancelled.");
}
catch (Exception ex) when (_disposed || cancellationToken.IsCancellationRequested)
{
// Service was disposed during sync, silently ignore
_logger.LogDebug(ex, "Background database sync aborted due to disposal.");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during background database sync.");
_globalNotificationService.AddErrorMessage(_sharedLocalizer["ErrorUnknown"]);
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
else
{
_logger.LogWarning("Background sync to server failed.");
_globalNotificationService.AddErrorMessage(
"Failed to sync changes to server. Your changes are saved locally and will be synced on next refresh.");
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Error during background database sync.");
_globalNotificationService.AddErrorMessage(_sharedLocalizer["ErrorUnknown"]);
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
});
},
cancellationToken);
}
/// <summary>
@@ -1127,6 +1163,9 @@ public sealed class DbService : IDisposable
if (disposing)
{
// Cancel any pending background sync operations first
_backgroundSyncCts.Cancel();
_backgroundSyncCts.Dispose();
_sqlConnection?.Dispose();
}

View File

@@ -42,16 +42,16 @@ public class TwoFactorAuthBase : ClientPlaywrightTest
// Press the confirm disable button as well.
await confirmButton.ClickAsync();
// Check if the success message is displayed.
// Check if the success message is displayed
var expectedMessage = "Two-factor authentication is now successfully disabled.";
var successMessageLocator = Page.Locator($"div[role='alert']:has-text('{expectedMessage}')");
var successMessageLocator = Page.Locator($".messages-container div[role='alert']:has-text('{expectedMessage}')");
await successMessageLocator.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 5000,
});
var message = await Page.TextContentAsync("div[role='alert']");
var message = await successMessageLocator.TextContentAsync();
Assert.That(message, Does.Contain("Two-factor authentication is now successfully disabled."), "No two-factor auth disable success message displayed.");
}
}

View File

@@ -27,14 +27,14 @@ public class TwoFactorAuthTests : TwoFactorAuthBase
await DisableTwoFactorIfEnabled();
var (totpCode, _) = await EnableTwoFactor();
// Check if the success message is displayed.
var successMessage = Page.Locator("div[role='alert']");
// Check if the success message is displayed (target the app notification area, not the error UI in index.html).
var successMessage = Page.Locator(".messages-container div[role='alert']");
await successMessage.WaitForAsync(new LocatorWaitForOptions
{
State = WaitForSelectorState.Visible,
Timeout = 5000,
});
var message = await Page.TextContentAsync("div[role='alert']");
var message = await successMessage.TextContentAsync();
Assert.That(message, Does.Contain("Two-factor authentication is now successfully enabled."), "No success message displayed.");
await Logout();

View File

@@ -160,7 +160,8 @@ public class AuthTests : ClientPlaywrightTest
await deleteButton.ClickAsync();
// Check for error message about wrong username
var warning = await Page.TextContentAsync("div[role='alert']");
var warningLocator = Page.Locator(".messages-container div[role='alert']");
var warning = await warningLocator.TextContentAsync();
Assert.That(warning, Does.Contain("The username you entered does not match your current username"), "No warning shown when attempting to delete account with wrong username.");
// Try with correct username
@@ -178,7 +179,7 @@ public class AuthTests : ClientPlaywrightTest
await confirmButton.ClickAsync();
// Check for error message about wrong password
warning = await Page.TextContentAsync("div[role='alert']");
warning = await warningLocator.TextContentAsync();
Assert.That(warning, Does.Contain("The provided password does not match"), "No warning shown when attempting to delete account with wrong password.");
// Fill in correct password
@@ -201,7 +202,7 @@ public class AuthTests : ClientPlaywrightTest
var loginButton = await WaitForAndGetElement("button[type='submit']");
await loginButton.ClickAsync();
warning = await Page.TextContentAsync("div[role='alert']");
warning = await warningLocator.TextContentAsync();
Assert.That(warning, Does.Contain("Invalid username or password"), "No error shown when attempting to login with deleted account.");
}