1+ using System . Linq . Expressions ;
2+ using Bogus . Extensions . Brazil ;
3+ using FluentAssertions ;
4+ using Moq ;
5+ using SnackFlow . Application . Exceptions ;
6+ using SnackFlow . Application . Extensions ;
7+ using SnackFlow . Application . Features . Companies . Queries . GetCompanyById ;
8+ using SnackFlow . Domain . Constants ;
9+ using SnackFlow . Domain . Entities ;
10+ using SnackFlow . Domain . Enums ;
11+ using SnackFlow . Domain . Repositories ;
12+ using SnackFlow . Domain . Tests ;
13+ using SnackFlow . Domain . ValueObjects . CompanyName ;
14+ using SnackFlow . Domain . ValueObjects . Email ;
15+ using SnackFlow . Domain . ValueObjects . Phone ;
16+ using SnackFlow . Domain . ValueObjects . TaxId ;
17+
18+ namespace SnackFlow . Application . Tests . Features . Companies . Queries ;
19+
20+ public class GetCompanyByIdQueryHandlerUnitTests : BaseTest
21+ {
22+ #region Setup
23+
24+ private readonly Mock < IUnitOfWork > _mockUnitOfWork ;
25+ private readonly Mock < ICompanyRepository > _mockCompanyRepository ;
26+ private readonly GetCompanyByIdQueryHandler _queryHandler ;
27+ private readonly GetCompanyByIdQuery _query ;
28+ private readonly Company _company ;
29+
30+ public GetCompanyByIdQueryHandlerUnitTests ( )
31+ {
32+ _mockUnitOfWork = new Mock < IUnitOfWork > ( ) ;
33+ _mockCompanyRepository = new Mock < ICompanyRepository > ( ) ;
34+ _queryHandler = new GetCompanyByIdQueryHandler ( _mockUnitOfWork . Object ) ;
35+
36+ _company = Company . Create (
37+ CompanyName . Create ( _faker . Person . FullName , _faker . Company . CompanyName ( ) ) ,
38+ TaxId . Create ( _faker . Company . Cnpj ( ) , TaxIdType . LegalEntityWithCnpj ) ,
39+ Email . Create ( _faker . Person . Email . ToLowerInvariant ( ) ) ,
40+ Phone . Create ( _faker . Phone . PhoneNumber ( "(92) 9####-####" ) )
41+ ) ;
42+
43+ _query = new GetCompanyByIdQuery ( _company . Id ) ;
44+ }
45+
46+ #endregion
47+
48+ #region Success Tests
49+
50+ [ Fact ( DisplayName = "Should return company successfully when company exists" ) ]
51+ public async Task Handle_WhenCompanyExists_ShouldReturnCompanySuccessfully ( )
52+ {
53+ // Arrange
54+ _mockCompanyRepository
55+ . Setup ( x => x . GetSingleAsync (
56+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
57+ It . IsAny < CancellationToken > ( )
58+ ) )
59+ . ReturnsAsync ( _company ) ;
60+
61+ _mockUnitOfWork
62+ . Setup ( x => x . Companies )
63+ . Returns ( _mockCompanyRepository . Object ) ;
64+
65+ // Act
66+ var result = await _queryHandler . Handle ( _query , CancellationToken . None ) ;
67+
68+ // Assert
69+ result . IsSuccess . Should ( ) . BeTrue ( ) ;
70+ result . IsFailure . Should ( ) . BeFalse ( ) ;
71+ result . Value . Should ( ) . NotBeNull ( ) ;
72+ result . Value . CorporateName . Should ( ) . Be ( _company . Name . Corporate ) ;
73+ result . Value . FantasyName . Should ( ) . Be ( _company . Name . Fantasy ) ;
74+ result . Value . Email . Should ( ) . Be ( _company . Email ) ;
75+ result . Value . Phone . Should ( ) . Be ( _company . Phone ) ;
76+ result . Value . TaxId . Should ( ) . Be ( _company . TaxId ) ;
77+ result . Value . Type . Should ( ) . Be ( _company . TaxId . Type . GetDescription ( ) ) ;
78+
79+ _mockCompanyRepository . Verify ( x => x . GetSingleAsync (
80+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
81+ It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
82+ }
83+
84+ #endregion
85+
86+ #region Not Found Tests
87+
88+ [ Fact ( DisplayName = "Should throw not found exception when company does not exist" ) ]
89+ public async Task Handle_WhenCompanyDoesNotExist_ShouldThrowNotFoundException ( )
90+ {
91+ // Arrange
92+ _mockCompanyRepository
93+ . Setup ( x => x . GetSingleAsync (
94+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
95+ It . IsAny < CancellationToken > ( )
96+ ) )
97+ . ReturnsAsync ( ( Company ? ) null ) ;
98+
99+ _mockUnitOfWork
100+ . Setup ( x => x . Companies )
101+ . Returns ( _mockCompanyRepository . Object ) ;
102+
103+ // Act
104+ var result = async ( ) => await _queryHandler . Handle ( _query , CancellationToken . None ) ;
105+
106+ // Assert
107+ await result . Should ( )
108+ . ThrowAsync < NotFoundException > ( )
109+ . WithMessage ( ErrorMessage . NotFound . Company ) ;
110+
111+ _mockCompanyRepository . Verify ( x => x . GetSingleAsync (
112+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
113+ It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
114+ }
115+
116+ [ Fact ( DisplayName = "Should throw not found exception when company id is empty" ) ]
117+ public async Task Handle_WhenCompanyIdIsEmpty_ShouldThrowNotFoundException ( )
118+ {
119+ // Arrange
120+ var emptyQuery = new GetCompanyByIdQuery ( Guid . Empty ) ;
121+
122+ _mockCompanyRepository
123+ . Setup ( x => x . GetSingleAsync (
124+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
125+ It . IsAny < CancellationToken > ( )
126+ ) )
127+ . ReturnsAsync ( ( Company ? ) null ) ;
128+
129+ _mockUnitOfWork
130+ . Setup ( x => x . Companies )
131+ . Returns ( _mockCompanyRepository . Object ) ;
132+
133+ // Act
134+ var result = async ( ) => await _queryHandler . Handle ( emptyQuery , CancellationToken . None ) ;
135+
136+ // Assert
137+ await result . Should ( )
138+ . ThrowAsync < NotFoundException > ( )
139+ . WithMessage ( ErrorMessage . NotFound . Company ) ;
140+
141+ _mockCompanyRepository . Verify ( x => x . GetSingleAsync (
142+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
143+ It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
144+ }
145+
146+ #endregion
147+
148+ #region Cancellation Tests
149+
150+ [ Fact ( DisplayName = "Should respect cancellation token during repository call" ) ]
151+ public async Task Handle_WhenCancellationTokenDuringRepositoryCall_ShouldRespectCancellationToken ( )
152+ {
153+ // Arrange
154+ var mockCancellationToken = new CancellationToken ( true ) ;
155+
156+ _mockCompanyRepository
157+ . Setup ( x => x . GetSingleAsync (
158+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
159+ It . Is < CancellationToken > ( ct => ct . IsCancellationRequested ) ) )
160+ . ThrowsAsync ( new OperationCanceledException ( ) ) ;
161+
162+ _mockUnitOfWork
163+ . Setup ( x => x . Companies )
164+ . Returns ( _mockCompanyRepository . Object ) ;
165+
166+ // Act
167+ var result = async ( ) => await _queryHandler . Handle ( _query , mockCancellationToken ) ;
168+
169+ // Assert
170+ await result . Should ( )
171+ . ThrowAsync < OperationCanceledException > ( ) ;
172+
173+ _mockCompanyRepository . Verify ( x => x . GetSingleAsync (
174+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
175+ It . Is < CancellationToken > ( ct => ct . IsCancellationRequested ) ) ,
176+ Times . Once ) ;
177+ }
178+
179+ #endregion
180+
181+ #region Repository Verification Tests
182+
183+ [ Fact ( DisplayName = "Should call repository with correct expression" ) ]
184+ public async Task Handle_WhenCalled_ShouldCallRepositoryWithCorrectExpression ( )
185+ {
186+ // Arrange
187+ Expression < Func < Company , bool > > ? capturedExpression = null ;
188+
189+ _mockCompanyRepository
190+ . Setup ( x => x . GetSingleAsync (
191+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
192+ It . IsAny < CancellationToken > ( ) ,
193+ It . IsAny < Expression < Func < Company , object > > [ ] > ( )
194+ ) )
195+ . Callback < Expression < Func < Company , bool > > , CancellationToken , Expression < Func < Company , object > > [ ] > (
196+ ( expr , ct , includes ) =>
197+ {
198+ capturedExpression = expr ;
199+ } )
200+ . ReturnsAsync ( _company ) ;
201+
202+ _mockUnitOfWork
203+ . Setup ( x => x . Companies )
204+ . Returns ( _mockCompanyRepository . Object ) ;
205+
206+ // Act
207+ await _queryHandler . Handle ( _query , CancellationToken . None ) ;
208+
209+ // Assert
210+ capturedExpression . Should ( ) . NotBeNull ( ) ;
211+
212+ var compiledExpression = capturedExpression ! . Compile ( ) ;
213+ compiledExpression ( _company ) . Should ( ) . BeTrue ( ) ;
214+
215+ var differentCompany = Company . Create (
216+ CompanyName . Create ( _faker . Person . FullName , _faker . Company . CompanyName ( ) ) ,
217+ TaxId . Create ( _faker . Company . Cnpj ( ) , TaxIdType . LegalEntityWithCnpj ) ,
218+ Email . Create ( _faker . Person . Email . ToLowerInvariant ( ) ) ,
219+ Phone . Create ( _faker . Phone . PhoneNumber ( "(92) 9####-####" ) )
220+ ) ;
221+
222+ compiledExpression ( differentCompany ) . Should ( ) . BeFalse ( ) ;
223+ }
224+
225+ #endregion
226+
227+ #region Repository Verification Tests - Alternative with includes parameter
228+
229+ [ Fact ( DisplayName = "Should call repository with correct expression including includes parameter" ) ]
230+ public async Task Handle_WhenCalledWithIncludes_ShouldCallRepositoryWithCorrectExpression ( )
231+ {
232+ // Arrange
233+ Expression < Func < Company , bool > > ? capturedExpression = null ;
234+
235+ _mockCompanyRepository
236+ . Setup ( x => x . GetSingleAsync (
237+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
238+ It . IsAny < CancellationToken > ( ) ,
239+ It . IsAny < Expression < Func < Company , object > > [ ] > ( )
240+ ) )
241+ . Callback < Expression < Func < Company , bool > > , CancellationToken , Expression < Func < Company , object > > [ ] > (
242+ ( expr , ct , includes ) =>
243+ {
244+ capturedExpression = expr ;
245+ } )
246+ . ReturnsAsync ( _company ) ;
247+
248+ _mockUnitOfWork
249+ . Setup ( x => x . Companies )
250+ . Returns ( _mockCompanyRepository . Object ) ;
251+
252+ // Act
253+ await _queryHandler . Handle ( _query , CancellationToken . None ) ;
254+
255+ // Assert
256+ capturedExpression . Should ( ) . NotBeNull ( ) ;
257+
258+ var compiledExpression = capturedExpression ! . Compile ( ) ;
259+ compiledExpression ( _company ) . Should ( ) . BeTrue ( ) ;
260+
261+ var differentCompany = Company . Create (
262+ CompanyName . Create ( _faker . Person . FullName , _faker . Company . CompanyName ( ) ) ,
263+ TaxId . Create ( _faker . Company . Cnpj ( ) , TaxIdType . LegalEntityWithCnpj ) ,
264+ Email . Create ( _faker . Person . Email . ToLowerInvariant ( ) ) ,
265+ Phone . Create ( _faker . Phone . PhoneNumber ( "(92) 9####-####" ) )
266+ ) ;
267+
268+ compiledExpression ( differentCompany ) . Should ( ) . BeFalse ( ) ;
269+ }
270+
271+ #endregion
272+
273+ #region Response Mapping Tests
274+
275+ [ Fact ( DisplayName = "Should map all company properties correctly to response" ) ]
276+ public async Task Handle_WhenCompanyExists_ShouldMapAllPropertiesCorrectly ( )
277+ {
278+ // Arrange
279+ _mockCompanyRepository
280+ . Setup ( x => x . GetSingleAsync (
281+ It . IsAny < Expression < Func < Company , bool > > > ( ) ,
282+ It . IsAny < CancellationToken > ( )
283+ ) )
284+ . ReturnsAsync ( _company ) ;
285+
286+ _mockUnitOfWork
287+ . Setup ( x => x . Companies )
288+ . Returns ( _mockCompanyRepository . Object ) ;
289+
290+ // Act
291+ var result = await _queryHandler . Handle ( _query , CancellationToken . None ) ;
292+
293+ // Assert
294+ result . Value . Should ( ) . NotBeNull ( ) ;
295+
296+ result . Value . CorporateName . Should ( ) . Be ( _company . Name . Corporate ) ;
297+ result . Value . FantasyName . Should ( ) . Be ( _company . Name . Fantasy ) ;
298+ result . Value . Email . Should ( ) . Be ( _company . Email ) ;
299+ result . Value . Phone . Should ( ) . Be ( _company . Phone ) ;
300+ result . Value . TaxId . Should ( ) . Be ( _company . TaxId ) ;
301+ result . Value . Type . Should ( ) . Be ( _company . TaxId . Type . GetDescription ( ) ) ;
302+
303+ result . Value . CorporateName . Should ( ) . NotBeNullOrEmpty ( ) ;
304+ result . Value . FantasyName . Should ( ) . NotBeNullOrEmpty ( ) ;
305+ result . Value . Email . Should ( ) . NotBeNullOrEmpty ( ) ;
306+ result . Value . Phone . Should ( ) . NotBeNullOrEmpty ( ) ;
307+ result . Value . TaxId . Should ( ) . NotBeNullOrEmpty ( ) ;
308+ result . Value . Type . Should ( ) . NotBeNullOrEmpty ( ) ;
309+ }
310+
311+ #endregion
312+ }
0 commit comments