- Implemented tests for RouterConfig, RoutingOptions, StaticInstanceConfig, and RouterConfigOptions to ensure default values are set correctly. - Added tests for RouterConfigProvider to validate configurations and ensure defaults are returned when no file is specified. - Created tests for ConfigValidationResult to check success and error scenarios. - Developed tests for ServiceCollectionExtensions to verify service registration for RouterConfig. - Introduced UdpTransportTests to validate serialization, connection, request-response, and error handling in UDP transport. - Added scripts for signing authority gaps and hashing DevPortal SDK snippets.
383 lines
12 KiB
C#
383 lines
12 KiB
C#
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Moq;
|
|
using StellaOps.Microservice;
|
|
using StellaOps.Router.Common.Models;
|
|
using Xunit;
|
|
|
|
namespace StellaOps.Microservice.Tests;
|
|
|
|
/// <summary>
|
|
/// Tests for EndpointOverrideMerger - verifies merge logic and precedence.
|
|
/// </summary>
|
|
public class EndpointOverrideMergerTests
|
|
{
|
|
private readonly EndpointOverrideMerger _merger;
|
|
private readonly Mock<ILogger<EndpointOverrideMerger>> _loggerMock;
|
|
|
|
public EndpointOverrideMergerTests()
|
|
{
|
|
_loggerMock = new Mock<ILogger<EndpointOverrideMerger>>();
|
|
_merger = new EndpointOverrideMerger(_loggerMock.Object);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_WithNullYamlConfig_ReturnsCodeEndpointsUnchanged()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/test", TimeSpan.FromSeconds(30))
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, null);
|
|
|
|
result.Should().BeEquivalentTo(codeEndpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_WithEmptyYamlConfig_ReturnsCodeEndpointsUnchanged()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/test", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig { Endpoints = [] };
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().BeEquivalentTo(codeEndpoints);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_OverridesTimeout_WhenYamlSpecifiesTimeout()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("POST", "/api/generate", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "POST",
|
|
Path = "/api/generate",
|
|
DefaultTimeout = "5m"
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(5));
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_OverridesStreaming_WhenYamlSpecifiesStreaming()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/data", TimeSpan.FromSeconds(30), supportsStreaming: false)
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "GET",
|
|
Path = "/api/data",
|
|
SupportsStreaming = true
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].SupportsStreaming.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_OverridesClaims_WhenYamlSpecifiesClaims()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("DELETE", "/api/users/{id}", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "DELETE",
|
|
Path = "/api/users/{id}",
|
|
RequiringClaims =
|
|
[
|
|
new ClaimRequirementConfig { Type = "role", Value = "admin" }
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].RequiringClaims.Should().HaveCount(1);
|
|
result[0].RequiringClaims![0].Type.Should().Be("role");
|
|
result[0].RequiringClaims[0].Value.Should().Be("admin");
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_PreservesCodeDefaults_WhenYamlDoesNotOverride()
|
|
{
|
|
var originalTimeout = TimeSpan.FromSeconds(45);
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/test", originalTimeout, supportsStreaming: true)
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "GET",
|
|
Path = "/api/test"
|
|
// No overrides specified
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].DefaultTimeout.Should().Be(originalTimeout);
|
|
result[0].SupportsStreaming.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_MatchesCaseInsensitively()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/Test", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "get", // lowercase
|
|
Path = "/API/TEST", // uppercase
|
|
DefaultTimeout = "1m"
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(1));
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_LeavesUnmatchedEndpointsUnchanged()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/one", TimeSpan.FromSeconds(10)),
|
|
CreateEndpoint("POST", "/api/two", TimeSpan.FromSeconds(20)),
|
|
CreateEndpoint("PUT", "/api/three", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "POST",
|
|
Path = "/api/two",
|
|
DefaultTimeout = "5m"
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(3);
|
|
result[0].DefaultTimeout.Should().Be(TimeSpan.FromSeconds(10)); // unchanged
|
|
result[1].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(5)); // overridden
|
|
result[2].DefaultTimeout.Should().Be(TimeSpan.FromSeconds(30)); // unchanged
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_LogsWarning_WhenYamlOverrideDoesNotMatchAnyEndpoint()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/existing", TimeSpan.FromSeconds(30))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "POST",
|
|
Path = "/api/nonexistent",
|
|
DefaultTimeout = "5m"
|
|
}
|
|
]
|
|
};
|
|
|
|
_merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
_loggerMock.Verify(
|
|
x => x.Log(
|
|
LogLevel.Warning,
|
|
It.IsAny<EventId>(),
|
|
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains("does not match any code endpoint")),
|
|
It.IsAny<Exception>(),
|
|
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
|
|
Times.Once);
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_AppliesMultipleOverrides()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
CreateEndpoint("GET", "/api/one", TimeSpan.FromSeconds(10)),
|
|
CreateEndpoint("POST", "/api/two", TimeSpan.FromSeconds(20))
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "GET",
|
|
Path = "/api/one",
|
|
DefaultTimeout = "1m"
|
|
},
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "POST",
|
|
Path = "/api/two",
|
|
DefaultTimeout = "2m"
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(2);
|
|
result[0].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(1));
|
|
result[1].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(2));
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_PreservesOriginalEndpointProperties()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
new()
|
|
{
|
|
ServiceName = "test-service",
|
|
Version = "2.0.0",
|
|
Method = "GET",
|
|
Path = "/api/test",
|
|
DefaultTimeout = TimeSpan.FromSeconds(30),
|
|
SupportsStreaming = false,
|
|
HandlerType = typeof(object)
|
|
}
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "GET",
|
|
Path = "/api/test",
|
|
DefaultTimeout = "1m"
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result.Should().HaveCount(1);
|
|
result[0].ServiceName.Should().Be("test-service");
|
|
result[0].Version.Should().Be("2.0.0");
|
|
result[0].Method.Should().Be("GET");
|
|
result[0].Path.Should().Be("/api/test");
|
|
result[0].DefaultTimeout.Should().Be(TimeSpan.FromMinutes(1));
|
|
result[0].HandlerType.Should().Be(typeof(object));
|
|
}
|
|
|
|
[Fact]
|
|
public void Merge_YamlOverridesCodeClaims_Completely()
|
|
{
|
|
var codeEndpoints = new List<EndpointDescriptor>
|
|
{
|
|
new()
|
|
{
|
|
ServiceName = "test-service",
|
|
Version = "1.0.0",
|
|
Method = "GET",
|
|
Path = "/api/test",
|
|
DefaultTimeout = TimeSpan.FromSeconds(30),
|
|
RequiringClaims =
|
|
[
|
|
new ClaimRequirement { Type = "original", Value = "claim" }
|
|
]
|
|
}
|
|
};
|
|
var yamlConfig = new MicroserviceYamlConfig
|
|
{
|
|
Endpoints =
|
|
[
|
|
new EndpointOverrideConfig
|
|
{
|
|
Method = "GET",
|
|
Path = "/api/test",
|
|
RequiringClaims =
|
|
[
|
|
new ClaimRequirementConfig { Type = "new", Value = "claim1" },
|
|
new ClaimRequirementConfig { Type = "new", Value = "claim2" }
|
|
]
|
|
}
|
|
]
|
|
};
|
|
|
|
var result = _merger.Merge(codeEndpoints, yamlConfig);
|
|
|
|
result[0].RequiringClaims.Should().HaveCount(2);
|
|
result[0].RequiringClaims!.All(c => c.Type == "new").Should().BeTrue();
|
|
}
|
|
|
|
private static EndpointDescriptor CreateEndpoint(
|
|
string method,
|
|
string path,
|
|
TimeSpan timeout,
|
|
bool supportsStreaming = false)
|
|
{
|
|
return new EndpointDescriptor
|
|
{
|
|
ServiceName = "test-service",
|
|
Version = "1.0.0",
|
|
Method = method,
|
|
Path = path,
|
|
DefaultTimeout = timeout,
|
|
SupportsStreaming = supportsStreaming
|
|
};
|
|
}
|
|
}
|