using System.IO.Compression; using LiquidCode.Tester.Common.Models; using LiquidCode.Tester.Worker.Controllers; using LiquidCode.Tester.Worker.Services; using Microsoft.Extensions.Logging; using Moq; namespace LiquidCode.Tester.Worker.Tests; public class TestingServiceTests : IDisposable { private readonly Mock _packageParserMock; private readonly Mock _compilationFactoryMock; private readonly Mock _executionFactoryMock; private readonly Mock _outputCheckerMock; private readonly Mock _callbackServiceMock; private readonly Mock> _loggerMock; private readonly TestingService _service; private readonly string _testDirectory; public TestingServiceTests() { _packageParserMock = new Mock(); _compilationFactoryMock = new Mock(); _executionFactoryMock = new Mock(); _outputCheckerMock = new Mock(); _callbackServiceMock = new Mock(); _loggerMock = new Mock>(); _service = new TestingService( _packageParserMock.Object, _compilationFactoryMock.Object, _executionFactoryMock.Object, _outputCheckerMock.Object, _callbackServiceMock.Object, _loggerMock.Object ); _testDirectory = Path.Combine(Path.GetTempPath(), "TestingServiceTests", Guid.NewGuid().ToString()); Directory.CreateDirectory(_testDirectory); } [Fact] public async Task ProcessSubmitAsync_EmptyPackage_ReturnsUnknownError() { // Arrange var packageFilePath = Path.Combine(_testDirectory, "empty_package.zip"); await CreateEmptyPackage(packageFilePath); var request = new TestRequest { Id = 123, MissionId = 456, Language = "cpp", LanguageVersion = "17", SourceCode = "int main() { return 0; }", PackageFilePath = packageFilePath, CallbackUrl = "http://localhost/callback" }; var emptyPackage = new ProblemPackage { WorkingDirectory = _testDirectory, ExtractionRoot = _testDirectory, TestCases = new List() // Empty list! }; _packageParserMock .Setup(x => x.ParsePackageAsync(It.IsAny())) .ReturnsAsync(emptyPackage); // Act await _service.ProcessSubmitAsync(request); // Assert - verify callback was called with error _callbackServiceMock.Verify( x => x.SendStatusAsync( request.CallbackUrl, It.Is(r => r.State == State.Done && r.ErrorCode == ErrorCode.UnknownError && r.Message == "No test cases found in package" ) ), Times.Once ); // Verify compilation was NOT attempted _compilationFactoryMock.Verify( x => x.GetCompilationService(It.IsAny()), Times.Never ); } [Fact] public async Task ProcessSubmitAsync_ValidPackage_RunsAllTests() { // Arrange var packageFilePath = Path.Combine(_testDirectory, "valid_package.zip"); var inputFile = Path.Combine(_testDirectory, "1.in"); var outputFile = Path.Combine(_testDirectory, "1.out"); var executablePath = Path.Combine(_testDirectory, "solution.exe"); await File.WriteAllTextAsync(inputFile, "test input"); await File.WriteAllTextAsync(outputFile, "expected output"); await File.WriteAllTextAsync(executablePath, "dummy"); await CreateEmptyPackage(packageFilePath); var request = new TestRequest { Id = 123, MissionId = 456, Language = "cpp", LanguageVersion = "17", SourceCode = "int main() { return 0; }", PackageFilePath = packageFilePath, CallbackUrl = "http://localhost/callback" }; var package = new ProblemPackage { WorkingDirectory = _testDirectory, ExtractionRoot = _testDirectory, TestCases = new List { new TestCase { Number = 1, InputFilePath = inputFile, OutputFilePath = outputFile, TimeLimit = 2000, MemoryLimit = 256 } } }; var compilationService = new Mock(); var executionService = new Mock(); _packageParserMock .Setup(x => x.ParsePackageAsync(It.IsAny())) .ReturnsAsync(package); _compilationFactoryMock .Setup(x => x.GetCompilationService("cpp")) .Returns(compilationService.Object); _executionFactoryMock .Setup(x => x.GetExecutionService("cpp")) .Returns(executionService.Object); compilationService .Setup(x => x.CompileAsync(It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new CompilationResult { Success = true, ExecutablePath = executablePath }); executionService .Setup(x => x.ExecuteAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(new ExecutionResult { Success = true, Output = "expected output", ExitCode = 0, RuntimeError = false, TimeLimitExceeded = false, MemoryLimitExceeded = false }); _outputCheckerMock .Setup(x => x.CheckOutputWithCheckerAsync( It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .ReturnsAsync(true); // Act await _service.ProcessSubmitAsync(request); // Assert - verify callback was called with success _callbackServiceMock.Verify( x => x.SendStatusAsync( request.CallbackUrl, It.Is(r => r.State == State.Done && r.ErrorCode == ErrorCode.None && r.Message == "All tests passed" ) ), Times.AtLeastOnce ); } private async Task CreateEmptyPackage(string filePath) { using var fileStream = File.Create(filePath); using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create); // Create empty archive } public void Dispose() { if (Directory.Exists(_testDirectory)) { try { Directory.Delete(_testDirectory, true); } catch { // Ignore cleanup errors in tests } } } }