test(e2e): add CliRunner helpers for data-connection, api-method, api-key teardown, instance read-back
This commit is contained in:
@@ -224,6 +224,94 @@ public static partial class CliRunner
|
||||
/// <param name="id">Site id.</param>
|
||||
public static Task DeleteSiteAsync(int id) => BestEffortAsync("site", "delete", id);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a data connection on a site via <c>data-connection create</c> and returns its new <c>id</c>.
|
||||
/// </summary>
|
||||
public static async Task<int> CreateDataConnectionAsync(int siteId, string name, string protocol = "OpcUa", string? primaryConfig = null)
|
||||
{
|
||||
var inv = System.Globalization.CultureInfo.InvariantCulture;
|
||||
var args = new List<string>
|
||||
{
|
||||
"data-connection", "create",
|
||||
"--site-id", siteId.ToString(inv),
|
||||
"--name", name,
|
||||
"--protocol", protocol,
|
||||
};
|
||||
if (!string.IsNullOrEmpty(primaryConfig))
|
||||
{
|
||||
args.Add("--primary-config");
|
||||
args.Add(primaryConfig);
|
||||
}
|
||||
|
||||
using var doc = await RunJsonAsync([.. args]);
|
||||
return RequireId(doc, "data-connection create");
|
||||
}
|
||||
|
||||
/// <summary>Best-effort delete of a data connection via <c>data-connection delete</c> for teardown.</summary>
|
||||
public static Task DeleteDataConnectionAsync(int id) => BestEffortAsync("data-connection", "delete", id);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an inbound API method via <c>api-method create</c> (so it appears as a checkbox in the
|
||||
/// API-key form) and returns its new <c>id</c>.
|
||||
/// </summary>
|
||||
public static async Task<int> CreateApiMethodAsync(string name, string script = "return null;")
|
||||
{
|
||||
using var doc = await RunJsonAsync("api-method", "create", "--name", name, "--script", script);
|
||||
return RequireId(doc, "api-method create");
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public static async Task<string?> ResolveApiKeyIdByNameAsync(string name)
|
||||
{
|
||||
using var doc = await RunJsonAsync("security", "api-key", "list");
|
||||
if (doc.RootElement.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var key in doc.RootElement.EnumerateArray())
|
||||
{
|
||||
if (key.TryGetProperty("name", out var n)
|
||||
&& n.ValueKind == JsonValueKind.String
|
||||
&& string.Equals(n.GetString(), name, StringComparison.Ordinal)
|
||||
&& key.TryGetProperty("keyId", out var k)
|
||||
&& k.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
return k.GetString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort delete of an API key via <c>security api-key delete --key-id</c> for teardown.
|
||||
/// The key id is an opaque string, so this cannot use the int-based <see cref="BestEffortAsync"/>.
|
||||
/// </summary>
|
||||
public static async Task DeleteApiKeyAsync(string keyId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await RunAsync("security", "api-key", "delete", "--key-id", keyId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Best-effort teardown — never mask the test's own failure.
|
||||
}
|
||||
}
|
||||
|
||||
/// <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) =>
|
||||
RunJsonAsync("instance", "get", "--id", id.ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
|
||||
/// <summary>
|
||||
/// Returns the ids of all templates whose <c>name</c> starts with
|
||||
/// <paramref name="prefix"/>, via <c>template list</c>.
|
||||
|
||||
+41
@@ -43,4 +43,45 @@ public class CliRunnerHelpersTests
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
Assert.True(await CliRunner.ResolveSiteIdAsync("site-a") > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A freshly created data connection returns a positive id and is cleanly
|
||||
/// deleted in teardown, exercising <see cref="CliRunner.CreateDataConnectionAsync"/>
|
||||
/// and <see cref="CliRunner.DeleteDataConnectionAsync"/> as a round-trip.
|
||||
/// </summary>
|
||||
[SkippableFact]
|
||||
public async Task CreateThenDeleteDataConnection_RoundTrips()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
var siteId = await CliRunner.ResolveSiteIdAsync("site-a");
|
||||
var id = await CliRunner.CreateDataConnectionAsync(siteId, CliRunner.UniqueName("conn"));
|
||||
try
|
||||
{
|
||||
Assert.True(id > 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CliRunner.DeleteDataConnectionAsync(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A freshly created API method returns a positive id and is cleanly deleted in
|
||||
/// teardown, exercising <see cref="CliRunner.CreateApiMethodAsync"/> and
|
||||
/// <see cref="CliRunner.DeleteApiMethodAsync"/> as a round-trip.
|
||||
/// </summary>
|
||||
[SkippableFact]
|
||||
public async Task CreateThenDeleteApiMethod_RoundTrips()
|
||||
{
|
||||
Skip.IfNot(await ClusterAvailability.IsAvailableAsync(), ClusterAvailability.SkipReason);
|
||||
var id = await CliRunner.CreateApiMethodAsync(CliRunner.UniqueName("method"));
|
||||
try
|
||||
{
|
||||
Assert.True(id > 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await CliRunner.DeleteApiMethodAsync(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user