using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using GermanApp.Application.DTOs; using GermanApp.Application.Services; using GermanApp.Domain.Entities; using GermanApp.Domain.Interfaces; using GermanApp.Presentation.Controllers; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace GermanApp.Tests.Integration.Presentation.Controllers; /// /// Integration tests for StoryController. /// Tests the full controller -> service -> repository flow. /// [TestClass] public class StoryControllerTests { private Mock _lessonRepositoryMock; private Mock _storyRepositoryMock; private Mock _generationServiceMock; private Mock _storyServiceMock; private Mock _unlockServiceMock; private Mock _levelRepositoryMock; private Mock> _loggerMock; private StoryController _controller; [TestInitialize] public void TestInitialize() { _lessonRepositoryMock = new Mock(); _storyRepositoryMock = new Mock(); _generationServiceMock = new Mock(); _storyServiceMock = new Mock(); _unlockServiceMock = new Mock(); _levelRepositoryMock = new Mock(); _loggerMock = new Mock>(); _controller = new StoryController( _storyServiceMock.Object, _generationServiceMock.Object, _unlockServiceMock.Object, _levelRepositoryMock.Object, _lessonRepositoryMock.Object, _loggerMock.Object); } [TestCleanup] public void TestCleanup() { _controller?.Dispose(); } // ============================================ // Story Generation Tests // ============================================ [TestMethod] public async Task GenerateStoryAsync_ReturnsSuccess_WhenLessonsExistAndGenerationSucceeds() { // Arrange int levelId = 1; string theme = "Adventure"; var request = new StoryGenerationRequestDto(theme); var lessons = new List { Lesson.Create(1, "Greetings", 1, "Greetings", "Basic greetings"), Lesson.Create(1, "Numbers", 2, "Numbers", "German numbers 1-100") }; var expectedResponse = new StoryGenerationResponseDto( levelId, theme, 2, "Full story text here", new List()); _lessonRepositoryMock.Setup(r => r.GetByLevelAsync(levelId, It.IsAny())) .ReturnsAsync(lessons); _generationServiceMock.Setup(s => s.GenerateStoryAsync( levelId, theme, lessons, It.IsAny())) .ReturnsAsync(expectedResponse); // Act var result = await _controller.GenerateStoryAsync(levelId, request, CancellationToken.None); // Assert Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); var okResult = result.Result as OkObjectResult; Assert.IsNotNull(okResult); Assert.IsInstanceOfType(okResult.Value, typeof(StoryGenerationResponseDto)); var response = okResult.Value as StoryGenerationResponseDto; Assert.AreEqual(levelId, response.LevelId); Assert.AreEqual(theme, response.Theme); Assert.AreEqual(2, response.SegmentCount); } [TestMethod] public async Task GenerateStoryAsync_ReturnsBadRequest_WhenNoLessonsFound() { // Arrange int levelId = 999; // Non-existent level string theme = "Adventure"; var request = new StoryGenerationRequestDto(theme); _lessonRepositoryMock.Setup(r => r.GetByLevelAsync(levelId, It.IsAny())) .ReturnsAsync(new List()); // Empty list // Act var result = await _controller.GenerateStoryAsync(levelId, request, CancellationToken.None); // Assert Assert.IsInstanceOfType(result.Result, typeof(BadRequestObjectResult)); } // ============================================ // Story Segment Audio Generation Tests // ============================================ [TestMethod] public async Task GenerateSegmentAudioAsync_ReturnsSuccess_WhenSegmentExists() { // Arrange int segmentId = 1; var expectedDto = new StorySegmentDto { Id = segmentId, AudioUrl = "/audio/story/level1-segment1.wav" }; _storyServiceMock.Setup(s => s.GenerateAudioAsync(segmentId, It.IsAny())) .ReturnsAsync(expectedDto); // Act var result = await _controller.GenerateSegmentAudioAsync(segmentId, CancellationToken.None); // Assert Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); var okResult = result.Result as OkObjectResult; Assert.IsNotNull(okResult); Assert.IsInstanceOfType(okResult.Value, typeof(StorySegmentDto)); var response = okResult.Value as StorySegmentDto; Assert.AreEqual(segmentId, response.Id); Assert.AreEqual(expectedDto.AudioUrl, response.AudioUrl); } [TestMethod] public async Task GenerateSegmentAudioAsync_ReturnsNotFound_WhenSegmentDoesNotExist() { // Arrange int segmentId = 999; // Non-existent segment _storyServiceMock.Setup(s => s.GenerateAudioAsync(segmentId, It.IsAny())) .ReturnsAsync((StorySegmentDto?)null); // Act var result = await _controller.GenerateSegmentAudioAsync(segmentId, CancellationToken.None); // Assert Assert.IsInstanceOfType(result.Result, typeof(NotFoundResult)); } // ============================================ // Get Stories by Level Tests // ============================================ [TestMethod] public async Task GetByLevelAsync_ReturnsSuccess_WhenSegmentsExist() { // Arrange int levelId = 1; var expectedSegments = new List { new StorySegmentDto { Id = 1, LevelId = levelId, Order = 1, Title = "Part 1" }, new StorySegmentDto { Id = 2, LevelId = levelId, Order = 2, Title = "Part 2" } }; _storyServiceMock.Setup(s => s.GetByLevelAsync(levelId, false, It.IsAny())) .ReturnsAsync(expectedSegments); // Act var result = await _controller.GetByLevelAsync(levelId, CancellationToken.None); // Assert Assert.IsInstanceOfType(result.Result, typeof(OkObjectResult)); var okResult = result.Result as OkObjectResult; Assert.IsNotNull(okResult); Assert.IsInstanceOfType(okResult.Value, typeof(List)); var response = okResult.Value as List; Assert.AreEqual(2, response.Count); } }