Skip to content

Commit 18ff5c0

Browse files
Merge pull request #20 from DilmurodDeveloper/users/DilmurodDeveloper/foundations-book-add
FOUNDATIONS: Add Book
2 parents c38ac1e + 5e9074b commit 18ff5c0

20 files changed

+711
-12
lines changed

LibraryManagement.Api.Tests.Unit/DeleteMe.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

LibraryManagement.Api.Tests.Unit/LibraryManagement.Api.Tests.Unit.csproj

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,24 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="coverlet.collector" Version="6.0.0" />
14-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
15-
<PackageReference Include="xunit" Version="2.5.3" />
16-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
13+
<PackageReference Include="coverlet.collector" Version="6.0.4">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
<PackageReference Include="DeepCloner" Version="0.10.4" />
18+
<PackageReference Include="FluentAssertions" Version="6.11.0" />
19+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
20+
<PackageReference Include="Moq" Version="4.20.72" />
21+
<PackageReference Include="Tynamix.ObjectFiller" Version="1.5.9" />
22+
<PackageReference Include="xunit" Version="2.9.3" />
23+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
24+
<PrivateAssets>all</PrivateAssets>
25+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
26+
</PackageReference>
27+
</ItemGroup>
28+
29+
<ItemGroup>
30+
<ProjectReference Include="..\LibraryManagement.Api\LibraryManagement.Api.csproj" />
1731
</ItemGroup>
1832

1933
<ItemGroup>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//-----------------------------------------------------------
2+
// Copyright (c) Coalition of Good-Hearted Engineers
3+
// Free To Use To Build Reliable Library Management Solutions
4+
//-----------------------------------------------------------
5+
6+
using EFxceptions.Models.Exceptions;
7+
using LibraryManagement.Api.Models.Foundations.Books;
8+
using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
9+
using Microsoft.Data.SqlClient;
10+
using Moq;
11+
12+
namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
13+
{
14+
public partial class BookServiceTests
15+
{
16+
[Fact]
17+
public async Task ShouldThrowCriticalDependencyExceptionOnAddIfSqlErrorOccursAndLogItAsync()
18+
{
19+
// given
20+
Book someBook = CreateRandomBook();
21+
SqlException sqlException = GetSqlError();
22+
23+
var failedBookStorageException =
24+
new FailedBookStorageException(sqlException);
25+
26+
var expectedBookDependencyException =
27+
new BookDependencyException(failedBookStorageException);
28+
29+
this.storageBrokerMock.Setup(broker =>
30+
broker.InsertBookAsync(someBook))
31+
.ThrowsAsync(sqlException);
32+
33+
// when
34+
ValueTask<Book> addBookTask =
35+
this.bookService.AddBookAsync(someBook);
36+
37+
// then
38+
await Assert.ThrowsAsync<BookDependencyException>(() =>
39+
addBookTask.AsTask());
40+
41+
this.storageBrokerMock.Verify(broker =>
42+
broker.InsertBookAsync(someBook),
43+
Times.Once);
44+
45+
this.loggingBrokerMock.Verify(broker =>
46+
broker.LogCritical(It.Is(SameExceptionAs(
47+
expectedBookDependencyException))),
48+
Times.Once);
49+
50+
this.storageBrokerMock.VerifyNoOtherCalls();
51+
this.loggingBrokerMock.VerifyNoOtherCalls();
52+
}
53+
54+
[Fact]
55+
public async Task ShouldThrowDependencyValidationOnAddIfDuplicateKeyErrorOccursAndLogItAsync()
56+
{
57+
// given
58+
Book someBook = CreateRandomBook();
59+
string someMessage = GetRandomString();
60+
61+
var duplicateKeyException =
62+
new DuplicateKeyException(someMessage);
63+
64+
var alreadyExistsBookException =
65+
new AlreadyExistsBookException(duplicateKeyException);
66+
67+
var expectedBookDependencyValidationException =
68+
new BookDependencyValidationException(alreadyExistsBookException);
69+
70+
this.storageBrokerMock.Setup(broker =>
71+
broker.InsertBookAsync(someBook))
72+
.ThrowsAsync(duplicateKeyException);
73+
74+
// when
75+
ValueTask<Book> addBookTask =
76+
this.bookService.AddBookAsync(someBook);
77+
78+
// then
79+
await Assert.ThrowsAsync<BookDependencyValidationException>(() =>
80+
addBookTask.AsTask());
81+
82+
this.storageBrokerMock.Verify(broker =>
83+
broker.InsertBookAsync(someBook),
84+
Times.Once);
85+
86+
this.loggingBrokerMock.Verify(broker =>
87+
broker.LogError(It.Is(SameExceptionAs(
88+
expectedBookDependencyValidationException))),
89+
Times.Once);
90+
91+
this.storageBrokerMock.VerifyNoOtherCalls();
92+
this.loggingBrokerMock.VerifyNoOtherCalls();
93+
}
94+
95+
[Fact]
96+
public async Task ShouldThrowServiceExceptionOnAddIfServiceErrorOccursAndLogItAsync()
97+
{
98+
// given
99+
Book someBook = CreateRandomBook();
100+
var serviceException = new Exception();
101+
102+
var failedBookServiceException =
103+
new FailedBookServiceException(serviceException);
104+
105+
var expectedBookServiceException =
106+
new BookServiceException(failedBookServiceException);
107+
108+
this.storageBrokerMock.Setup(broker =>
109+
broker.InsertBookAsync(someBook))
110+
.ThrowsAsync(serviceException);
111+
112+
// when
113+
ValueTask<Book> addBookTask =
114+
this.bookService.AddBookAsync(someBook);
115+
116+
// then
117+
await Assert.ThrowsAsync<BookServiceException>(() =>
118+
addBookTask.AsTask());
119+
120+
this.storageBrokerMock.Verify(broker =>
121+
broker.InsertBookAsync(someBook),
122+
Times.Once);
123+
124+
this.loggingBrokerMock.Verify(broker =>
125+
broker.LogError(It.Is(SameExceptionAs(
126+
expectedBookServiceException))),
127+
Times.Once);
128+
129+
this.storageBrokerMock.VerifyNoOtherCalls();
130+
this.loggingBrokerMock.VerifyNoOtherCalls();
131+
}
132+
}
133+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//-----------------------------------------------------------
2+
// Copyright (c) Coalition of Good-Hearted Engineers
3+
// Free To Use To Build Reliable Library Management Solutions
4+
//-----------------------------------------------------------
5+
6+
using FluentAssertions;
7+
using Force.DeepCloner;
8+
using LibraryManagement.Api.Models.Foundations.Books;
9+
using Moq;
10+
11+
namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
12+
{
13+
public partial class BookServiceTests
14+
{
15+
[Fact]
16+
public async Task ShouldAddBookAsync()
17+
{
18+
// given
19+
Book randomBook = CreateRandomBook();
20+
Book inputBook = randomBook;
21+
Book storageBook = inputBook;
22+
Book expectedBook = storageBook.DeepClone();
23+
24+
this.storageBrokerMock.Setup(broker =>
25+
broker.InsertBookAsync(inputBook))
26+
.ReturnsAsync(storageBook);
27+
28+
// when
29+
Book actualBook =
30+
await this.bookService.AddBookAsync(inputBook);
31+
32+
// then
33+
actualBook.Should().BeEquivalentTo(expectedBook);
34+
35+
this.storageBrokerMock.Verify(broker =>
36+
broker.InsertBookAsync(inputBook),
37+
Times.Once);
38+
39+
this.storageBrokerMock.VerifyNoOtherCalls();
40+
this.loggingBrokerMock.VerifyNoOtherCalls();
41+
}
42+
}
43+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
//-----------------------------------------------------------
2+
// Copyright (c) Coalition of Good-Hearted Engineers
3+
// Free To Use To Build Reliable Library Management Solutions
4+
//-----------------------------------------------------------
5+
6+
using LibraryManagement.Api.Models.Foundations.Books;
7+
using LibraryManagement.Api.Models.Foundations.Books.Exceptions;
8+
using Moq;
9+
10+
namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
11+
{
12+
public partial class BookServiceTests
13+
{
14+
[Fact]
15+
public async Task ShouldThrowValidationExceptionOnAddIfBookIsNullAndLogItAsync()
16+
{
17+
// given
18+
Book nullBook = null;
19+
var nullBookException = new NullBookException();
20+
21+
var expectedBookValidationException =
22+
new BookValidationException(nullBookException);
23+
24+
// when
25+
ValueTask<Book> addBookTask =
26+
this.bookService.AddBookAsync(nullBook);
27+
28+
// then
29+
await Assert.ThrowsAsync<BookValidationException>(() =>
30+
addBookTask.AsTask());
31+
32+
this.loggingBrokerMock.Verify(broker =>
33+
broker.LogError(It.Is(SameExceptionAs(
34+
expectedBookValidationException))),
35+
Times.Once);
36+
37+
this.storageBrokerMock.Verify(broker =>
38+
broker.InsertBookAsync(It.IsAny<Book>()),
39+
Times.Never);
40+
41+
this.loggingBrokerMock.VerifyNoOtherCalls();
42+
this.storageBrokerMock.VerifyNoOtherCalls();
43+
}
44+
45+
[Theory]
46+
[InlineData(null)]
47+
[InlineData("")]
48+
[InlineData(" ")]
49+
public async Task ShouldThrowValidationExceptionOnAddIfBookIsInvalidAndLogItAsync(
50+
string invalidText)
51+
{
52+
var invalidBook = new Book
53+
{
54+
BookTitle = invalidText
55+
};
56+
57+
var invalidBookException = new InvalidBookException();
58+
59+
invalidBookException.AddData(
60+
key: nameof(Book.BookId),
61+
values: "Id is required");
62+
63+
invalidBookException.AddData(
64+
key: nameof(Book.ReaderId),
65+
values: "Id is required");
66+
67+
invalidBookException.AddData(
68+
key: nameof(Book.BookTitle),
69+
values: "Text is required");
70+
71+
invalidBookException.AddData(
72+
key: nameof(Book.Author),
73+
values: "Text is required");
74+
75+
invalidBookException.AddData(
76+
key: nameof(Book.Genre),
77+
values: "Text is required");
78+
79+
80+
var expectedBookValidationException =
81+
new BookValidationException(invalidBookException);
82+
83+
// when
84+
ValueTask<Book> addBookTask =
85+
this.bookService.AddBookAsync(invalidBook);
86+
87+
// then
88+
await Assert.ThrowsAsync<BookValidationException>(() =>
89+
addBookTask.AsTask());
90+
91+
this.loggingBrokerMock.Verify(broker =>
92+
broker.LogError(It.Is(SameExceptionAs(
93+
expectedBookValidationException))),
94+
Times.Once);
95+
96+
this.storageBrokerMock.Verify(broker =>
97+
broker.InsertBookAsync(It.IsAny<Book>()),
98+
Times.Never);
99+
100+
this.loggingBrokerMock.VerifyNoOtherCalls();
101+
this.storageBrokerMock.VerifyNoOtherCalls();
102+
}
103+
}
104+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//-----------------------------------------------------------
2+
// Copyright (c) Coalition of Good-Hearted Engineers
3+
// Free To Use To Build Reliable Library Management Solutions
4+
//-----------------------------------------------------------
5+
6+
using System.Linq.Expressions;
7+
using System.Runtime.CompilerServices;
8+
using LibraryManagement.Api.Brokers.Loggings;
9+
using LibraryManagement.Api.Brokers.Storages;
10+
using LibraryManagement.Api.Models.Foundations.Books;
11+
using LibraryManagement.Api.Services.Foundations.Books;
12+
using Microsoft.Data.SqlClient;
13+
using Moq;
14+
using Tynamix.ObjectFiller;
15+
using Xeptions;
16+
17+
namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Books
18+
{
19+
public partial class BookServiceTests
20+
{
21+
private readonly Mock<IStorageBroker> storageBrokerMock;
22+
private readonly Mock<ILoggingBroker> loggingBrokerMock;
23+
private readonly IBookService bookService;
24+
25+
public BookServiceTests()
26+
{
27+
this.storageBrokerMock = new Mock<IStorageBroker>();
28+
this.loggingBrokerMock = new Mock<ILoggingBroker>();
29+
this.bookService = new BookService(
30+
storageBroker: this.storageBrokerMock.Object,
31+
loggingBroker: this.loggingBrokerMock.Object);
32+
}
33+
34+
private static Book CreateRandomBook() =>
35+
CreateBookFiller(date: GetRandomDateTimeOffset()).Create();
36+
37+
private static DateTimeOffset GetRandomDateTimeOffset() =>
38+
new DateTimeRange(earliestDate: new DateTime()).GetValue();
39+
40+
private static SqlException GetSqlError() =>
41+
(SqlException)RuntimeHelpers.GetUninitializedObject(typeof(SqlException));
42+
43+
private static string GetRandomString() =>
44+
new MnemonicString().GetValue();
45+
46+
private Expression<Func<Xeption, bool>> SameExceptionAs(Xeption expectedException) =>
47+
actualException => actualException.SameExceptionAs(expectedException);
48+
49+
private static Filler<Book> CreateBookFiller(DateTimeOffset date)
50+
{
51+
var filler = new Filler<Book>();
52+
53+
filler.Setup()
54+
.OnType<DateTimeOffset>().Use(date);
55+
56+
return filler;
57+
}
58+
}
59+
}

LibraryManagement.Api/LibraryManagement.Api.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
</PackageReference>
2020
<PackageReference Include="RESTFulSense" Version="3.2.0" />
2121
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
22+
<PackageReference Include="Xeption" Version="2.8.0" />
2223
</ItemGroup>
2324

2425
</Project>

0 commit comments

Comments
 (0)