using EntKube.Provisioning.Domain; using EntKube.Provisioning.Features.Apps.TriggerSync; using EntKube.Provisioning.Infrastructure; using EntKube.SharedKernel.Domain; using FluentAssertions; namespace EntKube.Provisioning.Tests.Features; public class TriggerSyncHandlerTests { private readonly InMemoryAppRepository repository; private readonly TriggerSyncHandler handler; public TriggerSyncHandlerTests() { repository = new InMemoryAppRepository(); handler = new TriggerSyncHandler(repository); } [Fact] public async Task HandleAsync_SyncedEnvironment_MarksPending() { // Arrange — An environment that was previously synced. The user // wants to force a re-sync (e.g., after a config change outside // the UI or to retry after fixing a problem). App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment); Guid envId = Guid.NewGuid(); AppEnvironment env = app.AddEnvironment(envId, Guid.NewGuid(), "api-dev"); env.ConfigureDeployment(new DeploymentSpec( Image: "api", Tag: "v1", Replicas: 1, ContainerPort: 8080, ServicePort: 80, HostName: null, PathPrefix: null, EnvironmentVariables: null, Resources: null)); env.MarkSynced(); await repository.AddAsync(app); // Act Result result = await handler.HandleAsync(new TriggerSyncRequest(app.Id, envId)); // Assert — Should be marked Pending so the reconciler picks it up. result.IsSuccess.Should().BeTrue(); App? updated = await repository.GetByIdAsync(app.Id); updated!.Environments[0].SyncStatus.Should().Be(AppSyncStatus.Pending); } [Fact] public async Task HandleAsync_ErrorEnvironment_MarksPendingForRetry() { // Arrange — An environment in error state. The admin fixes the issue // and wants to retry the sync. App app = App.Create(Guid.NewGuid(), Guid.NewGuid(), "API", "api", AppType.Deployment); Guid envId = Guid.NewGuid(); AppEnvironment env = app.AddEnvironment(envId, Guid.NewGuid(), "api-dev"); env.ConfigureDeployment(new DeploymentSpec( Image: "api", Tag: "v1", Replicas: 1, ContainerPort: 8080, ServicePort: 80, HostName: null, PathPrefix: null, EnvironmentVariables: null, Resources: null)); env.MarkError("Connection refused"); await repository.AddAsync(app); // Act Result result = await handler.HandleAsync(new TriggerSyncRequest(app.Id, envId)); // Assert result.IsSuccess.Should().BeTrue(); App? updated = await repository.GetByIdAsync(app.Id); updated!.Environments[0].SyncStatus.Should().Be(AppSyncStatus.Pending); } [Fact] public async Task HandleAsync_NonexistentApp_ReturnsFailure() { // Act Result result = await handler.HandleAsync( new TriggerSyncRequest(Guid.NewGuid(), Guid.NewGuid())); // Assert result.IsFailure.Should().BeTrue(); result.Error.Should().Contain("not found"); } }