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);
}
}