ad7f9e731f
v2-ci / build (push) Failing after 50s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
A thin gateway over the admin-operations cluster singleton so CI/scripts can trigger a deployment without the Blazor button. Forwards to the same IAdminOperationsClient. StartDeploymentAsync; mounted on admin-role nodes. Auth is a fixed-time X-Api-Key check against Security:DeployApiKey (orthogonal to the cookie-only web auth); AllowAnonymous so the auth fallback doesn't 401 it, self-disabling (503) until the key is set. Outcome->status: 202/200/409/422. Unit tests for the key check + outcome mapping; HTTP E2E (real auth + real deploy via the 2-node harness). Documented in docs/security.md.
59 lines
2.9 KiB
C#
59 lines
2.9 KiB
C#
using Microsoft.AspNetCore.Http;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Api;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Api;
|
|
|
|
/// <summary>Unit tests for the pure helpers behind the headless deploy REST endpoint —
|
|
/// <see cref="DeployApiEndpoints.IsAuthorized"/> (fixed-time API-key check) and
|
|
/// <see cref="DeployApiEndpoints.ToResult"/> (outcome → HTTP status mapping).</summary>
|
|
public sealed class DeployApiEndpointsTests
|
|
{
|
|
// ── IsAuthorized ────────────────────────────────────────────────────────────
|
|
|
|
/// <summary>Verifies the exact configured key authorizes.</summary>
|
|
[Fact]
|
|
public void IsAuthorized_true_for_matching_key()
|
|
=> DeployApiEndpoints.IsAuthorized("s3cret", "s3cret").ShouldBeTrue();
|
|
|
|
/// <summary>Verifies a wrong key is rejected.</summary>
|
|
[Fact]
|
|
public void IsAuthorized_false_for_wrong_key()
|
|
=> DeployApiEndpoints.IsAuthorized("nope", "s3cret").ShouldBeFalse();
|
|
|
|
/// <summary>Verifies a missing/empty provided key is rejected.</summary>
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
public void IsAuthorized_false_for_missing_provided_key(string? provided)
|
|
=> DeployApiEndpoints.IsAuthorized(provided, "s3cret").ShouldBeFalse();
|
|
|
|
/// <summary>Verifies that when no key is configured nothing authorizes (endpoint stays closed).</summary>
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
public void IsAuthorized_false_when_no_key_configured(string? configured)
|
|
=> DeployApiEndpoints.IsAuthorized("anything", configured).ShouldBeFalse();
|
|
|
|
// ── ToResult ────────────────────────────────────────────────────────────────
|
|
|
|
/// <summary>Verifies each deployment outcome maps to the right HTTP status.</summary>
|
|
[Theory]
|
|
[InlineData(StartDeploymentOutcome.Accepted, StatusCodes.Status202Accepted)]
|
|
[InlineData(StartDeploymentOutcome.NoChanges, StatusCodes.Status200OK)]
|
|
[InlineData(StartDeploymentOutcome.AnotherDeploymentInFlight, StatusCodes.Status409Conflict)]
|
|
[InlineData(StartDeploymentOutcome.Rejected, StatusCodes.Status422UnprocessableEntity)]
|
|
public void ToResult_maps_outcome_to_status(StartDeploymentOutcome outcome, int expectedStatus)
|
|
{
|
|
var result = DeployApiEndpoints.ToResult(
|
|
new StartDeploymentResult(outcome, DeploymentId: null, RevisionHash: null,
|
|
Message: "x", CorrelationId: CorrelationId.NewId()));
|
|
|
|
result.ShouldBeAssignableTo<IStatusCodeHttpResult>();
|
|
((IStatusCodeHttpResult)result).StatusCode.ShouldBe(expectedStatus);
|
|
}
|
|
}
|