test(e2e): address Task 0 review — rename GetInstanceDocumentAsync (ownership), add CreateApiKeyAsync (CLI emits prose not JSON)

This commit is contained in:
Joseph Doherty
2026-06-06 11:44:22 -04:00
parent e618137ce7
commit 32240919cc
@@ -283,6 +283,32 @@ public static partial class CliRunner
/// <summary>Best-effort delete of an API method via <c>api-method delete</c> for teardown.</summary>
public static Task DeleteApiMethodAsync(int id) => BestEffortAsync("api-method", "delete", id);
/// <summary>
/// Creates an API key via <c>security api-key create</c> and returns its opaque string
/// <c>keyId</c> (resolved by name from <c>security api-key list</c>).
/// </summary>
/// <remarks>
/// <c>security api-key create</c> prints a human-readable block (not JSON) even under
/// <c>--format json</c>, so this uses <see cref="CliRunner.RunAsync"/> (never
/// <c>RunJsonAsync</c>, which would throw a <c>JsonException</c> 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
/// <see cref="DeleteApiKeyAsync"/> for teardown.
/// </remarks>
/// <param name="name">Unique key name (typically from <see cref="UniqueName"/>).</param>
/// <param name="methods">Comma-separated API method names the key is scoped to.</param>
/// <returns>The new key's opaque <c>keyId</c>.</returns>
/// <exception cref="InvalidOperationException">
/// The CLI failed, or the key could not be resolved by name after creation.
/// </exception>
public static async Task<string> 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'.");
}
/// <summary>
/// Resolves an API key's opaque string <c>keyId</c> from its display name via
/// <c>security api-key list</c>; returns <see langword="null"/> if no key matches.
@@ -327,9 +353,13 @@ public static partial class CliRunner
/// <summary>
/// Reads an instance's full configuration via <c>instance get</c>; the returned document exposes
/// <c>connectionBindings</c>, <c>attributeOverrides</c>, and <c>areaId</c> for persistence read-back.
/// Caller owns the returned <see cref="JsonDocument"/>.
/// </summary>
public static Task<JsonDocument> GetInstanceAsync(int id) =>
/// <remarks>
/// This is the only helper that hands back a live <see cref="JsonDocument"/> (the rest return
/// scalars). The caller OWNS it and MUST wrap the call in <c>using var doc = …</c>; the
/// <c>Document</c> suffix is the signal that this returns a disposable resource, not plain data.
/// </remarks>
public static Task<JsonDocument> GetInstanceDocumentAsync(int id) =>
RunJsonAsync("instance", "get", "--id", id.ToString(System.Globalization.CultureInfo.InvariantCulture));
/// <summary>