Files

327 lines
14 KiB
C#

using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Moq;
using NUnit.Framework;
using Strata.ContinuousImprovement.Api.Controllers;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace Strata.ContinuousImprovement.Api.Test.Unit.Controllers
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Unit"), Category("Controller")]
public class StrategicOpportunityControllerTests
{
private StrategicOpportunityController _controller;
private Mock<IStrategicOpportunityService> _mockOpportunityService;
[SetUp]
public void SetUp()
{
_mockOpportunityService = new Mock<IStrategicOpportunityService>();
_controller = new StrategicOpportunityController(_mockOpportunityService.Object);
}
// --- GetGeneralInfo ---
[Test]
public async Task GetGeneralInfo_ReturnsOk_WhenOpportunityExists()
{
const long opportunityId = 1;
var info = new StrategicOpportunityGeneralInfo();
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityGeneralInfo(opportunityId, It.IsAny<CancellationToken>()))
.ReturnsAsync(info);
var result = await _controller.GetGeneralInfo(opportunityId, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>()
.Which.Value.Should().Be(info);
}
[Test]
public async Task GetGeneralInfo_ReturnsNotFound_WhenOpportunityMissing()
{
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityGeneralInfo(It.IsAny<long>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((StrategicOpportunityGeneralInfo)null);
var result = await _controller.GetGeneralInfo(99, CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- GetDrawerDetails ---
[Test]
public async Task GetDrawerDetails_ReturnsOk_WhenOpportunityExists()
{
const long opportunityId = 5;
var details = new StrategicOpportunityDrawerDetails();
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityDrawerDetails(opportunityId, It.IsAny<CancellationToken>()))
.ReturnsAsync(details);
var result = await _controller.GetDrawerDetails(opportunityId, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>()
.Which.Value.Should().Be(details);
}
[Test]
public async Task GetDrawerDetails_ReturnsNotFound_WhenOpportunityMissing()
{
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityDrawerDetails(It.IsAny<long>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((StrategicOpportunityDrawerDetails)null);
var result = await _controller.GetDrawerDetails(99, CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- Copy ---
[Test]
public async Task Copy_ReturnsOk_WithNewId_WhenSuccessful()
{
const long opportunityId = 10;
const long newId = 42;
_mockOpportunityService
.Setup(s => s.CopyStrategicOpportunity(opportunityId, It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(newId);
var result = await _controller.Copy(opportunityId, "Copy of Opp", CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>()
.Which.Value.Should().Be(newId);
}
[Test]
public async Task Copy_ReturnsBadRequest_WhenServiceReturnsZero()
{
_mockOpportunityService
.Setup(s => s.CopyStrategicOpportunity(It.IsAny<long>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(0L);
var result = await _controller.Copy(10, "Copy of Opp", CancellationToken.None);
result.Result.Should().BeOfType<BadRequestResult>();
}
// --- SetStatus ---
[Test]
public async Task SetStatus_ReturnsOk_WhenUpdateSucceeds()
{
const long opportunityId = 3;
var dto = new StrategicOpportunityUpdateStatusDto();
_mockOpportunityService
.Setup(s => s.UpdateStrategicOpportunityStatus(opportunityId, dto, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result = await _controller.SetStatus(opportunityId, dto, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>();
}
[Test]
public async Task SetStatus_ReturnsNotFound_WhenUpdateFails()
{
_mockOpportunityService
.Setup(s => s.UpdateStrategicOpportunityStatus(It.IsAny<long>(), It.IsAny<StrategicOpportunityUpdateStatusDto>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
var result = await _controller.SetStatus(99, new StrategicOpportunityUpdateStatusDto(), CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- UpdateLayout ---
[Test]
public async Task UpdateLayout_ReturnsOk_WhenUpdateSucceeds()
{
const long opportunityId = 7;
var layout = new StrategicOpportunityLayout();
_mockOpportunityService
.Setup(s => s.UpdateStrategicOpportunityLayout(opportunityId, layout, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result = await _controller.UpdateLayout(opportunityId, layout, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>();
}
[Test]
public async Task UpdateLayout_ReturnsNotFound_WhenUpdateFails()
{
_mockOpportunityService
.Setup(s => s.UpdateStrategicOpportunityLayout(It.IsAny<long>(), It.IsAny<StrategicOpportunityLayout>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
var result = await _controller.UpdateLayout(99, new StrategicOpportunityLayout(), CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- UpdateRoles ---
[Test]
public async Task UpdateRoles_ReturnsOk_WhenUpdateSucceeds()
{
const long opportunityId = 4;
var dto = new StrategicRolesUpdate();
_mockOpportunityService
.Setup(s => s.UpdateRoles(opportunityId, dto, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result = await _controller.UpdateRoles(opportunityId, dto, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>();
}
[Test]
public async Task UpdateRoles_ReturnsNotFound_WhenUpdateFails()
{
_mockOpportunityService
.Setup(s => s.UpdateRoles(It.IsAny<long>(), It.IsAny<StrategicRolesUpdate>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
var result = await _controller.UpdateRoles(99, new StrategicRolesUpdate(), CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- RunTrackingTask ---
[Test]
public async Task RunTrackingTask_ReturnsOk_WhenSuccessful()
{
const long opportunityId = 8;
_mockOpportunityService
.Setup(s => s.RunTrackingTask(opportunityId, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result = await _controller.RunTrackingTask(opportunityId, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>();
}
[Test]
public async Task RunTrackingTask_ReturnsNotFound_WhenOpportunityMissing()
{
_mockOpportunityService
.Setup(s => s.RunTrackingTask(It.IsAny<long>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
var result = await _controller.RunTrackingTask(99, CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- GetMeasures ---
[Test]
public async Task GetMeasures_ReturnsOk_WhenMeasuresExist()
{
const long opportunityId = 2;
var measures = new List<StrategicOpportunityMeasureDto> { new() };
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityMeasures(opportunityId, It.IsAny<CancellationToken>()))
.ReturnsAsync(measures);
var result = await _controller.GetMeasures(opportunityId, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>()
.Which.Value.Should().Be(measures);
}
[Test]
public async Task GetMeasures_ReturnsNotFound_WhenNoMeasures()
{
_mockOpportunityService
.Setup(s => s.GetStrategicOpportunityMeasures(It.IsAny<long>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new List<StrategicOpportunityMeasureDto>());
var result = await _controller.GetMeasures(99, CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- CompleteInitiative ---
[Test]
public async Task CompleteInitiative_ReturnsOk_WhenSuccessful()
{
const long opportunityId = 6;
var data = new StrategicOpportunityCompleteInitiativeData();
_mockOpportunityService
.Setup(s => s.CompleteInitiative(opportunityId, data, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
var result = await _controller.CompleteInitiative(opportunityId, data, CancellationToken.None);
result.Result.Should().BeOfType<OkObjectResult>();
}
[Test]
public async Task CompleteInitiative_ReturnsNotFound_WhenOpportunityMissing()
{
_mockOpportunityService
.Setup(s => s.CompleteInitiative(It.IsAny<long>(), It.IsAny<StrategicOpportunityCompleteInitiativeData>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(false);
var result = await _controller.CompleteInitiative(99, new StrategicOpportunityCompleteInitiativeData(), CancellationToken.None);
result.Result.Should().BeOfType<NotFoundResult>();
}
// --- Route attribute assertions ---
[Test]
public void Controller_HasExpectedRouteTemplate()
{
var routeAttr = typeof(StrategicOpportunityController).GetCustomAttribute<RouteAttribute>();
routeAttr.Should().NotBeNull();
routeAttr.Template.Should().Be("api/v{api-version:apiVersion}/[controller]");
}
[TestCase(nameof(StrategicOpportunityController.GetOpportunities), typeof(HttpGetAttribute), "")]
[TestCase(nameof(StrategicOpportunityController.GetGeneralInfo), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.IsNameValid), typeof(HttpPostAttribute), "[action]")]
[TestCase(nameof(StrategicOpportunityController.GetLayout), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.UpdateLayout), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.SetStatus), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.GetInitiatives), typeof(HttpGetAttribute), "[action]")]
[TestCase(nameof(StrategicOpportunityController.UpdateInitiative), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.UpdateRoles), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.UpdateRampUps), typeof(HttpPutAttribute), "{opportunityId}/ramp-ups")]
[TestCase(nameof(StrategicOpportunityController.GetRampUps), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.RunTrackingTask), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.CompleteInitiative), typeof(HttpPutAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.GetMeasures), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.GetPrimaryMeasure), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.GetMeasureDetails), typeof(HttpGetAttribute), "{measureId}/[action]")]
[TestCase(nameof(StrategicOpportunityController.GetDrawerDetails), typeof(HttpGetAttribute), "{opportunityId}")]
[TestCase(nameof(StrategicOpportunityController.Copy), typeof(HttpPostAttribute), "{opportunityId}/copy")]
public void ActionRoute_HasExpectedHttpMethodAndTemplate(string methodName, Type httpAttrType, string expectedTemplate)
{
var method = typeof(StrategicOpportunityController).GetMethod(methodName);
method.Should().NotBeNull($"method '{methodName}' should exist on {nameof(StrategicOpportunityController)}");
var attr = method.GetCustomAttribute(httpAttrType);
attr.Should().NotBeNull($"{methodName} should be decorated with [{httpAttrType.Name}]");
var template = (string)attr.GetType().GetProperty("Template").GetValue(attr);
template.Should().Be(expectedTemplate);
}
}
}