feat: Implement Filesystem and MongoDB provenance writers for PackRun execution context
- Added `FilesystemPackRunProvenanceWriter` to write provenance manifests to the filesystem. - Introduced `MongoPackRunArtifactReader` to read artifacts from MongoDB. - Created `MongoPackRunProvenanceWriter` to store provenance manifests in MongoDB. - Developed unit tests for filesystem and MongoDB provenance writers. - Established `ITimelineEventStore` and `ITimelineIngestionService` interfaces for timeline event handling. - Implemented `TimelineIngestionService` to validate and persist timeline events with hashing. - Created PostgreSQL schema and migration scripts for timeline indexing. - Added dependency injection support for timeline indexer services. - Developed tests for timeline ingestion and schema validation.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using MongoDB.Driver;
|
||||
using StellaOps.TaskRunner.Core.Execution;
|
||||
using StellaOps.TaskRunner.Core.Execution.Simulation;
|
||||
using StellaOps.TaskRunner.Core.Planning;
|
||||
using StellaOps.TaskRunner.Core.TaskPacks;
|
||||
using StellaOps.TaskRunner.Infrastructure.Execution;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.TaskRunner.Tests;
|
||||
|
||||
public sealed class PackRunProvenanceWriterTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Filesystem_writer_emits_manifest()
|
||||
{
|
||||
var (context, state) = CreateRunState();
|
||||
var completedAt = new DateTimeOffset(2025, 11, 30, 12, 30, 0, TimeSpan.Zero);
|
||||
var temp = Directory.CreateTempSubdirectory();
|
||||
try
|
||||
{
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
var writer = new FilesystemPackRunProvenanceWriter(temp.FullName, new FixedTimeProvider(completedAt));
|
||||
await writer.WriteAsync(context, state, ct);
|
||||
|
||||
var path = Path.Combine(temp.FullName, "provenance", "run-test.json");
|
||||
Assert.True(File.Exists(path));
|
||||
|
||||
using var document = JsonDocument.Parse(await File.ReadAllTextAsync(path, ct));
|
||||
var root = document.RootElement;
|
||||
Assert.Equal("run-test", root.GetProperty("runId").GetString());
|
||||
Assert.Equal("tenant-alpha", root.GetProperty("tenantId").GetString());
|
||||
Assert.Equal(context.Plan.Hash, root.GetProperty("planHash").GetString());
|
||||
Assert.Equal(completedAt, root.GetProperty("completedAt").GetDateTimeOffset());
|
||||
}
|
||||
finally
|
||||
{
|
||||
temp.Delete(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Mongo_writer_upserts_manifest()
|
||||
{
|
||||
await using var mongo = MongoTaskRunnerTestContext.Create();
|
||||
var (context, state) = CreateRunState();
|
||||
var completedAt = new DateTimeOffset(2025, 11, 30, 12, 0, 0, TimeSpan.Zero);
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
|
||||
var options = mongo.CreateMongoOptions();
|
||||
var writer = new MongoPackRunProvenanceWriter(mongo.Database, options, new FixedTimeProvider(completedAt));
|
||||
await writer.WriteAsync(context, state, ct);
|
||||
|
||||
var collection = mongo.Database.GetCollection<MongoDB.Bson.BsonDocument>(options.ArtifactsCollection);
|
||||
var saved = await collection
|
||||
.Find(Builders<MongoDB.Bson.BsonDocument>.Filter.Eq("RunId", context.RunId))
|
||||
.FirstOrDefaultAsync(ct);
|
||||
|
||||
Assert.NotNull(saved);
|
||||
var manifest = saved!["Expression"].AsBsonDocument;
|
||||
Assert.Equal("run-test", manifest["runId"].AsString);
|
||||
Assert.Equal("tenant-alpha", manifest["tenantId"].AsString);
|
||||
Assert.Equal(context.Plan.Hash, manifest["planHash"].AsString);
|
||||
}
|
||||
|
||||
private static (PackRunExecutionContext Context, PackRunState State) CreateRunState()
|
||||
{
|
||||
var loader = new TaskPackManifestLoader();
|
||||
var planner = new TaskPackPlanner();
|
||||
var manifest = loader.Deserialize(TestManifests.Sample);
|
||||
var plan = planner.Plan(manifest, new Dictionary<string, JsonNode?>()).Plan ?? throw new InvalidOperationException("Plan generation failed.");
|
||||
|
||||
var graphBuilder = new PackRunExecutionGraphBuilder();
|
||||
var simulationEngine = new PackRunSimulationEngine();
|
||||
var graph = graphBuilder.Build(plan);
|
||||
|
||||
var requestedAt = new DateTimeOffset(2025, 11, 30, 10, 0, 0, TimeSpan.Zero);
|
||||
var context = new PackRunExecutionContext("run-test", plan, requestedAt, "tenant-alpha");
|
||||
var state = PackRunStateFactory.CreateInitialState(context, graph, simulationEngine, requestedAt);
|
||||
return (context, state);
|
||||
}
|
||||
|
||||
private sealed class FixedTimeProvider : TimeProvider
|
||||
{
|
||||
private readonly DateTimeOffset now;
|
||||
|
||||
public FixedTimeProvider(DateTimeOffset now)
|
||||
{
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
public override DateTimeOffset GetUtcNow() => now;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user