diff --git a/.idea/.idea.LiquidCode.Tester/.idea/workspace.xml b/.idea/.idea.LiquidCode.Tester/.idea/workspace.xml
index 6662305..7406874 100644
--- a/.idea/.idea.LiquidCode.Tester/.idea/workspace.xml
+++ b/.idea/.idea.LiquidCode.Tester/.idea/workspace.xml
@@ -12,12 +12,14 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -263,8 +265,17 @@
1761331001679
-
+
+
+
+ 1761333540672
+
+
+
+ 1761333540672
+
+
@@ -272,8 +283,21 @@
+
+
+
+
+
diff --git a/global.json b/global.json
index 93681ff..33d0916 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"sdk": {
- "version": "9.0.0",
+ "version": "10.0.100-rc.2.25502.107",
"rollForward": "latestMinor",
- "allowPrerelease": false
+ "allowPrerelease": true
}
}
\ No newline at end of file
diff --git a/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj b/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj
index e8c41ec..a5dfc1f 100644
--- a/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj
+++ b/src/LiquidCode.Tester.Common/LiquidCode.Tester.Common.csproj
@@ -1,11 +1,9 @@
- Exe
net9.0
enable
enable
- Linux
diff --git a/src/LiquidCode.Tester.Common/Models/ErrorCode.cs b/src/LiquidCode.Tester.Common/Models/ErrorCode.cs
new file mode 100644
index 0000000..5c0aa54
--- /dev/null
+++ b/src/LiquidCode.Tester.Common/Models/ErrorCode.cs
@@ -0,0 +1,12 @@
+namespace LiquidCode.Tester.Common.Models;
+
+public enum ErrorCode
+{
+ None,
+ CompileError,
+ RuntimeError,
+ MemoryError,
+ TimeLimitError,
+ IncorrectAnswer,
+ UnknownError
+}
diff --git a/src/LiquidCode.Tester.Common/Models/State.cs b/src/LiquidCode.Tester.Common/Models/State.cs
new file mode 100644
index 0000000..e98cc00
--- /dev/null
+++ b/src/LiquidCode.Tester.Common/Models/State.cs
@@ -0,0 +1,9 @@
+namespace LiquidCode.Tester.Common.Models;
+
+public enum State
+{
+ Waiting,
+ Compiling,
+ Testing,
+ Done
+}
diff --git a/src/LiquidCode.Tester.Common/Models/SubmitForTesterModel.cs b/src/LiquidCode.Tester.Common/Models/SubmitForTesterModel.cs
new file mode 100644
index 0000000..9434997
--- /dev/null
+++ b/src/LiquidCode.Tester.Common/Models/SubmitForTesterModel.cs
@@ -0,0 +1,11 @@
+namespace LiquidCode.Tester.Common.Models;
+
+public record SubmitForTesterModel(
+ long Id,
+ long MissionId,
+ string Language,
+ string LanguageVersion,
+ string SourceCode,
+ string PackageUrl,
+ string CallbackUrl
+);
diff --git a/src/LiquidCode.Tester.Common/Models/TesterResponseModel.cs b/src/LiquidCode.Tester.Common/Models/TesterResponseModel.cs
new file mode 100644
index 0000000..52aa778
--- /dev/null
+++ b/src/LiquidCode.Tester.Common/Models/TesterResponseModel.cs
@@ -0,0 +1,10 @@
+namespace LiquidCode.Tester.Common.Models;
+
+public record TesterResponseModel(
+ long SubmitId,
+ State State,
+ ErrorCode ErrorCode,
+ string Message,
+ int CurrentTest,
+ int AmountOfTests
+);
diff --git a/src/LiquidCode.Tester.Common/Program.cs b/src/LiquidCode.Tester.Common/Program.cs
deleted file mode 100644
index e5dff12..0000000
--- a/src/LiquidCode.Tester.Common/Program.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-// See https://aka.ms/new-console-template for more information
-
-Console.WriteLine("Hello, World!");
\ No newline at end of file
diff --git a/src/LiquidCode.Tester.Gateway/Controllers/TesterController.cs b/src/LiquidCode.Tester.Gateway/Controllers/TesterController.cs
new file mode 100644
index 0000000..b94527f
--- /dev/null
+++ b/src/LiquidCode.Tester.Gateway/Controllers/TesterController.cs
@@ -0,0 +1,52 @@
+using LiquidCode.Tester.Common.Models;
+using LiquidCode.Tester.Gateway.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace LiquidCode.Tester.Gateway.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class TesterController : ControllerBase
+{
+ private readonly IPackageDownloadService _packageDownloadService;
+ private readonly IWorkerClientService _workerClientService;
+ private readonly ILogger _logger;
+
+ public TesterController(
+ IPackageDownloadService packageDownloadService,
+ IWorkerClientService workerClientService,
+ ILogger logger)
+ {
+ _packageDownloadService = packageDownloadService;
+ _workerClientService = workerClientService;
+ _logger = logger;
+ }
+
+ [HttpPost("submit")]
+ public async Task Submit([FromBody] SubmitForTesterModel request)
+ {
+ _logger.LogInformation("Received submit request for ID {SubmitId}", request.Id);
+
+ try
+ {
+ // Download the package
+ var packagePath = await _packageDownloadService.DownloadPackageAsync(request.PackageUrl);
+
+ // Send to appropriate worker based on language
+ await _workerClientService.SendToWorkerAsync(request, packagePath);
+
+ return Accepted(new { message = "Submit accepted for testing", submitId = request.Id });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to process submit {SubmitId}", request.Id);
+ return StatusCode(500, new { error = "Failed to process submit", details = ex.Message });
+ }
+ }
+
+ [HttpGet("health")]
+ public IActionResult Health()
+ {
+ return Ok(new { status = "healthy", timestamp = DateTime.UtcNow });
+ }
+}
diff --git a/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj b/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj
index bb9f7eb..c2a6c33 100644
--- a/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj
+++ b/src/LiquidCode.Tester.Gateway/LiquidCode.Tester.Gateway.csproj
@@ -11,6 +11,10 @@
+
+
+
+
.dockerignore
diff --git a/src/LiquidCode.Tester.Gateway/Program.cs b/src/LiquidCode.Tester.Gateway/Program.cs
index d5e0ef3..5bb4655 100644
--- a/src/LiquidCode.Tester.Gateway/Program.cs
+++ b/src/LiquidCode.Tester.Gateway/Program.cs
@@ -1,41 +1,26 @@
+using LiquidCode.Tester.Gateway.Services;
+
var builder = WebApplication.CreateBuilder(args);
-// Add services to the container.
-// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
+// Add services to the container
+builder.Services.AddControllers();
builder.Services.AddOpenApi();
+// Add HttpClient
+builder.Services.AddHttpClient();
+
+// Register application services
+builder.Services.AddSingleton();
+builder.Services.AddSingleton();
+
var app = builder.Build();
-// Configure the HTTP request pipeline.
+// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
}
-app.UseHttpsRedirection();
-
-var summaries = new[]
-{
- "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
-};
-
-app.MapGet("/weatherforecast", () =>
- {
- var forecast = Enumerable.Range(1, 5).Select(index =>
- new WeatherForecast
- (
- DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
- Random.Shared.Next(-20, 55),
- summaries[Random.Shared.Next(summaries.Length)]
- ))
- .ToArray();
- return forecast;
- })
- .WithName("GetWeatherForecast");
+app.MapControllers();
app.Run();
-
-record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
-{
- public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-}
\ No newline at end of file
diff --git a/src/LiquidCode.Tester.Gateway/Services/IPackageDownloadService.cs b/src/LiquidCode.Tester.Gateway/Services/IPackageDownloadService.cs
new file mode 100644
index 0000000..1c23152
--- /dev/null
+++ b/src/LiquidCode.Tester.Gateway/Services/IPackageDownloadService.cs
@@ -0,0 +1,11 @@
+namespace LiquidCode.Tester.Gateway.Services;
+
+public interface IPackageDownloadService
+{
+ ///
+ /// Downloads a package from the specified URL
+ ///
+ /// URL to download the package from
+ /// Path to the downloaded package file
+ Task DownloadPackageAsync(string packageUrl);
+}
diff --git a/src/LiquidCode.Tester.Gateway/Services/IWorkerClientService.cs b/src/LiquidCode.Tester.Gateway/Services/IWorkerClientService.cs
new file mode 100644
index 0000000..2d49696
--- /dev/null
+++ b/src/LiquidCode.Tester.Gateway/Services/IWorkerClientService.cs
@@ -0,0 +1,13 @@
+using LiquidCode.Tester.Common.Models;
+
+namespace LiquidCode.Tester.Gateway.Services;
+
+public interface IWorkerClientService
+{
+ ///
+ /// Sends a submit to the appropriate worker based on the language
+ ///
+ /// Submit data
+ /// Local path to the downloaded package
+ Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath);
+}
diff --git a/src/LiquidCode.Tester.Gateway/Services/PackageDownloadService.cs b/src/LiquidCode.Tester.Gateway/Services/PackageDownloadService.cs
new file mode 100644
index 0000000..5abab22
--- /dev/null
+++ b/src/LiquidCode.Tester.Gateway/Services/PackageDownloadService.cs
@@ -0,0 +1,49 @@
+namespace LiquidCode.Tester.Gateway.Services;
+
+public class PackageDownloadService : IPackageDownloadService
+{
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly ILogger _logger;
+ private readonly string _downloadDirectory;
+
+ public PackageDownloadService(
+ IHttpClientFactory httpClientFactory,
+ ILogger logger,
+ IConfiguration configuration)
+ {
+ _httpClientFactory = httpClientFactory;
+ _logger = logger;
+ _downloadDirectory = configuration["PackageDownloadDirectory"] ?? Path.Combine(Path.GetTempPath(), "packages");
+
+ if (!Directory.Exists(_downloadDirectory))
+ {
+ Directory.CreateDirectory(_downloadDirectory);
+ }
+ }
+
+ public async Task DownloadPackageAsync(string packageUrl)
+ {
+ _logger.LogInformation("Downloading package from {Url}", packageUrl);
+
+ try
+ {
+ var httpClient = _httpClientFactory.CreateClient();
+ var response = await httpClient.GetAsync(packageUrl);
+ response.EnsureSuccessStatusCode();
+
+ var fileName = $"package_{Guid.NewGuid()}.zip";
+ var filePath = Path.Combine(_downloadDirectory, fileName);
+
+ await using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
+ await response.Content.CopyToAsync(fileStream);
+
+ _logger.LogInformation("Package downloaded successfully to {Path}", filePath);
+ return filePath;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to download package from {Url}", packageUrl);
+ throw;
+ }
+ }
+}
diff --git a/src/LiquidCode.Tester.Gateway/Services/WorkerClientService.cs b/src/LiquidCode.Tester.Gateway/Services/WorkerClientService.cs
new file mode 100644
index 0000000..41da9ab
--- /dev/null
+++ b/src/LiquidCode.Tester.Gateway/Services/WorkerClientService.cs
@@ -0,0 +1,92 @@
+using System.Net.Http.Headers;
+using LiquidCode.Tester.Common.Models;
+
+namespace LiquidCode.Tester.Gateway.Services;
+
+public class WorkerClientService : IWorkerClientService
+{
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly ILogger _logger;
+ private readonly IConfiguration _configuration;
+
+ public WorkerClientService(
+ IHttpClientFactory httpClientFactory,
+ ILogger logger,
+ IConfiguration configuration)
+ {
+ _httpClientFactory = httpClientFactory;
+ _logger = logger;
+ _configuration = configuration;
+ }
+
+ public async Task SendToWorkerAsync(SubmitForTesterModel submit, string packagePath)
+ {
+ var workerUrl = GetWorkerUrlForLanguage(submit.Language);
+ _logger.LogInformation("Sending submit {SubmitId} to worker at {WorkerUrl}", submit.Id, workerUrl);
+
+ try
+ {
+ var httpClient = _httpClientFactory.CreateClient();
+
+ using var form = new MultipartFormDataContent();
+
+ // Add submit metadata
+ form.Add(new StringContent(submit.Id.ToString()), "Id");
+ form.Add(new StringContent(submit.MissionId.ToString()), "MissionId");
+ form.Add(new StringContent(submit.Language), "Language");
+ form.Add(new StringContent(submit.LanguageVersion), "LanguageVersion");
+ form.Add(new StringContent(submit.SourceCode), "SourceCode");
+ form.Add(new StringContent(submit.CallbackUrl), "CallbackUrl");
+
+ // Add package file
+ var fileStream = File.OpenRead(packagePath);
+ var fileContent = new StreamContent(fileStream);
+ fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
+ form.Add(fileContent, "Package", Path.GetFileName(packagePath));
+
+ var response = await httpClient.PostAsync($"{workerUrl}/api/test", form);
+ response.EnsureSuccessStatusCode();
+
+ _logger.LogInformation("Submit {SubmitId} sent successfully to worker", submit.Id);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to send submit {SubmitId} to worker", submit.Id);
+ throw;
+ }
+ finally
+ {
+ // Clean up downloaded package
+ try
+ {
+ if (File.Exists(packagePath))
+ {
+ File.Delete(packagePath);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogWarning(ex, "Failed to delete package file {Path}", packagePath);
+ }
+ }
+ }
+
+ private string GetWorkerUrlForLanguage(string language)
+ {
+ var workerUrl = language.ToLowerInvariant() switch
+ {
+ "c++" => _configuration["Workers:Cpp"],
+ "java" => _configuration["Workers:Java"],
+ "kotlin" => _configuration["Workers:Kotlin"],
+ "c#" => _configuration["Workers:CSharp"],
+ _ => throw new NotSupportedException($"Language {language} is not supported")
+ };
+
+ if (string.IsNullOrEmpty(workerUrl))
+ {
+ throw new InvalidOperationException($"Worker URL for language {language} is not configured");
+ }
+
+ return workerUrl;
+ }
+}
diff --git a/src/LiquidCode.Tester.Gateway/appsettings.Development.json b/src/LiquidCode.Tester.Gateway/appsettings.Development.json
index 0c208ae..495e353 100644
--- a/src/LiquidCode.Tester.Gateway/appsettings.Development.json
+++ b/src/LiquidCode.Tester.Gateway/appsettings.Development.json
@@ -4,5 +4,9 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
+ },
+ "PackageDownloadDirectory": "C:\\temp\\packages",
+ "Workers": {
+ "Cpp": "http://localhost:8081"
}
}
diff --git a/src/LiquidCode.Tester.Gateway/appsettings.json b/src/LiquidCode.Tester.Gateway/appsettings.json
index 10f68b8..3f0f92b 100644
--- a/src/LiquidCode.Tester.Gateway/appsettings.json
+++ b/src/LiquidCode.Tester.Gateway/appsettings.json
@@ -5,5 +5,12 @@
"Microsoft.AspNetCore": "Warning"
}
},
- "AllowedHosts": "*"
+ "AllowedHosts": "*",
+ "PackageDownloadDirectory": "/tmp/packages",
+ "Workers": {
+ "Cpp": "http://liquidcode-tester-worker-cpp:8080",
+ "Java": "http://liquidcode-tester-worker-java:8080",
+ "Kotlin": "http://liquidcode-tester-worker-kotlin:8080",
+ "CSharp": "http://liquidcode-tester-worker-csharp:8080"
+ }
}
diff --git a/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj b/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj
index e8c41ec..a993745 100644
--- a/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj
+++ b/src/LiquidCode.Tester.Worker/LiquidCode.Tester.Worker.csproj
@@ -1,13 +1,20 @@
-
+
- Exe
net9.0
enable
enable
Linux
+
+
+
+
+
+
+
+
.dockerignore