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 Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; namespace GermanApp.Tests.Unit.Application.Services; [TestClass] public class StoryUnlockServiceTests { private Mock _mockStoryService; private Mock _mockUserProgressRepository; private Mock _mockLessonRepository; private Mock> _mockLogger; private StoryUnlockService _service; [TestInitialize] public void Setup() { _mockStoryService = new Mock( new Mock().Object, new Mock().Object, new Mock>().Object); _mockUserProgressRepository = new Mock(); _mockLessonRepository = new Mock(); _mockLogger = new Mock>(); _service = new StoryUnlockService( _mockStoryService.Object, _mockUserProgressRepository.Object, _mockLessonRepository.Object, _mockLogger.Object); } private Lesson CreateTestLesson(int id, int levelId, int order, string title = "Test", string topic = "Test") { var lesson = Lesson.Create(levelId, title, order, topic); typeof(Lesson).GetProperty("Id")?.SetValue(lesson, id); return lesson; } private StorySegment CreateTestSegment(int id, int levelId, int? lessonId, int order, string content = "Content", string title = "Title", string theme = "Theme") { var segment = StorySegment.Create(levelId, lessonId, content, order, title, theme); typeof(StorySegment).GetProperty("Id")?.SetValue(segment, id); return segment; } #region HandleLessonCompletionAsync Tests [TestMethod] public async Task HandleLessonCompletionAsync_WithValidLesson_UnlocksSegment() { // Arrange var userId = 1; var lessonId = 1; var lesson = CreateTestLesson(lessonId, 1, 1); var segment = CreateTestSegment(100, 1, 2, 2, "Next segment", "Next Title", "Theme"); _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync(lesson); _mockUserProgressRepository.Setup(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny())) .ReturnsAsync(true); _mockStoryService.Setup(s => s.GetNextSegmentToUnlockAsync(lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync(StorySegmentDto.FromEntity(segment)); _mockStoryService.Setup(s => s.IsSegmentUnlockedAsync(userId, segment.Id, It.IsAny())) .ReturnsAsync(false); _mockStoryService.Setup(s => s.UnlockNextSegmentAsync(userId, lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync(StorySegmentDto.FromEntity(segment)); // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsTrue(result); _mockLessonRepository.Verify(l => l.GetByIdAsync(lessonId, It.IsAny()), Times.Once); _mockUserProgressRepository.Verify(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny()), Times.Once); _mockStoryService.Verify(s => s.UnlockNextSegmentAsync(userId, lesson.LevelId, lesson.Order, It.IsAny()), Times.Once); } [TestMethod] public async Task HandleLessonCompletionAsync_WithNonExistingLesson_ReturnsFalse() { // Arrange var userId = 1; var lessonId = 999; _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync((Lesson?)null); // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsFalse(result); } [TestMethod] public async Task HandleLessonCompletionAsync_WithLessonNotCompleted_ReturnsFalse() { // Arrange var userId = 1; var lessonId = 1; var lesson = CreateTestLesson(lessonId, 1, 1); _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync(lesson); _mockUserProgressRepository.Setup(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny())) .ReturnsAsync(false); // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsFalse(result); } [TestMethod] public async Task HandleLessonCompletionAsync_WithNoSegmentToUnlock_ReturnsFalse() { // Arrange var userId = 1; var lessonId = 1; var lesson = CreateTestLesson(lessonId, 1, 10); // High order, no next segment _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync(lesson); _mockUserProgressRepository.Setup(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny())) .ReturnsAsync(true); _mockStoryService.Setup(s => s.GetNextSegmentToUnlockAsync(lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync((StorySegmentDto?)null); // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsFalse(result); } [TestMethod] public async Task HandleLessonCompletionAsync_WithSegmentAlreadyUnlocked_ReturnsFalse() { // Arrange var userId = 1; var lessonId = 1; var lesson = CreateTestLesson(lessonId, 1, 1); var segment = CreateTestSegment(100, 1, 2, 2, "Next segment", "Next Title", "Theme"); _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync(lesson); _mockUserProgressRepository.Setup(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny())) .ReturnsAsync(true); _mockStoryService.Setup(s => s.GetNextSegmentToUnlockAsync(lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync(StorySegmentDto.FromEntity(segment)); _mockStoryService.Setup(s => s.IsSegmentUnlockedAsync(userId, segment.Id, It.IsAny())) .ReturnsAsync(true); // Already unlocked // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsFalse(result); _mockStoryService.Verify(s => s.UnlockNextSegmentAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); } [TestMethod] public async Task HandleLessonCompletionAsync_WithUnlockFailure_ReturnsFalse() { // Arrange var userId = 1; var lessonId = 1; var lesson = CreateTestLesson(lessonId, 1, 1); var segment = CreateTestSegment(100, 1, 2, 2, "Next segment", "Next Title", "Theme"); _mockLessonRepository.Setup(l => l.GetByIdAsync(lessonId, It.IsAny())) .ReturnsAsync(lesson); _mockUserProgressRepository.Setup(u => u.HasUserCompletedLessonAsync(userId, lessonId, It.IsAny())) .ReturnsAsync(true); _mockStoryService.Setup(s => s.GetNextSegmentToUnlockAsync(lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync(StorySegmentDto.FromEntity(segment)); _mockStoryService.Setup(s => s.IsSegmentUnlockedAsync(userId, segment.Id, It.IsAny())) .ReturnsAsync(false); _mockStoryService.Setup(s => s.UnlockNextSegmentAsync(userId, lesson.LevelId, lesson.Order, It.IsAny())) .ReturnsAsync((StorySegmentDto?)null); // Unlock failed // Act var result = await _service.HandleLessonCompletionAsync(userId, lessonId); // Assert Assert.IsFalse(result); } #endregion #region Delegation Tests [TestMethod] public async Task IsSegmentUnlockedAsync_DelegatesToStoryService() { // Arrange var userId = 1; var segmentId = 100; _mockStoryService.Setup(s => s.IsSegmentUnlockedAsync(userId, segmentId, It.IsAny())) .ReturnsAsync(true); // Act var result = await _service.IsSegmentUnlockedAsync(userId, segmentId); // Assert Assert.IsTrue(result); _mockStoryService.Verify(s => s.IsSegmentUnlockedAsync(userId, segmentId, It.IsAny()), Times.Once); } [TestMethod] public async Task IsSegmentCompletedAsync_DelegatesToStoryService() { // Arrange var userId = 1; var segmentId = 100; _mockStoryService.Setup(s => s.IsSegmentCompletedAsync(userId, segmentId, It.IsAny())) .ReturnsAsync(true); // Act var result = await _service.IsSegmentCompletedAsync(userId, segmentId); // Assert Assert.IsTrue(result); _mockStoryService.Verify(s => s.IsSegmentCompletedAsync(userId, segmentId, It.IsAny()), Times.Once); } [TestMethod] public async Task MarkSegmentAsCompletedAsync_DelegatesToStoryService() { // Arrange var userId = 1; var segmentId = 100; _mockStoryService.Setup(s => s.MarkSegmentAsCompletedAsync(userId, segmentId, It.IsAny())) .ReturnsAsync(true); // Act var result = await _service.MarkSegmentAsCompletedAsync(userId, segmentId); // Assert Assert.IsTrue(result); _mockStoryService.Verify(s => s.MarkSegmentAsCompletedAsync(userId, segmentId, It.IsAny()), Times.Once); } [TestMethod] public async Task GetUserProgressAsync_DelegatesToStoryService() { // Arrange var userId = 1; var levelId = 1; var expectedProgress = new StoryProgressDto(1, "A1", 5, 3, 2, new List()); _mockStoryService.Setup(s => s.GetUserProgressAsync(userId, levelId, It.IsAny())) .ReturnsAsync(expectedProgress); // Act var result = await _service.GetUserProgressAsync(userId, levelId); // Assert Assert.IsNotNull(result); Assert.AreEqual(levelId, result.LevelId); Assert.AreEqual("A1", result.LevelName); _mockStoryService.Verify(s => s.GetUserProgressAsync(userId, levelId, It.IsAny()), Times.Once); } #endregion }