feat: Implement Filesystem and MongoDB provenance writers for PackRun execution context
Some checks failed
Airgap Sealed CI Smoke / sealed-smoke (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled

- 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:
StellaOps Bot
2025-11-30 15:38:14 +02:00
parent 8f54ffa203
commit 17d45a6d30
276 changed files with 8618 additions and 688 deletions

View File

@@ -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;
}
}