diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Cluster/CliRunner.Helpers.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Cluster/CliRunner.Helpers.cs index 6ca4ffee..bf8b1725 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Cluster/CliRunner.Helpers.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.PlaywrightTests/Cluster/CliRunner.Helpers.cs @@ -283,6 +283,32 @@ public static partial class CliRunner /// Best-effort delete of an API method via api-method delete for teardown. public static Task DeleteApiMethodAsync(int id) => BestEffortAsync("api-method", "delete", id); + /// + /// Creates an API key via security api-key create and returns its opaque string + /// keyId (resolved by name from security api-key list). + /// + /// + /// security api-key create prints a human-readable block (not JSON) even under + /// --format json, so this uses (never + /// RunJsonAsync, which would throw a JsonException on that output) and then + /// resolves the new key's id by its unique name. Use this for tests that need a + /// pre-existing key to act on (enable/disable/delete); pair with + /// for teardown. + /// + /// Unique key name (typically from ). + /// Comma-separated API method names the key is scoped to. + /// The new key's opaque keyId. + /// + /// The CLI failed, or the key could not be resolved by name after creation. + /// + public static async Task CreateApiKeyAsync(string name, string methods) + { + await RunAsync("security", "api-key", "create", "--name", name, "--methods", methods); + return await ResolveApiKeyIdByNameAsync(name) + ?? throw new InvalidOperationException( + $"API key '{name}' was created but could not be resolved by name in 'security api-key list'."); + } + /// /// Resolves an API key's opaque string keyId from its display name via /// security api-key list; returns if no key matches. @@ -327,9 +353,13 @@ public static partial class CliRunner /// /// Reads an instance's full configuration via instance get; the returned document exposes /// connectionBindings, attributeOverrides, and areaId for persistence read-back. - /// Caller owns the returned . /// - public static Task GetInstanceAsync(int id) => + /// + /// This is the only helper that hands back a live (the rest return + /// scalars). The caller OWNS it and MUST wrap the call in using var doc = …; the + /// Document suffix is the signal that this returns a disposable resource, not plain data. + /// + public static Task GetInstanceDocumentAsync(int id) => RunJsonAsync("instance", "get", "--id", id.ToString(System.Globalization.CultureInfo.InvariantCulture)); ///