refactor: address code review findings across all projects
Apply comprehensive fixes from code reviews including: - Extract shared utilities (SqlFormatHelper, CellValueConverter, DbDestinationBase) - Add interface abstractions (IAuthenticationService, IDatabaseMigrator, IMisQueryBuilder) - Implement SecureStore for encrypted secrets storage - Fix error handling with proper HTTP status codes and logging - Optimize double enumeration in DevEtlRegistry - Add DataSync.Dev README for developer onboarding - Extract filter panel base classes to reduce duplication - Update code review docs to mark all issues as fixed
This commit is contained in:
@@ -25,7 +25,7 @@ public class ServiceRegistrationTests
|
||||
// Act
|
||||
services.AddInfrastructure(configuration);
|
||||
var provider = services.BuildServiceProvider();
|
||||
var authService = provider.GetRequiredService<IAuthService>();
|
||||
var authService = provider.GetRequiredService<IAuthenticationService>();
|
||||
|
||||
// Assert
|
||||
authService.ShouldBeOfType<FakeAuthService>();
|
||||
@@ -41,7 +41,7 @@ public class ServiceRegistrationTests
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||
{
|
||||
["Auth:UseFakeAuth"] = "false",
|
||||
["Ldap:UseFakeAuth"] = "false",
|
||||
["Ldap:ServerUrls:0"] = "ldap://localhost:389",
|
||||
["Ldap:SearchBase"] = "DC=example,DC=com"
|
||||
})
|
||||
@@ -50,7 +50,7 @@ public class ServiceRegistrationTests
|
||||
// Act
|
||||
services.AddInfrastructure(configuration);
|
||||
var provider = services.BuildServiceProvider();
|
||||
var authService = provider.GetRequiredService<IAuthService>();
|
||||
var authService = provider.GetRequiredService<IAuthenticationService>();
|
||||
|
||||
// Assert
|
||||
authService.ShouldBeOfType<LdapAuthService>();
|
||||
@@ -74,7 +74,7 @@ public class ServiceRegistrationTests
|
||||
// Act
|
||||
services.AddInfrastructure(configuration);
|
||||
var provider = services.BuildServiceProvider();
|
||||
var authService = provider.GetRequiredService<IAuthService>();
|
||||
var authService = provider.GetRequiredService<IAuthenticationService>();
|
||||
|
||||
// Assert
|
||||
authService.ShouldBeOfType<LdapAuthService>();
|
||||
|
||||
@@ -14,6 +14,11 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Shouldly;
|
||||
|
||||
// Alias to avoid conflict with JdeScoping.Core.Interfaces.IAuthenticationService
|
||||
using IAspNetAuthService = Microsoft.AspNetCore.Authentication.IAuthenticationService;
|
||||
// Alias to match AuthController naming convention
|
||||
using IAuthService = JdeScoping.Core.Interfaces.IAuthenticationService;
|
||||
|
||||
namespace JdeScoping.Api.Tests.Controllers;
|
||||
|
||||
public class AuthControllerTests
|
||||
@@ -186,14 +191,14 @@ public class AuthControllerTests
|
||||
|
||||
private static HttpContext CreateMockHttpContext()
|
||||
{
|
||||
var authServiceMock = Substitute.For<IAuthenticationService>();
|
||||
var authServiceMock = Substitute.For<IAspNetAuthService>();
|
||||
authServiceMock.SignOutAsync(Arg.Any<HttpContext>(), Arg.Any<string>(), Arg.Any<AuthenticationProperties>())
|
||||
.Returns(Task.CompletedTask);
|
||||
authServiceMock.SignInAsync(Arg.Any<HttpContext>(), Arg.Any<string>(), Arg.Any<ClaimsPrincipal>(), Arg.Any<AuthenticationProperties>())
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
var serviceProvider = Substitute.For<IServiceProvider>();
|
||||
serviceProvider.GetService(typeof(IAuthenticationService)).Returns(authServiceMock);
|
||||
serviceProvider.GetService(typeof(IAspNetAuthService)).Returns(authServiceMock);
|
||||
|
||||
var httpContext = new DefaultHttpContext
|
||||
{
|
||||
|
||||
@@ -77,15 +77,15 @@ public class FileControllerTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UploadWorkOrders_NoFile_ReturnsError()
|
||||
public async Task UploadWorkOrders_NoFile_ReturnsBadRequest()
|
||||
{
|
||||
// Act
|
||||
var result = await _controller.UploadWorkOrders(null, CancellationToken.None);
|
||||
|
||||
// Assert
|
||||
result.Result.ShouldBeOfType<OkObjectResult>();
|
||||
var okResult = (OkObjectResult)result.Result!;
|
||||
var uploadResult = okResult.Value.ShouldBeOfType<FileUploadResult<WorkOrderViewModel>>();
|
||||
// Assert - should return BadRequest for validation errors, not 200 OK
|
||||
result.Result.ShouldBeOfType<BadRequestObjectResult>();
|
||||
var badRequestResult = (BadRequestObjectResult)result.Result!;
|
||||
var uploadResult = badRequestResult.Value.ShouldBeOfType<FileUploadResult<WorkOrderViewModel>>();
|
||||
uploadResult.WasSuccessful.ShouldBeFalse();
|
||||
uploadResult.ErrorMessage.ShouldBe("No file uploaded");
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class SearchControllerTests
|
||||
private readonly ILotFinderRepository _repository;
|
||||
private readonly IHubContext<StatusHub> _hubContext;
|
||||
private readonly ILogger<SearchController> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly SearchController _controller;
|
||||
|
||||
public SearchControllerTests()
|
||||
@@ -27,7 +28,8 @@ public class SearchControllerTests
|
||||
_repository = Substitute.For<ILotFinderRepository>();
|
||||
_hubContext = Substitute.For<IHubContext<StatusHub>>();
|
||||
_logger = Substitute.For<ILogger<SearchController>>();
|
||||
_controller = new SearchController(_repository, _hubContext, _logger);
|
||||
_timeProvider = TimeProvider.System;
|
||||
_controller = new SearchController(_repository, _hubContext, _logger, _timeProvider);
|
||||
SetupAuthenticatedUser("testuser");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using JdeScoping.Api.Hubs;
|
||||
using JdeScoping.Core.ViewModels;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Shouldly;
|
||||
@@ -9,13 +10,17 @@ namespace JdeScoping.Api.Tests.Hubs;
|
||||
|
||||
public class StatusHubTests
|
||||
{
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly ILogger<StatusHub> _logger;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly StatusHub _hub;
|
||||
|
||||
public StatusHubTests()
|
||||
{
|
||||
_cache = new MemoryCache(new MemoryCacheOptions());
|
||||
_logger = Substitute.For<ILogger<StatusHub>>();
|
||||
_hub = new StatusHub(_logger);
|
||||
_timeProvider = TimeProvider.System;
|
||||
_hub = new StatusHub(_cache, _logger, _timeProvider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -47,34 +52,33 @@ public class StatusHubTests
|
||||
"statusUpdate",
|
||||
Arg.Is<object?[]>(args => args.Length == 1 && args[0] == statusUpdate),
|
||||
Arg.Any<CancellationToken>());
|
||||
|
||||
// Assert - verify status was cached
|
||||
var cachedStatus = _hub.GetCachedStatus();
|
||||
cachedStatus.Message.ShouldBe("Processing");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCachedStatus_ReturnsLastSetStatus()
|
||||
{
|
||||
// The hub uses a static cached status, so this test checks the initial state
|
||||
// Note: Due to static state, tests may affect each other if run in parallel
|
||||
|
||||
// Act
|
||||
var status = _hub.GetCachedStatus();
|
||||
|
||||
// Assert
|
||||
status.ShouldNotBeNull();
|
||||
// Initial message is "Unknown"
|
||||
status.Message.ShouldNotBeNullOrEmpty();
|
||||
status.Message.ShouldBe("Unknown");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetCachedStatus_InitialStatusIsUnknown()
|
||||
{
|
||||
// This test verifies the default initial state
|
||||
// Note: This test assumes no other test has modified the static state
|
||||
|
||||
// Act
|
||||
var status = _hub.GetCachedStatus();
|
||||
|
||||
// Assert
|
||||
status.ShouldNotBeNull();
|
||||
status.Message.ShouldBe("Unknown");
|
||||
// The timestamp should be set
|
||||
status.Timestamp.ShouldNotBe(default);
|
||||
}
|
||||
|
||||
@@ -51,19 +51,6 @@ public class FakeAuthServiceTests
|
||||
result.User.Username.ShouldBe("testuser");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetUserInfoAsync_ReturnsUserInfo()
|
||||
{
|
||||
// Act
|
||||
var result = await _service.GetUserInfoAsync("testuser");
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Username.ShouldBe("testuser");
|
||||
result.FirstName.ShouldBe("Dev");
|
||||
result.LastName.ShouldBe("User");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthenticateAsync_DisplayNameComputedCorrectly()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user