w4c-workflows-api/w4c-workflows-api.Tests/RunsControllerTests.cs

125 lines
5.7 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using w4c_workflows.Controllers;
using w4c_workflows.Data;
using w4c_workflows.Models;
using Xunit;
namespace w4c_workflows.Tests;
[Collection("WorkflowsPostgres")]
public class RunsControllerTests
{
private readonly WorkflowsPostgresFixture _fixture;
public RunsControllerTests(WorkflowsPostgresFixture fixture)
{
_fixture = fixture;
}
private static RunsController Controller(WorkflowsDbContext db, string tenantId)
{
var controller = new RunsController(db)
{
ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() },
};
controller.HttpContext.Items["TenantId"] = tenantId;
return controller;
}
private static string Tenant() => "t" + Guid.NewGuid().ToString("N")[..12];
[Fact]
public async Task List_returns_runs_newest_first_scoped_to_tenant()
{
var tenantId = Tenant();
await using var db = _fixture.CreateContext();
var workflow = WorkflowDataHelpers.CompileAndSave(db, tenantId, WorkflowDataHelpers.LinearYaml);
var older = WorkflowDataHelpers.AddRun(db, workflow, RunStatus.Succeeded,
startedAt: DateTime.UtcNow.AddMinutes(-10), finishedAt: DateTime.UtcNow.AddMinutes(-9));
var newer = WorkflowDataHelpers.AddRun(db, workflow, RunStatus.Failed,
startedAt: DateTime.UtcNow.AddMinutes(-1), finishedAt: DateTime.UtcNow, error: "boom");
// A second tenant's runs must never leak into the response.
var other = Tenant();
var otherWorkflow = WorkflowDataHelpers.CompileAndSave(db, other, WorkflowDataHelpers.LinearYaml);
WorkflowDataHelpers.AddRun(db, otherWorkflow, RunStatus.Running, startedAt: DateTime.UtcNow);
var result = Assert.IsType<OkObjectResult>(await Controller(db, tenantId).List(null, 50, 0, default));
var body = Assert.IsType<RunsController.RunListResponse>(result.Value);
Assert.Equal(2, body.Total);
Assert.Equal(new[] { newer.Id, older.Id }, body.Runs.Select(r => r.Id).ToArray());
Assert.Equal("linear", body.Runs[0].WorkflowName);
Assert.Equal("boom", body.Runs[0].Error);
}
[Fact]
public async Task List_filters_by_workflow()
{
var tenantId = Tenant();
await using var db = _fixture.CreateContext();
var wf1 = WorkflowDataHelpers.CompileAndSave(db, tenantId, WorkflowDataHelpers.LinearYaml, "workflows/a.yaml");
var wf2 = WorkflowDataHelpers.CompileAndSave(db, tenantId, WorkflowDataHelpers.DurableYaml, "workflows/b.yaml");
WorkflowDataHelpers.AddRun(db, wf1, RunStatus.Succeeded, DateTime.UtcNow);
WorkflowDataHelpers.AddRun(db, wf2, RunStatus.Failed, DateTime.UtcNow);
var result = Assert.IsType<OkObjectResult>(await Controller(db, tenantId).List(wf1.Id, 50, 0, default));
var body = Assert.IsType<RunsController.RunListResponse>(result.Value);
Assert.Equal(1, body.Total);
Assert.Equal(wf1.Id, body.Runs[0].WorkflowId);
}
[Fact]
public async Task Get_returns_detail_and_404s_for_foreign_run()
{
var tenantId = Tenant();
await using var db = _fixture.CreateContext();
var workflow = WorkflowDataHelpers.CompileAndSave(db, tenantId, WorkflowDataHelpers.LinearYaml);
var run = WorkflowDataHelpers.AddRun(db, workflow, RunStatus.Succeeded,
DateTime.UtcNow.AddMinutes(-1), DateTime.UtcNow, error: null);
var controller = Controller(db, tenantId);
var result = Assert.IsType<OkObjectResult>(await controller.Get(run.Id, default));
var detail = Assert.IsType<RunsController.RunDetail>(result.Value);
Assert.Equal(run.Id, detail.Id);
Assert.Equal(workflow.Id, detail.WorkflowId);
Assert.Equal("linear", detail.WorkflowName);
// A run owned by another tenant is not visible.
var other = Tenant();
var otherWorkflow = WorkflowDataHelpers.CompileAndSave(db, other, WorkflowDataHelpers.LinearYaml);
var foreignRun = WorkflowDataHelpers.AddRun(db, otherWorkflow, RunStatus.Succeeded, DateTime.UtcNow);
Assert.IsType<NotFoundObjectResult>(await controller.Get(foreignRun.Id, default));
}
[Fact]
public async Task Tasks_returns_task_runs_in_dispatch_order()
{
var tenantId = Tenant();
await using var db = _fixture.CreateContext();
var workflow = WorkflowDataHelpers.CompileAndSave(db, tenantId, WorkflowDataHelpers.LinearYaml);
var run = WorkflowDataHelpers.AddRun(db, workflow, RunStatus.Running, DateTime.UtcNow);
var root = WorkflowDataHelpers.TaskByKey(workflow, "root");
var a = WorkflowDataHelpers.TaskByKey(workflow, "a");
db.TaskRuns.AddRange(
new TaskRun { Id = Guid.NewGuid(), RunId = run.Id, TaskId = root.Id, Attempt = 1, Status = TaskRunStatus.Succeeded, StartedAt = DateTime.UtcNow.AddMinutes(-2), FinishedAt = DateTime.UtcNow.AddMinutes(-1) },
new TaskRun { Id = Guid.NewGuid(), RunId = run.Id, TaskId = a.Id, Attempt = 1, Status = TaskRunStatus.Failed, Error = "boom", IsCompensation = true, StartedAt = DateTime.UtcNow.AddMinutes(-1), FinishedAt = DateTime.UtcNow });
db.SaveChanges();
var result = Assert.IsType<OkObjectResult>(await Controller(db, tenantId).Tasks(run.Id, default));
var tasks = Assert.IsType<List<RunsController.TaskRunSummary>>(result.Value);
Assert.Equal(2, tasks.Count);
Assert.Equal("root", tasks[0].TaskKey);
Assert.Equal("a", tasks[1].TaskKey);
Assert.True(tasks[1].IsCompensation);
Assert.Equal("boom", tasks[1].Error);
}
}