110 lines
4.0 KiB
C#
110 lines
4.0 KiB
C#
using System.Text.Json;
|
|
|
|
namespace Zcbot.WindowsNode.Tests;
|
|
|
|
public sealed class JobRepositoryCompatibilityTests : IDisposable
|
|
{
|
|
private readonly string root = Path.Combine(
|
|
Path.GetTempPath(), "zcbot-job-repository-tests", Guid.NewGuid().ToString("N"));
|
|
|
|
[Fact]
|
|
public void ReadsExistingRequestAndStateWithoutMigration()
|
|
{
|
|
var jobId = Guid.NewGuid();
|
|
var leaseId = Guid.NewGuid();
|
|
var directory = Path.Combine(root, jobId.ToString("D"));
|
|
Directory.CreateDirectory(Path.Combine(directory, "request"));
|
|
File.WriteAllText(
|
|
Path.Combine(directory, "request", "request.json"),
|
|
JsonSerializer.Serialize(new
|
|
{
|
|
job_id = jobId,
|
|
lease_id = leaseId,
|
|
request_digest = new string('b', 64),
|
|
capability = "origin.plot@v2",
|
|
accepted_at = "2026-08-20T01:02:03Z",
|
|
request = new { },
|
|
workspace = (object?)null,
|
|
input_transfers = Array.Empty<object>(),
|
|
}));
|
|
File.WriteAllText(
|
|
Path.Combine(directory, "state.json"),
|
|
"""{"stage":"software_running","progress":10,"detail":"running","updated_at":"2026-08-20T01:03:00Z"}""");
|
|
var monitor = new JobMonitorService(root);
|
|
var repository = new JobRepository(root);
|
|
|
|
var recovered = Assert.Single(repository.ReadRecoverableJobs());
|
|
var displayed = Assert.Single(monitor.ReadSnapshots());
|
|
|
|
Assert.Equal(jobId, recovered.JobId);
|
|
Assert.Equal(leaseId, recovered.LeaseId);
|
|
Assert.Equal("software_running", displayed.Stage);
|
|
Assert.False(recovered.UploadComplete);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecoveryTransitionPreservesTheExistingStateJsonShape()
|
|
{
|
|
var job = CreateJob();
|
|
var directory = Path.Combine(root, job.JobId.ToString("D"));
|
|
Directory.CreateDirectory(directory);
|
|
File.WriteAllText(
|
|
Path.Combine(directory, "state.json"),
|
|
"""{"stage":"software_running","progress":10,"detail":"running","updated_at":"2026-08-20T01:03:00Z"}""");
|
|
var repository = new JobRepository(root);
|
|
|
|
Assert.Throws<InvalidDataException>(() => repository.WriteState(
|
|
job, "downloading_inputs", 0, "retry"));
|
|
repository.WriteState(
|
|
job,
|
|
"downloading_inputs",
|
|
0,
|
|
"retry",
|
|
JobTransitionMode.Recovery);
|
|
|
|
using var state = JsonDocument.Parse(
|
|
File.ReadAllBytes(Path.Combine(directory, "state.json")));
|
|
Assert.Equal("downloading_inputs", state.RootElement.GetProperty("stage").GetString());
|
|
Assert.Equal(0, state.RootElement.GetProperty("progress").GetInt32());
|
|
Assert.Equal("retry", state.RootElement.GetProperty("detail").GetString());
|
|
Assert.True(state.RootElement.TryGetProperty("updated_at", out _));
|
|
Assert.Equal(4, state.RootElement.EnumerateObject().Count());
|
|
}
|
|
|
|
[Fact]
|
|
public void TerminalWriteRemainsIdempotent()
|
|
{
|
|
var job = CreateJob();
|
|
var repository = new JobRepository(root);
|
|
|
|
repository.WriteTerminal(job, "failed", "FIRST", "first failure");
|
|
repository.WriteTerminal(job, "cancelled", "SECOND", "late cancellation");
|
|
|
|
using var terminal = JsonDocument.Parse(File.ReadAllBytes(Path.Combine(
|
|
root, job.JobId.ToString("D"), "terminal.json")));
|
|
Assert.Equal("failed", terminal.RootElement.GetProperty("status").GetString());
|
|
Assert.Equal(
|
|
"FIRST",
|
|
terminal.RootElement.GetProperty("error").GetProperty("code").GetString());
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Directory.Exists(root))
|
|
{
|
|
Directory.Delete(root, recursive: true);
|
|
}
|
|
}
|
|
|
|
private static RecoverableJob CreateJob() => new(
|
|
Guid.NewGuid(),
|
|
Guid.NewGuid(),
|
|
new string('c', 64),
|
|
"origin.plot@v2",
|
|
null,
|
|
null,
|
|
null,
|
|
false,
|
|
false);
|
|
}
|