All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Add FluentValidation.AspNetCore package - Create LevelValidators (CreateLevelValidator, UpdateLevelValidator) - Create LessonValidators (CreateLessonValidator, UpdateLessonValidator) - Register FluentValidation in Program.cs with auto-validation - Update LevelsController and LessonsController to use IActionResult - Make service methods virtual for Moq testing compatibility - Add 26 integration tests for LevelsController (13 tests) - Add 34 integration tests for LessonsController (17 tests) Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
372 lines
13 KiB
C#
372 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using GermanApp.Application.DTOs;
|
|
using GermanApp.Application.Services;
|
|
using GermanApp.Presentation.Controllers;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
using Moq;
|
|
|
|
namespace GermanApp.Tests.Integration.Controllers;
|
|
|
|
/// <summary>
|
|
/// Integration tests for LessonsController.
|
|
/// Tests controller behavior with mocked services.
|
|
/// </summary>
|
|
[TestClass]
|
|
public class LessonsControllerTests
|
|
{
|
|
private Mock<LessonService>? _mockLessonService;
|
|
private Mock<ProgressService>? _mockProgressService;
|
|
private LessonsController? _controller;
|
|
|
|
[TestInitialize]
|
|
public void TestInitialize()
|
|
{
|
|
_mockLessonService = new Mock<LessonService>(null!, null!);
|
|
_mockProgressService = new Mock<ProgressService>(null!, null!, null!);
|
|
_controller = new LessonsController(_mockLessonService.Object, _mockProgressService.Object);
|
|
}
|
|
|
|
[TestCleanup]
|
|
public void TestCleanup()
|
|
{
|
|
_controller = null;
|
|
_mockLessonService = null;
|
|
_mockProgressService = null;
|
|
}
|
|
|
|
private static DateTime TestDate => new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
|
|
// ==================== GET ALL LESSONS TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetAllLessons")]
|
|
public async Task GetAllLessons_WithData_ReturnsOkWithLessons()
|
|
{
|
|
// Arrange
|
|
var expectedLessons = new List<LessonDto>
|
|
{
|
|
new LessonDto(1, "Greetings", "Learn basic greetings", 1, "A1", "Beginner A1", 1, "Vocabulary", TestDate, null),
|
|
new LessonDto(2, "Introductions", "Learn to introduce yourself", 1, "A1", "Beginner A1", 2, "Vocabulary", TestDate, null)
|
|
};
|
|
|
|
_mockLessonService!.Setup(s => s.GetAllLessonsAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLessons);
|
|
|
|
// Act
|
|
var result = await _controller!.GetAllLessonsAsync();
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLessons = okResult.Value as IReadOnlyList<LessonDto>;
|
|
Assert.IsNotNull(returnedLessons);
|
|
Assert.AreEqual(2, returnedLessons.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetAllLessons")]
|
|
public async Task GetAllLessons_WithNoData_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var expectedLessons = new List<LessonDto>();
|
|
|
|
_mockLessonService!.Setup(s => s.GetAllLessonsAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLessons);
|
|
|
|
// Act
|
|
var result = await _controller!.GetAllLessonsAsync();
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLessons = okResult.Value as IReadOnlyList<LessonDto>;
|
|
Assert.IsNotNull(returnedLessons);
|
|
Assert.AreEqual(0, returnedLessons.Count);
|
|
}
|
|
|
|
// ==================== GET LESSON BY ID TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetLessonById")]
|
|
public async Task GetLessonById_WithExistingId_ReturnsOkWithLesson()
|
|
{
|
|
// Arrange
|
|
var expectedLesson = new LessonDto(1, "Greetings", "Learn basic greetings", 1, "A1", "Beginner A1", 1, "Vocabulary", TestDate, null);
|
|
|
|
_mockLessonService!.Setup(s => s.GetLessonByIdAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLesson);
|
|
|
|
// Act
|
|
var result = await _controller!.GetLessonByIdAsync(1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLesson = okResult.Value as LessonDto;
|
|
Assert.IsNotNull(returnedLesson);
|
|
Assert.AreEqual(1, returnedLesson.Id);
|
|
Assert.AreEqual("Greetings", returnedLesson.Title);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetLessonById")]
|
|
public async Task GetLessonById_WithNonExistingId_ReturnsNotFound()
|
|
{
|
|
// Arrange
|
|
_mockLessonService!.Setup(s => s.GetLessonByIdAsync(999, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((LessonDto?)null);
|
|
|
|
// Act
|
|
var result = await _controller!.GetLessonByIdAsync(999);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(NotFoundResult));
|
|
}
|
|
|
|
// ==================== GET LESSONS BY LEVEL TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetLessonsByLevel")]
|
|
public async Task GetLessonsByLevel_WithExistingLevel_ReturnsOkWithLessons()
|
|
{
|
|
// Arrange
|
|
var expectedLessons = new List<LessonDto>
|
|
{
|
|
new LessonDto(1, "Greetings", "Learn basic greetings", 1, "A1", "Beginner A1", 1, "Vocabulary", TestDate, null)
|
|
};
|
|
|
|
_mockLessonService!.Setup(s => s.GetLessonsByLevelAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLessons);
|
|
|
|
// Act
|
|
var result = await _controller!.GetLessonsByLevelAsync(1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLessons = okResult.Value as IReadOnlyList<LessonDto>;
|
|
Assert.IsNotNull(returnedLessons);
|
|
Assert.AreEqual(1, returnedLessons.Count);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetLessonsByLevel")]
|
|
public async Task GetLessonsByLevel_WithNonExistingLevel_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
var expectedLessons = new List<LessonDto>();
|
|
|
|
_mockLessonService!.Setup(s => s.GetLessonsByLevelAsync(999, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLessons);
|
|
|
|
// Act
|
|
var result = await _controller!.GetLessonsByLevelAsync(999);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLessons = okResult.Value as IReadOnlyList<LessonDto>;
|
|
Assert.IsNotNull(returnedLessons);
|
|
Assert.AreEqual(0, returnedLessons.Count);
|
|
}
|
|
|
|
// ==================== CREATE LESSON TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("CreateLesson")]
|
|
public async Task CreateLesson_WithValidData_ReturnsCreatedAtAction()
|
|
{
|
|
// Arrange
|
|
var createDto = new CreateLessonDto("Greetings", "Learn basic greetings", 1, 1, "Vocabulary");
|
|
var expectedLesson = new LessonDto(1, "Greetings", "Learn basic greetings", 1, "A1", "Beginner A1", 1, "Vocabulary", TestDate, null);
|
|
|
|
_mockLessonService!.Setup(s => s.CreateLessonAsync(createDto, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLesson);
|
|
|
|
// Act
|
|
var result = await _controller!.CreateLessonAsync(createDto);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(CreatedAtActionResult));
|
|
var createdResult = result as CreatedAtActionResult;
|
|
Assert.IsNotNull(createdResult);
|
|
var returnedLesson = createdResult.Value as LessonDto;
|
|
Assert.IsNotNull(returnedLesson);
|
|
Assert.AreEqual(1, returnedLesson.Id);
|
|
}
|
|
|
|
// ==================== UPDATE LESSON TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("UpdateLesson")]
|
|
public async Task UpdateLesson_WithExistingId_ReturnsOkWithUpdatedLesson()
|
|
{
|
|
// Arrange
|
|
var updateDto = new UpdateLessonDto("Updated Greetings", "Updated description", 1, 1, "Updated Topic");
|
|
var expectedLesson = new LessonDto(1, "Updated Greetings", "Updated description", 1, "A1", "Beginner A1", 1, "Updated Topic", TestDate, TestDate);
|
|
|
|
_mockLessonService!.Setup(s => s.UpdateLessonAsync(1, updateDto, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLesson);
|
|
|
|
// Act
|
|
var result = await _controller!.UpdateLessonAsync(1, updateDto);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLesson = okResult.Value as LessonDto;
|
|
Assert.IsNotNull(returnedLesson);
|
|
Assert.AreEqual("Updated Greetings", returnedLesson.Title);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("UpdateLesson")]
|
|
public async Task UpdateLesson_WithNonExistingId_ReturnsNotFound()
|
|
{
|
|
// Arrange
|
|
var updateDto = new UpdateLessonDto("Updated Greetings", "Updated description", 1, 1, "Updated Topic");
|
|
|
|
_mockLessonService!.Setup(s => s.UpdateLessonAsync(999, updateDto, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((LessonDto?)null);
|
|
|
|
// Act
|
|
var result = await _controller!.UpdateLessonAsync(999, updateDto);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(NotFoundResult));
|
|
}
|
|
|
|
// ==================== DELETE LESSON TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("DeleteLesson")]
|
|
public async Task DeleteLesson_WithExistingId_ReturnsNoContent()
|
|
{
|
|
// Arrange
|
|
_mockLessonService!.Setup(s => s.DeleteLessonAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(true);
|
|
|
|
// Act
|
|
var result = await _controller!.DeleteLessonAsync(1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(NoContentResult));
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("DeleteLesson")]
|
|
public async Task DeleteLesson_WithNonExistingId_ReturnsNotFound()
|
|
{
|
|
// Arrange
|
|
_mockLessonService!.Setup(s => s.DeleteLessonAsync(999, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(false);
|
|
|
|
// Act
|
|
var result = await _controller!.DeleteLessonAsync(999);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(NotFoundResult));
|
|
}
|
|
|
|
// ==================== GET FIRST LESSON IN LEVEL TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetFirstLessonInLevel")]
|
|
public async Task GetFirstLessonInLevel_WithExistingLevel_ReturnsOkWithLesson()
|
|
{
|
|
// Arrange
|
|
var expectedLesson = new LessonDto(1, "Greetings", "Learn basic greetings", 1, "A1", "Beginner A1", 1, "Vocabulary", TestDate, null);
|
|
|
|
_mockLessonService!.Setup(s => s.GetFirstLessonInLevelAsync(1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(expectedLesson);
|
|
|
|
// Act
|
|
var result = await _controller!.GetFirstLessonInLevelAsync(1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
var returnedLesson = okResult.Value as LessonDto;
|
|
Assert.IsNotNull(returnedLesson);
|
|
Assert.AreEqual(1, returnedLesson.Order);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("GetFirstLessonInLevel")]
|
|
public async Task GetFirstLessonInLevel_WithNonExistingLevel_ReturnsNotFound()
|
|
{
|
|
// Arrange
|
|
_mockLessonService!.Setup(s => s.GetFirstLessonInLevelAsync(999, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync((LessonDto?)null);
|
|
|
|
// Act
|
|
var result = await _controller!.GetFirstLessonInLevelAsync(999);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(NotFoundResult));
|
|
}
|
|
|
|
// ==================== IS NEXT LESSON UNLOCKED TESTS ====================
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("IsNextLessonUnlocked")]
|
|
public async Task IsNextLessonUnlocked_WithCompletedPrevious_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
_mockProgressService!.Setup(s => s.IsNextLessonUnlockedAsync(1, 1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(true);
|
|
|
|
// Act
|
|
var result = await _controller!.IsNextLessonUnlockedAsync(1, 1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
Assert.AreEqual(true, okResult.Value);
|
|
}
|
|
|
|
[TestMethod]
|
|
[TestCategory("LessonsController")]
|
|
[TestCategory("IsNextLessonUnlocked")]
|
|
public async Task IsNextLessonUnlocked_WithIncompletePrevious_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
_mockProgressService!.Setup(s => s.IsNextLessonUnlockedAsync(1, 1, It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(false);
|
|
|
|
// Act
|
|
var result = await _controller!.IsNextLessonUnlockedAsync(1, 1);
|
|
|
|
// Assert
|
|
Assert.IsInstanceOfType(result, typeof(OkObjectResult));
|
|
var okResult = result as OkObjectResult;
|
|
Assert.IsNotNull(okResult);
|
|
Assert.AreEqual(false, okResult.Value);
|
|
}
|
|
}
|