212 lines
7.0 KiB
C#
212 lines
7.0 KiB
C#
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<IPackageParserService> _packageParserMock;
|
|
private readonly Mock<ICompilationServiceFactory> _compilationFactoryMock;
|
|
private readonly Mock<IExecutionServiceFactory> _executionFactoryMock;
|
|
private readonly Mock<IOutputCheckerService> _outputCheckerMock;
|
|
private readonly Mock<ICallbackService> _callbackServiceMock;
|
|
private readonly Mock<ILogger<TestingService>> _loggerMock;
|
|
private readonly TestingService _service;
|
|
private readonly string _testDirectory;
|
|
|
|
public TestingServiceTests()
|
|
{
|
|
_packageParserMock = new Mock<IPackageParserService>();
|
|
_compilationFactoryMock = new Mock<ICompilationServiceFactory>();
|
|
_executionFactoryMock = new Mock<IExecutionServiceFactory>();
|
|
_outputCheckerMock = new Mock<IOutputCheckerService>();
|
|
_callbackServiceMock = new Mock<ICallbackService>();
|
|
_loggerMock = new Mock<ILogger<TestingService>>();
|
|
|
|
_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,
|
|
TestCases = new List<TestCase>() // Empty list!
|
|
};
|
|
|
|
_packageParserMock
|
|
.Setup(x => x.ParsePackageAsync(It.IsAny<Stream>()))
|
|
.ReturnsAsync(emptyPackage);
|
|
|
|
// Act
|
|
await _service.ProcessSubmitAsync(request);
|
|
|
|
// Assert - verify callback was called with error
|
|
_callbackServiceMock.Verify(
|
|
x => x.SendStatusAsync(
|
|
request.CallbackUrl,
|
|
It.Is<TesterResponseModel>(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<string>()),
|
|
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");
|
|
|
|
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,
|
|
TestCases = new List<TestCase>
|
|
{
|
|
new TestCase
|
|
{
|
|
Number = 1,
|
|
InputFilePath = inputFile,
|
|
OutputFilePath = outputFile,
|
|
TimeLimit = 2000,
|
|
MemoryLimit = 256
|
|
}
|
|
}
|
|
};
|
|
|
|
var compilationService = new Mock<ICompilationService>();
|
|
var executionService = new Mock<IExecutionService>();
|
|
|
|
_packageParserMock
|
|
.Setup(x => x.ParsePackageAsync(It.IsAny<Stream>()))
|
|
.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<string>(), It.IsAny<string>(), It.IsAny<string>()))
|
|
.ReturnsAsync(new CompilationResult
|
|
{
|
|
Success = true,
|
|
ExecutablePath = executablePath
|
|
});
|
|
|
|
executionService
|
|
.Setup(x => x.ExecuteAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
|
.ReturnsAsync(new ExecutionResult
|
|
{
|
|
Success = true,
|
|
Output = "expected output",
|
|
ExitCode = 0,
|
|
RuntimeError = false,
|
|
TimeLimitExceeded = false,
|
|
MemoryLimitExceeded = false
|
|
});
|
|
|
|
_outputCheckerMock
|
|
.Setup(x => x.CheckOutputAsync(It.IsAny<string>(), It.IsAny<string>()))
|
|
.ReturnsAsync(true);
|
|
|
|
// Act
|
|
await _service.ProcessSubmitAsync(request);
|
|
|
|
// Assert - verify callback was called with success
|
|
_callbackServiceMock.Verify(
|
|
x => x.SendStatusAsync(
|
|
request.CallbackUrl,
|
|
It.Is<TesterResponseModel>(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
|
|
}
|
|
}
|
|
}
|
|
}
|