Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,29 @@ internal static async Task<bool> UntilTrueAsync(

return false;
}

/// <summary>
/// Invokes <paramref name="action"/> until its result satisfies
/// <paramref name="predicate"/> or the attempts are exhausted, and returns
/// that result. Used for read-after-write against the OS secret stores,
/// where a just-completed add isn't always immediately visible to the next
/// query under concurrent (multi-targeted) test runs. Returns the last
/// result even if the predicate never held, so the caller's assertion still
/// reports the real failure rather than a timeout.
/// </summary>
internal static async Task<T> UntilAsync<T>(
Func<Task<T>> action,
Func<T, bool> predicate,
int maxAttempts = 20,
int delayMs = 25)
{
var result = await action();
for (var attempt = 1; !predicate(result) && attempt < maxAttempts; attempt++)
{
await Task.Delay(delayMs);
result = await action();
}

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public async Task ExportCredentialsAsync_ReturnsDecryptedPayloadAndSelection()
var id = await manager.AddCredentialAsync("Adobe", "prod", "Production", "{\"apiKey\":\"secret\"}");
Assert.True(await manager.SelectCredentialAsync(id));

var adobe = Assert.Single(await manager.ExportCredentialsAsync());
var adobe = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));
Assert.Equal(id, adobe.AccountId);
Assert.Equal("prod", adobe.AccountName);
Assert.Equal("Adobe", adobe.ProviderName);
Expand All @@ -94,13 +94,13 @@ public async Task RestoreCredentialAsync_PreservesAccountIdAndSelection()
var manager = NewManager();
var id = await manager.AddCredentialAsync("Adobe", "prod", "Production", "{\"apiKey\":\"secret\"}");
_ = await manager.SelectCredentialAsync(id);
var exported = Assert.Single(await manager.ExportCredentialsAsync());
var exported = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));

// Simulate an import: drop it, then restore from the export record.
Assert.True(await RetryHelper.UntilTrueAsync(() => manager.DeleteCredentialAsync(id)));
await manager.RestoreCredentialAsync(exported);

var restored = Assert.Single(await manager.ExportCredentialsAsync());
var restored = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));
Assert.Equal(id, restored.AccountId);
Assert.True(restored.IsSelected);
Assert.Equal("{\"apiKey\":\"secret\"}", await manager.GetSelectedCredentialAsync("Adobe"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public async Task ExportCredentialsAsync_ReturnsDecryptedPayloadAndSelection()
var id = await manager.AddCredentialAsync("Adobe", "prod", "Production", "{\"apiKey\":\"secret\"}");
Assert.True(await manager.SelectCredentialAsync(id));

var adobe = Assert.Single(await manager.ExportCredentialsAsync());
var adobe = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));
Assert.Equal(id, adobe.AccountId);
Assert.Equal("prod", adobe.AccountName);
Assert.Equal("Adobe", adobe.ProviderName);
Expand All @@ -129,13 +129,13 @@ public async Task RestoreCredentialAsync_PreservesAccountIdCreatedAtAndSelection
var manager = NewManager();
var id = await manager.AddCredentialAsync("Adobe", "prod", "Production", "{\"apiKey\":\"secret\"}");
_ = await manager.SelectCredentialAsync(id);
var exported = Assert.Single(await manager.ExportCredentialsAsync());
var exported = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));

// Simulate an import: drop it, then restore from the export record.
Assert.True(await RetryHelper.UntilTrueAsync(() => manager.DeleteCredentialAsync(id)));
await manager.RestoreCredentialAsync(exported);

var restored = Assert.Single(await manager.ExportCredentialsAsync());
var restored = Assert.Single(await RetryHelper.UntilAsync(() => manager.ExportCredentialsAsync(), r => r.Count == 1));
Assert.Equal(id, restored.AccountId);
Assert.Equal(exported.CreatedAt, restored.CreatedAt); // libsecret preserves it via attribute
Assert.True(restored.IsSelected);
Expand Down