w4c-workflows-api/w4c-workflows-api.Tests/TaskDispatcherWorkingDirTests.cs
Vitali sharp8n 42ffcb9adc workflows
2026-09-13 19:28:47 +03:00

95 lines
3.9 KiB
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using w4c_workflows.Services;
using w4c_workflows.Services.Runs;
using Xunit;
namespace w4c_workflows.Tests;
/// <summary>
/// Regression: the run's working directory must be resolved from the tenant's OWN
/// source directory (the same one the workflow-file writer uses), never the
/// global/git root. Before the fix, <see cref="TaskDispatcher"/> fell back to the
/// git root when not in Forgejo-backed mode, so a freshly created workflow's entry
/// file (e.g. <c>Main.cs</c>) was not found at run time ("entry file not found:
/// Main.cs").
/// </summary>
public class TaskDispatcherWorkingDirTests
{
private static WorkflowSourceFactory NewFactory(string copiesRoot)
{
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["WorkflowSource:CopiesRoot"] = copiesRoot,
})
.Build();
return new(
config,
env: null!, // unused by the factory, keep the test free of ASP.NET types
NullLoggerFactory.Instance,
new GitRunner(config, NullLogger<GitRunner>.Instance),
httpFactory: null!); // Forgejo is not configured, so no client is used
}
private static TaskDispatcher NewDispatcher(string copiesRoot) =>
new(
db: null!, // not used by ResolveWorkingDirAsync
jobs: null!, // not used by ResolveWorkingDirAsync
config: new ConfigurationBuilder().Build(),
logger: NullLogger<TaskDispatcher>.Instance,
sourceFactory: NewFactory(copiesRoot));
[Fact]
public async Task ResolveWorkingDirAsync_uses_tenant_copies_root_not_git_root()
{
using var tmp = new TempDir();
var dispatcher = NewDispatcher(tmp.Path);
// A workflow definition stored under the tenant's own dir, referenced as a
// repo-relative path with the shared "workflows" prefix.
var workingDir = await dispatcher.ResolveWorkingDirAsync(
"tenant-1", "workflows/test/workflow.yaml", default);
// The run must resolve inside the tenant's CopiesRoot directory, i.e. exactly
// where WorkflowFilesController.SaveFile writes the code files.
var expected = Path.Combine(tmp.Path, "tenant-1", "workflows", "test");
Assert.Equal(Path.GetFullPath(expected), Path.GetFullPath(workingDir));
// And the entry file must actually exist there — proving "entry file not
// found: Main.cs" can no longer happen for a correctly scaffolded workflow.
Directory.CreateDirectory(expected);
var entry = Path.Combine(expected, "Main.cs");
await File.WriteAllTextAsync(entry, "public static class Program { }");
Assert.True(File.Exists(entry));
}
[Fact]
public async Task ResolveWorkingDirAsync_other_tenant_uses_its_own_copies_root()
{
using var tmp = new TempDir();
var dispatcher = NewDispatcher(tmp.Path);
var workingDir = await dispatcher.ResolveWorkingDirAsync(
"tenant-2", "workflows/ship/workflow.yaml", default);
var expected = Path.Combine(tmp.Path, "tenant-2", "workflows", "ship");
Assert.Equal(Path.GetFullPath(expected), Path.GetFullPath(workingDir));
}
[Fact]
public async Task ResolveWorkingDirAsync_without_factory_falls_back_to_global_root()
{
var dispatcher = new TaskDispatcher(
db: null!,
jobs: null!,
config: new ConfigurationBuilder().Build(),
logger: NullLogger<TaskDispatcher>.Instance);
// No source factory configured: must not throw, returns a deterministic path.
var workingDir = await dispatcher.ResolveWorkingDirAsync(
"t-1", "workflows/a/workflow.yaml", default);
Assert.False(string.IsNullOrWhiteSpace(workingDir));
}
}