Template
338 lines
16 KiB
C#
338 lines
16 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 StrategicChargeCodeControllerTests
|
|
{
|
|
private StrategicChargeCodeController _controller;
|
|
private Mock<IStrategicOpportunityService> _mockOpportunityService;
|
|
private Mock<IStrategicChargeCodeOpportunityDetailService> _mockDetailService;
|
|
|
|
[SetUp]
|
|
public void SetUp()
|
|
{
|
|
_mockOpportunityService = new Mock<IStrategicOpportunityService>();
|
|
_mockDetailService = new Mock<IStrategicChargeCodeOpportunityDetailService>();
|
|
_controller = new StrategicChargeCodeController(_mockOpportunityService.Object, _mockDetailService.Object);
|
|
}
|
|
|
|
// --- GetOpportunityChargeCode ---
|
|
|
|
[Test]
|
|
public async Task GetOpportunityChargeCode_ReturnsOk_WhenFound()
|
|
{
|
|
const long opportunityId = 1;
|
|
var data = new StrategicOpportunityChargeCodeEditData();
|
|
_mockOpportunityService
|
|
.Setup(s => s.GetStrategicOpportunityChargeCode(opportunityId, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(data);
|
|
|
|
var result = await _controller.GetOpportunityChargeCode(opportunityId, CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<OkObjectResult>()
|
|
.Which.Value.Should().Be(data);
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetOpportunityChargeCode_ReturnsNotFound_WhenMissing()
|
|
{
|
|
_mockOpportunityService
|
|
.Setup(s => s.GetStrategicOpportunityChargeCode(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((StrategicOpportunityChargeCodeEditData)null);
|
|
|
|
var result = await _controller.GetOpportunityChargeCode(99, CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<NotFoundResult>();
|
|
}
|
|
|
|
// --- CreateOpportunityChargeCode ---
|
|
|
|
[Test]
|
|
public async Task CreateOpportunityChargeCode_ReturnsOk_WithNewId_WhenSuccessful()
|
|
{
|
|
const long newId = 42;
|
|
var dto = new StrategicOpportunityChargeCodeDto();
|
|
_mockOpportunityService
|
|
.Setup(s => s.CreateOpportunityChargeCodeAsync(dto, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(newId);
|
|
|
|
var result = await _controller.CreateOpportunityChargeCode(dto, CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<OkObjectResult>()
|
|
.Which.Value.Should().Be(newId);
|
|
}
|
|
|
|
[Test]
|
|
public async Task CreateOpportunityChargeCode_ReturnsBadRequest_WhenServiceReturnsZero()
|
|
{
|
|
_mockOpportunityService
|
|
.Setup(s => s.CreateOpportunityChargeCodeAsync(It.IsAny<StrategicOpportunityChargeCodeDto>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(0L);
|
|
|
|
var result = await _controller.CreateOpportunityChargeCode(new StrategicOpportunityChargeCodeDto(), CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<BadRequestResult>();
|
|
}
|
|
|
|
// --- UpdateOpportunityChargeCode ---
|
|
|
|
[Test]
|
|
public async Task UpdateOpportunityChargeCode_ReturnsOk_WhenSuccessful()
|
|
{
|
|
const long opportunityId = 5;
|
|
_mockOpportunityService
|
|
.Setup(s => s.UpdateOpportunityChargeCode(opportunityId, It.IsAny<StrategicOpportunityChargeCodeUpdateDto>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(opportunityId);
|
|
|
|
var result = await _controller.UpdateOpportunityChargeCode(opportunityId, new StrategicOpportunityChargeCodeUpdateDto(), CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<OkObjectResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task UpdateOpportunityChargeCode_ReturnsNotFound_WhenServiceReturnsZero()
|
|
{
|
|
_mockOpportunityService
|
|
.Setup(s => s.UpdateOpportunityChargeCode(It.IsAny<long>(), It.IsAny<StrategicOpportunityChargeCodeUpdateDto>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(0L);
|
|
|
|
var result = await _controller.UpdateOpportunityChargeCode(99, new StrategicOpportunityChargeCodeUpdateDto(), CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<NotFoundResult>();
|
|
}
|
|
|
|
// --- DeleteOpportunityChargeCode ---
|
|
|
|
[Test]
|
|
public async Task DeleteOpportunityChargeCode_ReturnsNotFound_WhenOpportunityDoesNotExist()
|
|
{
|
|
_mockOpportunityService
|
|
.Setup(s => s.ContainsAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(false);
|
|
|
|
var result = await _controller.DeleteOpportunityChargeCode(99, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<NotFoundResult>();
|
|
_mockOpportunityService.Verify(s => s.DeleteChargeCodeOpportunityAsync(It.IsAny<long>(), It.IsAny<CancellationToken>()), Times.Never);
|
|
}
|
|
|
|
[Test]
|
|
public async Task DeleteOpportunityChargeCode_ReturnsNoContent_WhenDeleted()
|
|
{
|
|
const long opportunityId = 7;
|
|
_mockOpportunityService.Setup(s => s.ContainsAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
|
_mockOpportunityService.Setup(s => s.DeleteChargeCodeOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
|
|
|
var result = await _controller.DeleteOpportunityChargeCode(opportunityId, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<NoContentResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task DeleteOpportunityChargeCode_ReturnsBadRequest_WhenDeleteFails()
|
|
{
|
|
const long opportunityId = 7;
|
|
_mockOpportunityService.Setup(s => s.ContainsAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(true);
|
|
_mockOpportunityService.Setup(s => s.DeleteChargeCodeOpportunityAsync(opportunityId, It.IsAny<CancellationToken>())).ReturnsAsync(false);
|
|
|
|
var result = await _controller.DeleteOpportunityChargeCode(opportunityId, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<BadRequestResult>();
|
|
}
|
|
|
|
// --- GetGoalDetail ---
|
|
|
|
[Test]
|
|
public async Task GetGoalDetail_ReturnsOk_WhenFound()
|
|
{
|
|
const long opportunityId = 3;
|
|
var getData = new StrategicOpportunityGetDetailData();
|
|
var data = new StrategicOpportunityDetailData();
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(data);
|
|
|
|
var result = await _controller.GetGoalDetail(opportunityId, getData, CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<OkObjectResult>()
|
|
.Which.Value.Should().Be(data);
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetGoalDetail_ReturnsNotFound_WhenMissing()
|
|
{
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(It.IsAny<long>(), false, It.IsAny<StrategicOpportunityGetDetailData>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((StrategicOpportunityDetailData)null);
|
|
|
|
var result = await _controller.GetGoalDetail(99, new StrategicOpportunityGetDetailData(), CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<NotFoundResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetGoalDetail_PassesIsTrackingFalse_ToService()
|
|
{
|
|
const long opportunityId = 3;
|
|
var getData = new StrategicOpportunityGetDetailData();
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new StrategicOpportunityDetailData());
|
|
|
|
await _controller.GetGoalDetail(opportunityId, getData, CancellationToken.None);
|
|
|
|
_mockDetailService.Verify(s => s.GetStrategicOpportunityDetailData(opportunityId, false, getData, CancellationToken.None), Times.Once);
|
|
}
|
|
|
|
// --- GetTrackingDetail ---
|
|
|
|
[Test]
|
|
public async Task GetTrackingDetail_ReturnsOk_WhenFound()
|
|
{
|
|
const long opportunityId = 4;
|
|
var getData = new StrategicOpportunityGetDetailData();
|
|
var data = new StrategicOpportunityDetailData();
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(data);
|
|
|
|
var result = await _controller.GetTrackingDetail(opportunityId, getData, CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<OkObjectResult>()
|
|
.Which.Value.Should().Be(data);
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetTrackingDetail_ReturnsNotFound_WhenMissing()
|
|
{
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(It.IsAny<long>(), true, It.IsAny<StrategicOpportunityGetDetailData>(), It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((StrategicOpportunityDetailData)null);
|
|
|
|
var result = await _controller.GetTrackingDetail(99, new StrategicOpportunityGetDetailData(), CancellationToken.None);
|
|
|
|
result.Result.Should().BeOfType<NotFoundResult>();
|
|
}
|
|
|
|
[Test]
|
|
public async Task GetTrackingDetail_PassesIsTrackingTrue_ToService()
|
|
{
|
|
const long opportunityId = 4;
|
|
var getData = new StrategicOpportunityGetDetailData();
|
|
_mockDetailService
|
|
.Setup(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(new StrategicOpportunityDetailData());
|
|
|
|
await _controller.GetTrackingDetail(opportunityId, getData, CancellationToken.None);
|
|
|
|
_mockDetailService.Verify(s => s.GetStrategicOpportunityDetailData(opportunityId, true, getData, CancellationToken.None), Times.Once);
|
|
}
|
|
|
|
// --- UpdateDetails ---
|
|
|
|
[Test]
|
|
public async Task UpdateDetails_ReturnsBadRequest_WhenDtoIsNull()
|
|
{
|
|
var result = await _controller.UpdateDetails(1, null, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<BadRequestResult>();
|
|
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
|
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
|
}
|
|
|
|
[Test]
|
|
public async Task UpdateDetails_ReturnsBadRequest_WhenAdjustmentsIsEmpty()
|
|
{
|
|
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
|
{
|
|
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment>(),
|
|
Rows = new List<string> { "system", "entity" }
|
|
};
|
|
|
|
var result = await _controller.UpdateDetails(1, dto, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<BadRequestResult>();
|
|
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
|
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
|
}
|
|
|
|
[Test]
|
|
public async Task UpdateDetails_ReturnsBadRequest_WhenRowsIsEmpty()
|
|
{
|
|
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
|
{
|
|
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment> { new() { NodeKey = "101", CostsValue = 5000m } },
|
|
Rows = new List<string>()
|
|
};
|
|
|
|
var result = await _controller.UpdateDetails(1, dto, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<BadRequestResult>();
|
|
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
|
It.IsAny<long>(), It.IsAny<IEnumerable<StrategicOpportunityChargeCodeAdjustment>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<CancellationToken>()), Times.Never);
|
|
}
|
|
|
|
[Test]
|
|
public async Task UpdateDetails_ReturnsOk_AndCallsService_WhenDtoIsValid()
|
|
{
|
|
const long opportunityId = 42;
|
|
var dto = new StrategicChargeCodeController.UpdateDetailsDto
|
|
{
|
|
Adjustments = new List<StrategicOpportunityChargeCodeAdjustment> { new() { NodeKey = "101", CostsValue = 5000m } },
|
|
Rows = new List<string> { "system", "entity", "chargeCode" }
|
|
};
|
|
|
|
var result = await _controller.UpdateDetails(opportunityId, dto, CancellationToken.None);
|
|
|
|
result.Should().BeOfType<OkResult>();
|
|
_mockDetailService.Verify(s => s.UpdateStrategicOpportunityDetailsData(
|
|
opportunityId, dto.Adjustments, dto.Rows, CancellationToken.None), Times.Once);
|
|
}
|
|
|
|
// --- Route attribute assertions ---
|
|
|
|
[Test]
|
|
public void Controller_HasExpectedRouteTemplate()
|
|
{
|
|
var routeAttr = typeof(StrategicChargeCodeController).GetCustomAttribute<RouteAttribute>();
|
|
|
|
routeAttr.Should().NotBeNull();
|
|
routeAttr.Template.Should().Be("api/v{api-version:apiVersion}/[controller]");
|
|
}
|
|
|
|
[TestCase(nameof(StrategicChargeCodeController.GetOpportunityChargeCode), typeof(HttpGetAttribute), "{opportunityId}/[action]")]
|
|
[TestCase(nameof(StrategicChargeCodeController.CreateOpportunityChargeCode), typeof(HttpPostAttribute), null)]
|
|
[TestCase(nameof(StrategicChargeCodeController.UpdateOpportunityChargeCode), typeof(HttpPutAttribute), "{opportunityId}")]
|
|
[TestCase(nameof(StrategicChargeCodeController.DeleteOpportunityChargeCode), typeof(HttpDeleteAttribute), "{opportunityId}")]
|
|
[TestCase(nameof(StrategicChargeCodeController.GetGoalDetail), typeof(HttpPostAttribute), "{opportunityId}/goal-detail")]
|
|
[TestCase(nameof(StrategicChargeCodeController.ExportInitiative), typeof(HttpPostAttribute), "{opportunityId}/export")]
|
|
[TestCase(nameof(StrategicChargeCodeController.ExportOpportunity), typeof(HttpPostAttribute), "{opportunityId}/export-opportunity")]
|
|
[TestCase(nameof(StrategicChargeCodeController.GetTrackingDetail), typeof(HttpPostAttribute), "{opportunityId}/tracking-detail")]
|
|
[TestCase(nameof(StrategicChargeCodeController.UpdateDetails), typeof(HttpPutAttribute), "{opportunityId}/details")]
|
|
public void ActionRoute_HasExpectedHttpMethodAndTemplate(string methodName, Type httpAttrType, string expectedTemplate)
|
|
{
|
|
var method = typeof(StrategicChargeCodeController).GetMethod(methodName);
|
|
method.Should().NotBeNull($"method '{methodName}' should exist on {nameof(StrategicChargeCodeController)}");
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|