110 lines
4 KiB
C#
110 lines
4 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using w4c_workflows.Data;
|
|
using w4c_workflows.Models;
|
|
using w4c_workflows.Services;
|
|
using w4c_workflows.Services.Quota;
|
|
using w4c_workflows.Services.Runs;
|
|
using Xunit;
|
|
|
|
namespace w4c_workflows.Tests;
|
|
|
|
[Collection("WorkflowsPostgres")]
|
|
public class RunLauncherTests
|
|
{
|
|
private const string CronYaml = """
|
|
name: nightly
|
|
mode: function
|
|
language: shell
|
|
trigger:
|
|
type: cron
|
|
cron: "0 9 * * *"
|
|
entry: { file: nightly.sh }
|
|
""";
|
|
|
|
private readonly WorkflowsPostgresFixture _fixture;
|
|
|
|
public RunLauncherTests(WorkflowsPostgresFixture fixture)
|
|
{
|
|
_fixture = fixture;
|
|
}
|
|
|
|
private static Workflow CompileAndSave(WorkflowsDbContext db, string tenantId, string yaml)
|
|
{
|
|
var compiler = new WorkflowCompiler(new WorkflowValidator(new LanguageRegistry()));
|
|
var result = compiler.Compile(yaml, "workflows/nightly.yaml", tenantId);
|
|
Assert.True(result.Success, string.Join("\n", result.Errors));
|
|
|
|
db.Workflows.Add(result.Workflow!.Workflow);
|
|
db.Tasks.AddRange(result.Workflow.Tasks);
|
|
db.SaveChanges();
|
|
return result.Workflow.Workflow;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Launch_creates_a_pending_run_with_trigger_input_and_correlation()
|
|
{
|
|
var tenantId = "t" + Guid.NewGuid().ToString("N")[..12];
|
|
await using var db = _fixture.CreateContext();
|
|
var workflow = CompileAndSave(db, tenantId, CronYaml);
|
|
|
|
var launcher = QuotaTestData.Launcher(db);
|
|
var runId = await launcher.LaunchAsync(
|
|
new LaunchRequest(tenantId, workflow.Id, workflow.TriggerJson, "{\"x\":1}", "corr-1"), default);
|
|
|
|
var run = await db.WorkflowRuns.SingleAsync(r => r.Id == runId);
|
|
Assert.Equal(workflow.Id, run.WorkflowId);
|
|
Assert.Equal(tenantId, run.TenantId);
|
|
Assert.Equal(RunStatus.Pending, run.Status);
|
|
Assert.Equal(workflow.TriggerJson, run.TriggerJson);
|
|
Assert.Equal("{\"x\":1}", run.InputJson);
|
|
Assert.Equal("corr-1", run.CorrelationId);
|
|
Assert.Null(run.StartedAt);
|
|
Assert.Null(run.FinishedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Launch_rejects_unknown_workflow()
|
|
{
|
|
await using var db = _fixture.CreateContext();
|
|
var launcher = QuotaTestData.Launcher(db);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
launcher.LaunchAsync(new LaunchRequest("t", Guid.NewGuid(), null, null, null), default));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Launch_rejects_workflow_for_another_tenant()
|
|
{
|
|
var tenantId = "t" + Guid.NewGuid().ToString("N")[..12];
|
|
await using var db = _fixture.CreateContext();
|
|
var workflow = CompileAndSave(db, tenantId, CronYaml);
|
|
|
|
var launcher = QuotaTestData.Launcher(db);
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
launcher.LaunchAsync(new LaunchRequest("other-tenant", workflow.Id, null, null, null), default));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Launch_rejects_a_run_once_the_execution_quota_is_exhausted()
|
|
{
|
|
var tenantId = "t" + Guid.NewGuid().ToString("N")[..12];
|
|
await using var db = _fixture.CreateContext();
|
|
var workflow = CompileAndSave(db, tenantId, CronYaml);
|
|
|
|
var quota = QuotaTestData.Service(db, new WorkflowQuotaOptions { RunsPerMonth = 1 });
|
|
var launcher = QuotaTestData.Launcher(db, quota);
|
|
|
|
// The first execution fits in the quota…
|
|
await launcher.LaunchAsync(new LaunchRequest(tenantId, workflow.Id, null, null, "c1"), default);
|
|
|
|
// …the second is rejected with the snapshot so the API can return 429.
|
|
var ex = await Assert.ThrowsAsync<WorkflowQuotaExceededException>(() =>
|
|
launcher.LaunchAsync(new LaunchRequest(tenantId, workflow.Id, null, null, "c2"), default));
|
|
|
|
Assert.True(ex.Quota.Exceeded);
|
|
Assert.Equal(1, ex.Quota.Used);
|
|
Assert.Equal(1, ex.Quota.Limit);
|
|
}
|
|
}
|