Template
chore: deploy initial code base
This commit is contained in:
+337
@@ -0,0 +1,337 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user