using Microsoft.Extensions.Logging.Abstractions; using Moq; using StellaOps.Router.Common.Models; using StellaOps.TestKit; namespace StellaOps.Microservice.Tests; /// /// Unit tests for . /// public sealed class EndpointDiscoveryServiceTests { private readonly Mock _discoveryProviderMock; private readonly Mock _yamlLoaderMock; private readonly Mock _mergerMock; public EndpointDiscoveryServiceTests() { _discoveryProviderMock = new Mock(); _yamlLoaderMock = new Mock(); _mergerMock = new Mock(); // Default setups _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()) .Returns(new List()); _yamlLoaderMock.Setup(l => l.Load()) .Returns((MicroserviceYamlConfig?)null); _mergerMock.Setup(m => m.Merge(It.IsAny>(), It.IsAny())) .Returns, MicroserviceYamlConfig?>((e, _) => e); } private EndpointDiscoveryService CreateService() { return new EndpointDiscoveryService( _discoveryProviderMock.Object, _yamlLoaderMock.Object, _mergerMock.Object, NullLogger.Instance); } #region DiscoverEndpoints Tests [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_CallsDiscoveryProvider() { // Arrange var service = CreateService(); // Act service.DiscoverEndpoints(); // Assert _discoveryProviderMock.Verify(d => d.DiscoverEndpoints(), Times.Once); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_LoadsYamlConfig() { // Arrange var service = CreateService(); // Act service.DiscoverEndpoints(); // Assert _yamlLoaderMock.Verify(l => l.Load(), Times.Once); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_MergesCodeAndYaml() { // Arrange var codeEndpoints = new List { new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users" } }; _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()).Returns(codeEndpoints); var yamlConfig = new MicroserviceYamlConfig { Endpoints = [ new EndpointOverrideConfig { Method = "GET", Path = "/api/users", DefaultTimeout = "30s" } ] }; _yamlLoaderMock.Setup(l => l.Load()).Returns(yamlConfig); var service = CreateService(); // Act service.DiscoverEndpoints(); // Assert _mergerMock.Verify(m => m.Merge(codeEndpoints, yamlConfig), Times.Once); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_ReturnsMergedEndpoints() { // Arrange var codeEndpoints = new List { new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users" } }; _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()).Returns(codeEndpoints); var mergedEndpoints = new List { new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users", DefaultTimeout = TimeSpan.FromSeconds(30) } }; _mergerMock.Setup(m => m.Merge(It.IsAny>(), It.IsAny())) .Returns(mergedEndpoints); var service = CreateService(); // Act var result = service.DiscoverEndpoints(); // Assert result.Should().BeSameAs(mergedEndpoints); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_WhenYamlLoadFails_UsesCodeEndpointsOnly() { // Arrange var codeEndpoints = new List { new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users" } }; _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()).Returns(codeEndpoints); _yamlLoaderMock.Setup(l => l.Load()).Throws(new FileNotFoundException("YAML not found")); var service = CreateService(); // Act service.DiscoverEndpoints(); // Assert - merger should be called with null config _mergerMock.Verify(m => m.Merge(codeEndpoints, null), Times.Once); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_WithMultipleEndpoints_ReturnsAll() { // Arrange var endpoints = new List { new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users" }, new() { ServiceName = "test", Version = "1.0", Method = "POST", Path = "/api/users" }, new() { ServiceName = "test", Version = "1.0", Method = "GET", Path = "/api/users/{id}" }, new() { ServiceName = "test", Version = "1.0", Method = "DELETE", Path = "/api/users/{id}" } }; _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()).Returns(endpoints); _mergerMock.Setup(m => m.Merge(endpoints, null)).Returns(endpoints); var service = CreateService(); // Act var result = service.DiscoverEndpoints(); // Assert result.Should().HaveCount(4); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_EmptyEndpoints_ReturnsEmptyList() { // Arrange _discoveryProviderMock.Setup(d => d.DiscoverEndpoints()).Returns(new List()); _mergerMock.Setup(m => m.Merge(It.IsAny>(), null)) .Returns(new List()); var service = CreateService(); // Act var result = service.DiscoverEndpoints(); // Assert result.Should().BeEmpty(); } [Trait("Category", TestCategories.Unit)] [Fact] public void DiscoverEndpoints_CanBeCalledMultipleTimes() { // Arrange var service = CreateService(); // Act var result1 = service.DiscoverEndpoints(); var result2 = service.DiscoverEndpoints(); // Assert _discoveryProviderMock.Verify(d => d.DiscoverEndpoints(), Times.Exactly(2)); } #endregion }