feat(auth): ScadaBridge CentralUI pages onto IInboundApiKeyAdmin seam (re-arch C3; string keyId, method-scopes replace ApprovedApiKeyIds, token-once display, approved-keys<->scopes inversion)
This commit is contained in:
+118
@@ -0,0 +1,118 @@
|
||||
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Inbound-API key re-arch (C3): unit tests for <see cref="ApiMethodKeyScopeReconciler"/>, which
|
||||
/// inverts the API-method form's "Approved API Keys" selection into per-key method-scope edits.
|
||||
/// Covers approve, revoke (preserving other scopes), the empty-last-scope guard, and the no-op case.
|
||||
/// </summary>
|
||||
public sealed class ApiMethodKeyScopeReconcilerTests
|
||||
{
|
||||
private static IReadOnlyDictionary<string, IReadOnlyList<string>> Current(
|
||||
params (string KeyId, string[] Methods)[] entries) =>
|
||||
entries.ToDictionary(
|
||||
e => e.KeyId,
|
||||
e => (IReadOnlyList<string>)e.Methods.ToList(),
|
||||
StringComparer.Ordinal);
|
||||
|
||||
private static IReadOnlyDictionary<string, string> Names(params (string KeyId, string Name)[] entries) =>
|
||||
entries.ToDictionary(e => e.KeyId, e => e.Name, StringComparer.Ordinal);
|
||||
|
||||
[Fact]
|
||||
public void Approve_AddsMethodToKey_PreservingExistingScopes()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string> { "k1" },
|
||||
initialKeyIds: new HashSet<string>(),
|
||||
currentMethodsByKey: Current(("k1", new[] { "GetStatus" })),
|
||||
keyNamesById: Names(("k1", "Key One")));
|
||||
|
||||
Assert.Empty(result.EmptyScopeKeyNames);
|
||||
var update = Assert.Single(result.Updates);
|
||||
Assert.Equal("k1", update.KeyId);
|
||||
Assert.Equal(new[] { "GetStatus", "PlaceOrder" }, update.NewMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Approve_KeyWithNoExistingScopes_GetsJustThisMethod()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string> { "k1" },
|
||||
initialKeyIds: new HashSet<string>(),
|
||||
currentMethodsByKey: Current(("k1", Array.Empty<string>())),
|
||||
keyNamesById: Names(("k1", "Key One")));
|
||||
|
||||
var update = Assert.Single(result.Updates);
|
||||
Assert.Equal(new[] { "PlaceOrder" }, update.NewMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Revoke_RemovesMethod_LeavingOtherScopesIntact()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string>(),
|
||||
initialKeyIds: new HashSet<string> { "k1" },
|
||||
currentMethodsByKey: Current(("k1", new[] { "PlaceOrder", "GetStatus" })),
|
||||
keyNamesById: Names(("k1", "Key One")));
|
||||
|
||||
Assert.Empty(result.EmptyScopeKeyNames);
|
||||
var update = Assert.Single(result.Updates);
|
||||
Assert.Equal("k1", update.KeyId);
|
||||
Assert.Equal(new[] { "GetStatus" }, update.NewMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Revoke_LastScope_ReportedAsEmptyConflict_AndNotInUpdates()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string>(),
|
||||
initialKeyIds: new HashSet<string> { "k1" },
|
||||
currentMethodsByKey: Current(("k1", new[] { "PlaceOrder" })),
|
||||
keyNamesById: Names(("k1", "Key One")));
|
||||
|
||||
Assert.Empty(result.Updates);
|
||||
var emptyName = Assert.Single(result.EmptyScopeKeyNames);
|
||||
Assert.Equal("Key One", emptyName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mixed_ApproveOneRevokeAnother_ProducesBothUpdates()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string> { "k2" }, // approve k2
|
||||
initialKeyIds: new HashSet<string> { "k1" }, // revoke k1
|
||||
currentMethodsByKey: Current(
|
||||
("k1", new[] { "PlaceOrder", "GetStatus" }),
|
||||
("k2", new[] { "Ping" })),
|
||||
keyNamesById: Names(("k1", "Key One"), ("k2", "Key Two")));
|
||||
|
||||
Assert.Empty(result.EmptyScopeKeyNames);
|
||||
Assert.Equal(2, result.Updates.Count);
|
||||
|
||||
var k1 = result.Updates.Single(u => u.KeyId == "k1");
|
||||
Assert.Equal(new[] { "GetStatus" }, k1.NewMethods);
|
||||
|
||||
var k2 = result.Updates.Single(u => u.KeyId == "k2");
|
||||
Assert.Equal(new[] { "Ping", "PlaceOrder" }, k2.NewMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoChange_ProducesNoUpdates()
|
||||
{
|
||||
var result = ApiMethodKeyScopeReconciler.Reconcile(
|
||||
methodName: "PlaceOrder",
|
||||
selectedKeyIds: new HashSet<string> { "k1" },
|
||||
initialKeyIds: new HashSet<string> { "k1" },
|
||||
currentMethodsByKey: Current(("k1", new[] { "PlaceOrder" })),
|
||||
keyNamesById: Names(("k1", "Key One")));
|
||||
|
||||
Assert.Empty(result.Updates);
|
||||
Assert.Empty(result.EmptyScopeKeyNames);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user