1+ using FluentAssertions ;
2+ using ProjectVG . Application . Models . Character ;
3+ using ProjectVG . Application . Services . Character ;
4+ using ProjectVG . Common . Exceptions ;
5+ using ProjectVG . Tests . Application . Integration . TestBase ;
6+ using ProjectVG . Tests . Application . TestUtilities ;
7+ using Xunit ;
8+
9+ namespace ProjectVG . Tests . Application . Integration
10+ {
11+ [ Collection ( "ApplicationIntegration" ) ]
12+ public class CharacterServiceIntegrationTests
13+ {
14+ private readonly ICharacterService _characterService ;
15+ private readonly ApplicationIntegrationTestFixture _fixture ;
16+
17+ public CharacterServiceIntegrationTests ( ApplicationIntegrationTestFixture fixture )
18+ {
19+ _fixture = fixture ;
20+ _characterService = fixture . GetService < ICharacterService > ( ) ;
21+ }
22+
23+ #region Create and Retrieve Integration Tests
24+
25+ [ Fact ]
26+ public async Task CreateAndGetCharacterAsync_ShouldPersistAndRetrieveCharacter ( )
27+ {
28+ // Arrange
29+ await _fixture . ClearDatabaseAsync ( ) ;
30+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand (
31+ "Integration Test Character" ,
32+ "A character for integration testing" ,
33+ "Test Role" ) ;
34+
35+ // Act - Create
36+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
37+
38+ // Act - Retrieve
39+ var retrievedCharacter = await _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ;
40+
41+ // Assert
42+ retrievedCharacter . Should ( ) . NotBeNull ( ) ;
43+ retrievedCharacter . Id . Should ( ) . Be ( createdCharacter . Id ) ;
44+ retrievedCharacter . Name . Should ( ) . Be ( createCommand . Name ) ;
45+ retrievedCharacter . Description . Should ( ) . Be ( createCommand . Description ) ;
46+ retrievedCharacter . Role . Should ( ) . Be ( createCommand . Role ) ;
47+ retrievedCharacter . IsActive . Should ( ) . Be ( createCommand . IsActive ) ;
48+ }
49+
50+ [ Fact ]
51+ public async Task CreateMultipleCharacters_GetAllCharactersAsync_ShouldReturnAllCharacters ( )
52+ {
53+ // Arrange
54+ await _fixture . ClearDatabaseAsync ( ) ;
55+ var commands = new [ ]
56+ {
57+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 1" , "Description 1" , "Role 1" ) ,
58+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 2" , "Description 2" , "Role 2" ) ,
59+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 3" , "Description 3" , "Role 3" )
60+ } ;
61+
62+ // Act - Create characters
63+ var createdCharacters = new List < CharacterDto > ( ) ;
64+ foreach ( var command in commands )
65+ {
66+ var created = await _characterService . CreateCharacterAsync ( command ) ;
67+ createdCharacters . Add ( created ) ;
68+ }
69+
70+ // Act - Get all
71+ var allCharacters = await _characterService . GetAllCharactersAsync ( ) ;
72+
73+ // Assert
74+ allCharacters . Should ( ) . HaveCount ( 3 ) ;
75+ allCharacters . Should ( ) . OnlyContain ( c => createdCharacters . Any ( cc => cc . Id == c . Id ) ) ;
76+ }
77+
78+ #endregion
79+
80+ #region Update Integration Tests
81+
82+ [ Fact ]
83+ public async Task CreateUpdateAndRetrieveCharacterAsync_ShouldPersistChanges ( )
84+ {
85+ // Arrange
86+ await _fixture . ClearDatabaseAsync ( ) ;
87+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand ( "Original Name" ) ;
88+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
89+
90+ var updateCommand = TestDataBuilder . CreateUpdateCharacterCommand (
91+ "Updated Name" ,
92+ "Updated Description" ,
93+ "Updated Role" ,
94+ true ) ; // Keep IsActive = true so character can be retrieved
95+
96+ // Act - Update
97+ var updatedCharacter = await _characterService . UpdateCharacterAsync ( createdCharacter . Id , updateCommand ) ;
98+
99+ // Act - Retrieve after update
100+ var retrievedCharacter = await _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ;
101+
102+ // Assert
103+ updatedCharacter . Should ( ) . NotBeNull ( ) ;
104+ updatedCharacter . Id . Should ( ) . Be ( createdCharacter . Id ) ;
105+ updatedCharacter . Name . Should ( ) . Be ( updateCommand . Name ) ;
106+ updatedCharacter . Description . Should ( ) . Be ( updateCommand . Description ) ;
107+ updatedCharacter . Role . Should ( ) . Be ( updateCommand . Role ) ;
108+ updatedCharacter . IsActive . Should ( ) . Be ( updateCommand . IsActive ) ;
109+
110+ retrievedCharacter . Should ( ) . BeEquivalentTo ( updatedCharacter ) ;
111+ }
112+
113+ [ Fact ]
114+ public async Task UpdateNonExistentCharacter_ShouldThrowNotFoundException ( )
115+ {
116+ // Arrange
117+ await _fixture . ClearDatabaseAsync ( ) ;
118+ var nonExistentId = Guid . NewGuid ( ) ;
119+ var updateCommand = TestDataBuilder . CreateUpdateCharacterCommand ( ) ;
120+
121+ // Act & Assert
122+ await Assert . ThrowsAsync < NotFoundException > (
123+ ( ) => _characterService . UpdateCharacterAsync ( nonExistentId , updateCommand ) ) ;
124+ }
125+
126+ #endregion
127+
128+ #region Delete Integration Tests
129+
130+ [ Fact ]
131+ public async Task CreateDeleteAndTryRetrieveCharacterAsync_ShouldRemoveFromDatabase ( )
132+ {
133+ // Arrange
134+ await _fixture . ClearDatabaseAsync ( ) ;
135+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand ( "To Be Deleted" ) ;
136+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
137+
138+ // Verify character exists
139+ var existsBefore = await _characterService . CharacterExistsAsync ( createdCharacter . Id ) ;
140+ existsBefore . Should ( ) . BeTrue ( ) ;
141+
142+ // Act - Delete
143+ await _characterService . DeleteCharacterAsync ( createdCharacter . Id ) ;
144+
145+ // Assert - Should not exist
146+ var existsAfter = await _characterService . CharacterExistsAsync ( createdCharacter . Id ) ;
147+ existsAfter . Should ( ) . BeFalse ( ) ;
148+
149+ // Should throw when trying to retrieve
150+ await Assert . ThrowsAsync < NotFoundException > (
151+ ( ) => _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ) ;
152+ }
153+
154+ [ Fact ]
155+ public async Task DeleteNonExistentCharacter_ShouldThrowNotFoundException ( )
156+ {
157+ // Arrange
158+ await _fixture . ClearDatabaseAsync ( ) ;
159+ var nonExistentId = Guid . NewGuid ( ) ;
160+
161+ // Act & Assert
162+ await Assert . ThrowsAsync < NotFoundException > (
163+ ( ) => _characterService . DeleteCharacterAsync ( nonExistentId ) ) ;
164+ }
165+
166+ [ Fact ]
167+ public async Task DeleteCharacterFromMultipleCharacters_ShouldOnlyDeleteSpecificCharacter ( )
168+ {
169+ // Arrange
170+ await _fixture . ClearDatabaseAsync ( ) ;
171+ var character1 = await _characterService . CreateCharacterAsync (
172+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 1" ) ) ;
173+ var character2 = await _characterService . CreateCharacterAsync (
174+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 2" ) ) ;
175+ var character3 = await _characterService . CreateCharacterAsync (
176+ TestDataBuilder . CreateCreateCharacterCommand ( "Character 3" ) ) ;
177+
178+ // Act - Delete middle character
179+ await _characterService . DeleteCharacterAsync ( character2 . Id ) ;
180+
181+ // Assert
182+ var allCharacters = await _characterService . GetAllCharactersAsync ( ) ;
183+ allCharacters . Should ( ) . HaveCount ( 2 ) ;
184+ allCharacters . Should ( ) . Contain ( c => c . Id == character1 . Id ) ;
185+ allCharacters . Should ( ) . Contain ( c => c . Id == character3 . Id ) ;
186+ allCharacters . Should ( ) . NotContain ( c => c . Id == character2 . Id ) ;
187+ }
188+
189+ #endregion
190+
191+ #region Character Exists Integration Tests
192+
193+ [ Fact ]
194+ public async Task CharacterExistsAsync_WithExistingCharacter_ShouldReturnTrue ( )
195+ {
196+ // Arrange
197+ await _fixture . ClearDatabaseAsync ( ) ;
198+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand ( "Existing Character" ) ;
199+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
200+
201+ // Act
202+ var exists = await _characterService . CharacterExistsAsync ( createdCharacter . Id ) ;
203+
204+ // Assert
205+ exists . Should ( ) . BeTrue ( ) ;
206+ }
207+
208+ [ Fact ]
209+ public async Task CharacterExistsAsync_WithNonExistentCharacter_ShouldReturnFalse ( )
210+ {
211+ // Arrange
212+ await _fixture . ClearDatabaseAsync ( ) ;
213+ var nonExistentId = Guid . NewGuid ( ) ;
214+
215+ // Act
216+ var exists = await _characterService . CharacterExistsAsync ( nonExistentId ) ;
217+
218+ // Assert
219+ exists . Should ( ) . BeFalse ( ) ;
220+ }
221+
222+ #endregion
223+
224+ #region Complex Scenarios Integration Tests
225+
226+ [ Fact ]
227+ public async Task CompleteCharacterLifecycle_ShouldWorkCorrectly ( )
228+ {
229+ // Arrange
230+ await _fixture . ClearDatabaseAsync ( ) ;
231+
232+ // Create
233+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand ( "Lifecycle Character" ) ;
234+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
235+
236+ createdCharacter . Should ( ) . NotBeNull ( ) ;
237+ createdCharacter . Name . Should ( ) . Be ( "Lifecycle Character" ) ;
238+
239+ // Exists check
240+ var existsAfterCreate = await _characterService . CharacterExistsAsync ( createdCharacter . Id ) ;
241+ existsAfterCreate . Should ( ) . BeTrue ( ) ;
242+
243+ // Update
244+ var updateCommand = TestDataBuilder . CreateUpdateCharacterCommand ( "Updated Lifecycle Character" ) ;
245+ var updatedCharacter = await _characterService . UpdateCharacterAsync ( createdCharacter . Id , updateCommand ) ;
246+
247+ updatedCharacter . Name . Should ( ) . Be ( "Updated Lifecycle Character" ) ;
248+ updatedCharacter . Id . Should ( ) . Be ( createdCharacter . Id ) ;
249+
250+ // Retrieve after update
251+ var retrievedAfterUpdate = await _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ;
252+ retrievedAfterUpdate . Name . Should ( ) . Be ( "Updated Lifecycle Character" ) ;
253+
254+ // Delete
255+ await _characterService . DeleteCharacterAsync ( createdCharacter . Id ) ;
256+
257+ // Verify deletion
258+ var existsAfterDelete = await _characterService . CharacterExistsAsync ( createdCharacter . Id ) ;
259+ existsAfterDelete . Should ( ) . BeFalse ( ) ;
260+
261+ // Should throw when trying to retrieve deleted character
262+ await Assert . ThrowsAsync < NotFoundException > (
263+ ( ) => _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ) ;
264+ }
265+
266+ [ Fact ]
267+ public async Task CreateCharactersWithSameName_ShouldAllowDuplicateNames ( )
268+ {
269+ // Arrange
270+ await _fixture . ClearDatabaseAsync ( ) ;
271+ var sameName = "Duplicate Name Character" ;
272+
273+ // Act - Create multiple characters with the same name
274+ var character1 = await _characterService . CreateCharacterAsync (
275+ TestDataBuilder . CreateCreateCharacterCommand ( sameName , "Description 1" ) ) ;
276+ var character2 = await _characterService . CreateCharacterAsync (
277+ TestDataBuilder . CreateCreateCharacterCommand ( sameName , "Description 2" ) ) ;
278+
279+ // Assert
280+ character1 . Should ( ) . NotBeNull ( ) ;
281+ character2 . Should ( ) . NotBeNull ( ) ;
282+ character1 . Id . Should ( ) . NotBe ( character2 . Id ) ;
283+ character1 . Name . Should ( ) . Be ( sameName ) ;
284+ character2 . Name . Should ( ) . Be ( sameName ) ;
285+ character1 . Description . Should ( ) . Be ( "Description 1" ) ;
286+ character2 . Description . Should ( ) . Be ( "Description 2" ) ;
287+
288+ var allCharacters = await _characterService . GetAllCharactersAsync ( ) ;
289+ allCharacters . Should ( ) . HaveCount ( 2 ) ;
290+ allCharacters . Should ( ) . OnlyContain ( c => c . Name == sameName ) ;
291+ }
292+
293+ #endregion
294+
295+ #region Edge Cases Integration Tests
296+
297+ [ Fact ]
298+ public async Task GetAllCharactersAsync_WithEmptyDatabase_ShouldReturnEmptyCollection ( )
299+ {
300+ // Arrange
301+ await _fixture . ClearDatabaseAsync ( ) ;
302+
303+ // Act
304+ var characters = await _characterService . GetAllCharactersAsync ( ) ;
305+
306+ // Assert
307+ characters . Should ( ) . NotBeNull ( ) ;
308+ characters . Should ( ) . BeEmpty ( ) ;
309+ }
310+
311+ [ Fact ]
312+ public async Task CreateCharacterWithSpecialCharacters_ShouldPersistCorrectly ( )
313+ {
314+ // Arrange
315+ await _fixture . ClearDatabaseAsync ( ) ;
316+ var specialName = "特殊文字キャラクター!@#$%^&*()_+-=[]{}|;':\" ,./<>?" ;
317+ var specialDescription = "Éñgłīšh àñd 中文 ànd العربية ànd עברית ànd русский" ;
318+
319+ var createCommand = TestDataBuilder . CreateCreateCharacterCommand (
320+ specialName ,
321+ specialDescription ,
322+ "Special Role" ) ;
323+
324+ // Act
325+ var createdCharacter = await _characterService . CreateCharacterAsync ( createCommand ) ;
326+ var retrievedCharacter = await _characterService . GetCharacterByIdAsync ( createdCharacter . Id ) ;
327+
328+ // Assert
329+ retrievedCharacter . Name . Should ( ) . Be ( specialName ) ;
330+ retrievedCharacter . Description . Should ( ) . Be ( specialDescription ) ;
331+ }
332+
333+ #endregion
334+ }
335+ }
0 commit comments