diff --git a/src/Application/Contracts/ContractService.cs b/src/Application/Contracts/ContractService.cs
index 566c9e4b..29728f4a 100644
--- a/src/Application/Contracts/ContractService.cs
+++ b/src/Application/Contracts/ContractService.cs
@@ -32,4 +32,10 @@ public void Add(Contract contract)
_repo.Add(contract);
}
+
+ ///
+ public bool Remove(Guid id)
+ {
+ return _repo.Remove(id);
+ }
}
diff --git a/src/Application/Contracts/IContractRepository.cs b/src/Application/Contracts/IContractRepository.cs
index 6d32d25f..1de3cda6 100644
--- a/src/Application/Contracts/IContractRepository.cs
+++ b/src/Application/Contracts/IContractRepository.cs
@@ -18,4 +18,11 @@ public interface IContractRepository
///
/// The new contract instance.
void Add(Contract contract);
+
+ ///
+ /// Removes the contract with the given ID.
+ ///
+ /// The id of the contract to be removed.
+ /// If the removal was successful.
+ public bool Remove(Guid id);
}
diff --git a/src/Application/Contracts/IContractService.cs b/src/Application/Contracts/IContractService.cs
index 0b96f6ed..3af8e0ab 100644
--- a/src/Application/Contracts/IContractService.cs
+++ b/src/Application/Contracts/IContractService.cs
@@ -18,4 +18,11 @@ public interface IContractService
///
/// The new contract.
void Add(Contract contract);
+
+ ///
+ /// Removes the specified contract.
+ ///
+ /// The id of the contract to be removed.
+ /// Whether the removal was successful.
+ bool Remove(Guid id);
}
diff --git a/src/Client/Pages/Admin/AdminPage.razor b/src/Client/Pages/Admin/AdminPage.razor
index d6bcaf2c..cb0c9a49 100644
--- a/src/Client/Pages/Admin/AdminPage.razor
+++ b/src/Client/Pages/Admin/AdminPage.razor
@@ -15,5 +15,4 @@
{
_contractList.Add(contract);
}
-
}
diff --git a/src/Client/Pages/Admin/ContractList.razor b/src/Client/Pages/Admin/ContractList.razor
index 7c447928..60e14fd1 100644
--- a/src/Client/Pages/Admin/ContractList.razor
+++ b/src/Client/Pages/Admin/ContractList.razor
@@ -12,7 +12,7 @@ else
@foreach (var contract in _contracts)
{
-
+
}
}
@@ -39,6 +39,16 @@ else
InvokeAsync(StateHasChanged);
}
-
+ ///
+ /// Removes a contract from the list.
+ ///
+ /// The contract to remove.
+ private void OnContractRemoved(Contract contract)
+ {
+ // Using null forgiving operator because because _contracts
+ // can not be null when remove button is pressed.
+ _contracts!.Remove(contract);
+ InvokeAsync(StateHasChanged);
+ }
}
diff --git a/src/Client/Pages/Admin/ContractListItem.razor b/src/Client/Pages/Admin/ContractListItem.razor
index f87d2f3d..6200f626 100644
--- a/src/Client/Pages/Admin/ContractListItem.razor
+++ b/src/Client/Pages/Admin/ContractListItem.razor
@@ -1,12 +1,35 @@
- @Name
+@using Domain.Contracts
+@inject HttpClient _http
+
+
+ @Contract.Name
+
+
+
@code {
+ ///
+ /// Called when a contract has been removed successfully.
+ ///
+ [Parameter]
+ public EventCallback OnContractRemoved { get; set; } = EventCallback.Empty;
+
///
- /// Name of the company.
+ /// The contract.
///
[Parameter, EditorRequired,]
- public string? Name { get; set; }
+ public Contract Contract { get; set; } = new();
+ private async Task RemoveContract()
+ {
+ HttpResponseMessage response = await _http.DeleteAsync($"api/v1/Contracts/{Contract.Id}");
+ if (response.IsSuccessStatusCode)
+ {
+ await OnContractRemoved.InvokeAsync(Contract);
+ }
+ }
}
diff --git a/src/Domain/Contracts/Contract.cs b/src/Domain/Contracts/Contract.cs
index 4a81f7e8..51a182f0 100644
--- a/src/Domain/Contracts/Contract.cs
+++ b/src/Domain/Contracts/Contract.cs
@@ -8,7 +8,7 @@ public class Contract
///
/// Gets the unique identifier.
///
- public Guid Id { get; } = Guid.NewGuid();
+ public Guid Id { get; init; } = Guid.NewGuid();
///
/// Gets or sets the name of the contract supplier.
diff --git a/src/Infrastructure/Contracts/FakeContractRepository.cs b/src/Infrastructure/Contracts/FakeContractRepository.cs
index 6136a63e..5991830a 100644
--- a/src/Infrastructure/Contracts/FakeContractRepository.cs
+++ b/src/Infrastructure/Contracts/FakeContractRepository.cs
@@ -9,7 +9,7 @@ namespace Infrastructure.Contracts;
///
public class FakeContractRepository : IContractRepository
{
- private readonly ICollection _contracts;
+ private readonly List _contracts;
///
/// Creates a fake contract for SJ.
@@ -27,4 +27,10 @@ public void Add(Contract contract)
{
_contracts.Add(contract);
}
+
+ ///
+ public bool Remove(Guid id)
+ {
+ return _contracts.RemoveAll(o => o.Id == id) > 0;
+ }
}
diff --git a/src/Server/Controllers/ContractsController.cs b/src/Server/Controllers/ContractsController.cs
index a0c02c31..a9f62c4f 100644
--- a/src/Server/Controllers/ContractsController.cs
+++ b/src/Server/Controllers/ContractsController.cs
@@ -59,4 +59,17 @@ public IActionResult CreateContract(Contract contract)
return Ok();
}
+
+ ///
+ /// Removes the specified contract.
+ ///
+ /// Id of the contract to be removed.
+ /// If the contract was successfully removed.
+ [HttpDelete("{id:guid}")]
+ public IActionResult Remove(Guid id)
+ {
+ return _contracts.Remove(id) ?
+ Ok() :
+ NotFound();
+ }
}
diff --git a/tests/Application.Tests/Contracts/ContractServiceTests.cs b/tests/Application.Tests/Contracts/ContractServiceTests.cs
index 17600a7a..b1aa8125 100644
--- a/tests/Application.Tests/Contracts/ContractServiceTests.cs
+++ b/tests/Application.Tests/Contracts/ContractServiceTests.cs
@@ -59,4 +59,33 @@ public void AddingContract_DoesNotThrow_IfIDIsUnique()
// Assert
add.Should().NotThrow();
}
+
+ [Fact]
+ public void RemovingContract_DoesReturnTrue_WhenAContractExists()
+ {
+ // Arrange
+ var contract = new Contract();
+ _mockRepo.Setup(repository => repository.Remove(contract.Id)).Returns(true);
+
+ // Act
+ bool actual = _cut.Remove(contract.Id);
+
+ // // Assert
+ actual.Should().BeTrue();
+ }
+
+ [Fact]
+ public void RemovingContract_DoesReturnFalse_WhenNoContractsExists()
+ {
+ // Arrange
+ _mockRepo.Setup(repository => repository.All).Returns(new List());
+
+ var id = Guid.NewGuid();
+
+ // Act
+ bool actual = _cut.Remove(id);
+
+ // Assert
+ actual.Should().BeFalse();
+ }
}
diff --git a/tests/Infrastructure.Tests/Contracts/FakeContractRepositoryTests.cs b/tests/Infrastructure.Tests/Contracts/FakeContractRepositoryTests.cs
new file mode 100644
index 00000000..6cb3005a
--- /dev/null
+++ b/tests/Infrastructure.Tests/Contracts/FakeContractRepositoryTests.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Linq;
+using Infrastructure.Contracts;
+
+namespace Infrastructure.Tests.Contracts;
+public class FakeContractRepositoryTests
+{
+ private readonly FakeContractRepository _cut;
+
+ public FakeContractRepositoryTests()
+ {
+ _cut = new FakeContractRepository();
+ }
+
+ [Fact]
+ public void RemoveContract_ReturnsFalse_WhenContractDoesNotExist()
+ {
+ // Arrange
+ var id = Guid.NewGuid();
+
+ // Act
+ bool actual = _cut.Remove(id);
+
+ // Assert
+ actual.Should().BeFalse();
+ }
+
+ [Fact]
+ public void RemoveContract_ReturnTrue_WhenContractExists()
+ {
+ // Arrange
+ Guid id = _cut.All.First().Id;
+
+ // Act
+ bool actual = _cut.Remove(id);
+
+ // Assert
+ actual.Should().BeTrue();
+ }
+}
diff --git a/tests/Presentation.Tests/Client/Pages/Admin/ContractListItemTests.cs b/tests/Presentation.Tests/Client/Pages/Admin/ContractListItemTests.cs
index a9f994b2..518e7fcb 100644
--- a/tests/Presentation.Tests/Client/Pages/Admin/ContractListItemTests.cs
+++ b/tests/Presentation.Tests/Client/Pages/Admin/ContractListItemTests.cs
@@ -1,4 +1,5 @@
using Client.Pages.Admin;
+using Domain.Contracts;
namespace Presentation.Tests.Client.Pages.Admin;
@@ -21,15 +22,16 @@ public void ContractListItem_ContainsTitle()
{
// Arrange
const string name = "SJ";
+ var contract = new Contract() { Name = name };
- static void ParameterBuilder(ComponentParameterCollectionBuilder parameters) =>
- parameters.Add(property => property.Name, name);
+ void ParameterBuilder(ComponentParameterCollectionBuilder parameters) =>
+ parameters.Add(property => property.Contract, contract);
// Act
IRenderedComponent cut =
_context.RenderComponent(ParameterBuilder);
// Assert
- cut.Find($"#{name}").TextContent.Should().Contain(name);
+ cut.Find($"#{contract.Name}").TextContent.Should().Contain(name);
}
}
diff --git a/tests/Presentation.Tests/Client/Pages/Admin/ContractListTests.cs b/tests/Presentation.Tests/Client/Pages/Admin/ContractListTests.cs
index ce991c33..b90df187 100644
--- a/tests/Presentation.Tests/Client/Pages/Admin/ContractListTests.cs
+++ b/tests/Presentation.Tests/Client/Pages/Admin/ContractListTests.cs
@@ -8,6 +8,7 @@
using Client.Pages.Admin;
using Domain.Contracts;
+using Microsoft.AspNetCore.Components.Web;
namespace Presentation.Tests.Client.Pages.Admin;
@@ -58,4 +59,27 @@ public void AddingContract_DoesNotThrow_BeforeContractsAreFetched()
// Assert
add.Should().NotThrow();
}
+
+ [Fact]
+ public async Task RemovingContract_RendersWithoutTheContractAsync()
+ {
+ // Arrange
+ var firstContract = new Contract() { Name = "first", };
+ Contract[] contracts = { firstContract, new Contract() { Name = "Second", }, };
+ MockHttp.When("/api/v1/Contracts/All").RespondJson(contracts);
+ MockHttp.When(HttpMethod.Delete, $"/api/v1/Contracts/{firstContract.Id}").Respond(req => new HttpResponseMessage(HttpStatusCode.OK));
+
+ IRenderedComponent cut = Context.RenderComponent();
+ const string removeButton = ".btn.btn-danger";
+ cut.WaitForElement(removeButton);
+
+ // Act
+ await cut.Find(removeButton).ClickAsync(new MouseEventArgs()).ConfigureAwait(false);
+ cut.WaitForState(() => cut.FindAll(removeButton).Count == 1);
+
+ // Assert
+ Expression>
+ elementWithNewName = contract => contract.TextContent.Contains(firstContract.Name);
+ cut.FindAll(".list-group-item").Should().NotContain(elementWithNewName);
+ }
}
diff --git a/tests/Presentation.Tests/Server/Controllers/ContractsControllerTests.cs b/tests/Presentation.Tests/Server/Controllers/ContractsControllerTests.cs
index 225bd16b..4798fbf7 100644
--- a/tests/Presentation.Tests/Server/Controllers/ContractsControllerTests.cs
+++ b/tests/Presentation.Tests/Server/Controllers/ContractsControllerTests.cs
@@ -1,6 +1,7 @@
using Application.Contracts;
using Domain.Contracts;
+using Microsoft.AspNetCore.Mvc;
namespace Presentation.Tests.Server.Controllers;
@@ -28,4 +29,46 @@ public void Get_AllContracts()
// Assert
actualWeather.Should().BeEquivalentTo(fakeContracts);
}
+
+ [Fact]
+ public void Remove_ReturnsOk_WhenIDExists()
+ {
+ // Arrange
+ var id = Guid.NewGuid();
+ _mockContracts.Setup(service => service.Remove(id)).Returns(true);
+
+ // Act
+ IActionResult actual = _cut.Remove(id);
+
+ // Assert
+ actual.Should().BeOfType();
+ }
+
+ [Fact]
+ public void Remove_CallsContractService_WhenIDExists()
+ {
+ // Arrange
+ var id = Guid.NewGuid();
+ _mockContracts.Setup(service => service.Remove(id)).Returns(true);
+
+ // Act
+ IActionResult actual = _cut.Remove(id);
+
+ // Assert
+ _mockContracts.Verify(o => o.Remove(id), Times.Once);
+ }
+
+ [Fact]
+ public void Remove_ReturnsNotFound_WhenIDDoesNotExist()
+ {
+ // Arrange
+ var id = Guid.NewGuid();
+ _mockContracts.Setup(service => service.Remove(id)).Returns(false);
+
+ // Act
+ IActionResult actual = _cut.Remove(id);
+
+ // Assert
+ actual.Should().BeOfType();
+ }
}