feat(worker): build/test the merge preview, not just the post-merge merge
preview_merge could only see file-overlap cleanliness via git merge-tree, never whether the result compiles -- the two costliest findings of the 2026-08-06 batch run were both merge-tree-clean but build-broken. When a list has a verify command configured, a clean preview is now additionally materialized (via a commit-tree + detached scratch worktree, outside the real repo, always cleaned up) and built/tested there, without ever touching the real working tree. preview_merge always attempts a verify run when a command is configured; preview_merge_set only does when its new runVerify parameter is set (default off), so a set preview never starts N builds unasked. The post-merge verify gate is unchanged.
This commit is contained in:
@@ -10,6 +10,7 @@ using ClaudeDo.Worker.Planning;
|
||||
using ClaudeDo.Worker.Queue;
|
||||
using ClaudeDo.Worker.Runner;
|
||||
using ClaudeDo.Worker.Tests.Infrastructure;
|
||||
using ClaudeDo.Worker.Tests.Services;
|
||||
using ClaudeDo.Worker.Usage;
|
||||
using ClaudeDo.Worker.Worktrees;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
@@ -123,13 +124,15 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
return task;
|
||||
}
|
||||
|
||||
private ExternalMcpService BuildSut(QueueService queue)
|
||||
private ExternalMcpService BuildSut(QueueService queue) => BuildSut(queue, new VerifyCommandRunner());
|
||||
|
||||
private ExternalMcpService BuildSut(QueueService queue, IVerifyCommandRunner verify)
|
||||
{
|
||||
var git = new GitService();
|
||||
var factory = _db.CreateFactory();
|
||||
var maintenance = new WorktreeMaintenanceService(factory, git, NullLogger<WorktreeMaintenanceService>.Instance);
|
||||
var state = TaskStateServiceBuilder.Build(factory).State;
|
||||
var merge = new TaskMergeService(factory, git, _broadcaster, state, new VerifyCommandRunner(), NullLogger<TaskMergeService>.Instance);
|
||||
var merge = new TaskMergeService(factory, git, _broadcaster, state, verify, NullLogger<TaskMergeService>.Instance);
|
||||
var aggregator = new PlanningAggregator(factory, git, NullLogger<PlanningAggregator>.Instance);
|
||||
var planningMerge = new PlanningMergeOrchestrator(
|
||||
factory, merge, aggregator, _broadcaster, git, state, NullLogger<PlanningMergeOrchestrator>.Instance);
|
||||
@@ -1609,6 +1612,36 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
Assert.Contains("working directory", ex.Message, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMerge_VerifyCommandConfigured_RunsInScratchWorktreeAndDoesNotTouchTarget()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var (task, list, wt) = await SeedWorktreeAsync(TaskStatus.WaitingForReview);
|
||||
File.WriteAllText(Path.Combine(wt.WorktreePath, "added.txt"), "content");
|
||||
var cfg = new WorkerConfig { WorktreeRootStrategy = "sibling" };
|
||||
var mgr = new WorktreeManager(new GitService(), _db.CreateFactory(), cfg, NullLogger<WorktreeManager>.Instance);
|
||||
await mgr.CommitIfChangedAsync(wt, task, list, CancellationToken.None);
|
||||
|
||||
using (var ctx = _db.CreateContext())
|
||||
await new ListRepository(ctx).SetConfigAsync(new ListConfigEntity { ListId = list.Id, VerifyCommand = "dotnet build" });
|
||||
|
||||
var git = new GitService();
|
||||
var target = await git.GetCurrentBranchAsync(list.WorkingDir!, CancellationToken.None);
|
||||
var headBefore = await git.RevParseHeadAsync(list.WorkingDir!, CancellationToken.None);
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(0, false, "ok") };
|
||||
var sut = BuildSut(CreateQueue(), fakeVerify);
|
||||
|
||||
var result = await sut.PreviewMerge(task.Id, target, CancellationToken.None);
|
||||
|
||||
Assert.Equal(TaskMergeService.PreviewClean, result.Status);
|
||||
Assert.Equal(0, result.VerifyExitCode);
|
||||
Assert.Equal("dotnet build", fakeVerify.CapturedCommand);
|
||||
Assert.NotEqual(list.WorkingDir, fakeVerify.CapturedWorkingDir);
|
||||
Assert.Equal(headBefore, await git.RevParseHeadAsync(list.WorkingDir!, CancellationToken.None));
|
||||
Assert.False(await git.HasChangesAsync(list.WorkingDir!, CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMergeSet_TwoTasksSharedFile_ReportsOverlap()
|
||||
{
|
||||
@@ -1627,7 +1660,7 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
await CreateActiveWorktreeAsync(repo, taskB.Id, "shared.txt", "b change\n");
|
||||
|
||||
var sut = BuildSut(CreateQueue());
|
||||
var result = await sut.PreviewMergeSet([taskA.Id, taskB.Id], "main", CancellationToken.None);
|
||||
var result = await sut.PreviewMergeSet([taskA.Id, taskB.Id], "main", cancellationToken: CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, result.Tasks.Count);
|
||||
Assert.All(result.Tasks, t => Assert.Null(t.Error));
|
||||
@@ -1648,7 +1681,7 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
var target = await new GitService().GetCurrentBranchAsync(list.WorkingDir!, CancellationToken.None);
|
||||
var sut = BuildSut(CreateQueue());
|
||||
|
||||
var result = await sut.PreviewMergeSet([task.Id], target, CancellationToken.None);
|
||||
var result = await sut.PreviewMergeSet([task.Id], target, cancellationToken: CancellationToken.None);
|
||||
|
||||
Assert.Single(result.Tasks);
|
||||
Assert.Empty(result.Overlaps);
|
||||
@@ -1665,7 +1698,7 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
var target = await new GitService().GetCurrentBranchAsync(list.WorkingDir!, CancellationToken.None);
|
||||
var sut = BuildSut(CreateQueue());
|
||||
|
||||
var result = await sut.PreviewMergeSet([task.Id, noWorktreeTask.Id], target, CancellationToken.None);
|
||||
var result = await sut.PreviewMergeSet([task.Id, noWorktreeTask.Id], target, cancellationToken: CancellationToken.None);
|
||||
|
||||
Assert.Equal(2, result.Tasks.Count);
|
||||
var ok = result.Tasks.Single(t => t.TaskId == task.Id);
|
||||
@@ -1710,7 +1743,7 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
await CreateActiveWorktreeAsync(repo, fullTask.Id, "full.txt", "content\n");
|
||||
|
||||
var sut = BuildSut(CreateQueue());
|
||||
var result = await sut.PreviewMergeSet([emptyTask.Id, fullTask.Id], "main", CancellationToken.None);
|
||||
var result = await sut.PreviewMergeSet([emptyTask.Id, fullTask.Id], "main", cancellationToken: CancellationToken.None);
|
||||
|
||||
var emptyEntry = result.Tasks.Single(t => t.TaskId == emptyTask.Id);
|
||||
var fullEntry = result.Tasks.Single(t => t.TaskId == fullTask.Id);
|
||||
@@ -1720,6 +1753,54 @@ public sealed class ExternalMcpServiceTests : IDisposable
|
||||
Assert.False(fullEntry.IsEmpty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMergeSet_DefaultRunVerifyFalse_NeverInvokesRunnerEvenWithCommandConfigured()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = new GitRepoFixture();
|
||||
_repos.Add(repo);
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "branch", "-m", "main");
|
||||
var listId = Guid.NewGuid().ToString();
|
||||
await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", WorkingDir = repo.RepoDir, CreatedAt = DateTime.UtcNow });
|
||||
using (var ctx = _db.CreateContext())
|
||||
await new ListRepository(ctx).SetConfigAsync(new ListConfigEntity { ListId = listId, VerifyCommand = "dotnet build" });
|
||||
var task = await SeedTaskAsync(listId, "a", TaskStatus.WaitingForReview);
|
||||
await CreateActiveWorktreeAsync(repo, task.Id, "added.txt", "content\n");
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(1, false, "should never run") };
|
||||
var sut = BuildSut(CreateQueue(), fakeVerify);
|
||||
|
||||
var result = await sut.PreviewMergeSet([task.Id], "main", cancellationToken: CancellationToken.None);
|
||||
|
||||
Assert.Null(result.Tasks.Single().VerifyExitCode);
|
||||
Assert.Null(fakeVerify.CapturedCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMergeSet_RunVerifyTrue_InvokesRunnerPerTask()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = new GitRepoFixture();
|
||||
_repos.Add(repo);
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "branch", "-m", "main");
|
||||
var listId = Guid.NewGuid().ToString();
|
||||
await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", WorkingDir = repo.RepoDir, CreatedAt = DateTime.UtcNow });
|
||||
using (var ctx = _db.CreateContext())
|
||||
await new ListRepository(ctx).SetConfigAsync(new ListConfigEntity { ListId = listId, VerifyCommand = "dotnet build" });
|
||||
var task = await SeedTaskAsync(listId, "a", TaskStatus.WaitingForReview);
|
||||
await CreateActiveWorktreeAsync(repo, task.Id, "added.txt", "content\n");
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(0, false, "ok") };
|
||||
var sut = BuildSut(CreateQueue(), fakeVerify);
|
||||
|
||||
var result = await sut.PreviewMergeSet([task.Id], "main", runVerify: true, cancellationToken: CancellationToken.None);
|
||||
|
||||
Assert.Equal(0, result.Tasks.Single().VerifyExitCode);
|
||||
Assert.Equal("dotnet build", fakeVerify.CapturedCommand);
|
||||
}
|
||||
|
||||
// ── AddTask model override ────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -61,4 +61,43 @@ public class GitServicePreviewMergeTests : IDisposable
|
||||
Assert.Equal(headBefore, GitRepoFixture.RunGit(repo.RepoDir, "rev-parse", "HEAD").Trim());
|
||||
Assert.False(await git.IsMidMergeAsync(repo.RepoDir));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMergeAsync_Clean_ReturnsTreeOidMaterializableIntoDetachedWorktree()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var git = new GitService();
|
||||
var baseBranch = await git.GetCurrentBranchAsync(repo.RepoDir);
|
||||
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "checkout", "-b", "feature");
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "newfile.txt"), "x\n");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "add", "-A");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "commit", "-m", "feat");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "checkout", baseBranch);
|
||||
|
||||
var preview = await git.PreviewMergeAsync(repo.RepoDir, baseBranch, "feature", CancellationToken.None);
|
||||
Assert.True(preview.Clean);
|
||||
Assert.False(string.IsNullOrWhiteSpace(preview.TreeOid));
|
||||
|
||||
// The tree a clean preview would produce can be wrapped in a commit and checked out into
|
||||
// a detached, branchless scratch worktree -- the technique the verify-in-preview feature
|
||||
// relies on -- without ever touching the real working tree.
|
||||
var parentSha = await git.RevParseAsync(repo.RepoDir, baseBranch, CancellationToken.None);
|
||||
var commitSha = await git.CommitTreeAsync(repo.RepoDir, preview.TreeOid!, parentSha, "scratch", CancellationToken.None);
|
||||
|
||||
var scratchPath = Path.Combine(Path.GetTempPath(), $"scratch_{Guid.NewGuid():N}");
|
||||
await git.WorktreeAddDetachedAsync(repo.RepoDir, scratchPath, commitSha, CancellationToken.None);
|
||||
try
|
||||
{
|
||||
Assert.True(File.Exists(Path.Combine(scratchPath, "newfile.txt")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await git.WorktreeRemoveAsync(repo.RepoDir, scratchPath, force: true, CancellationToken.None);
|
||||
}
|
||||
|
||||
Assert.False(Directory.Exists(scratchPath));
|
||||
Assert.False(await git.HasChangesAsync(repo.RepoDir, CancellationToken.None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,6 +589,154 @@ public class TaskMergeServiceTests : IDisposable
|
||||
Assert.Equal(TaskMergeService.PreviewUnavailable, preview.Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_RunVerifyTrue_NoVerifyCommandConfigured_NeverInvokesRunner()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var db = NewDb();
|
||||
var (list, task) = await SeedListAndTask(db, repo.RepoDir, TaskStatus.WaitingForReview);
|
||||
|
||||
var wtMgr = BuildWorktreeManager(db);
|
||||
var wtCtx = await wtMgr.CreateAsync(task, list, CancellationToken.None);
|
||||
_wtCleanups.Add((repo.RepoDir, wtCtx.WorktreePath));
|
||||
File.WriteAllText(Path.Combine(wtCtx.WorktreePath, "added.txt"), "x\n");
|
||||
await wtMgr.CommitIfChangedAsync(wtCtx, task, list, CancellationToken.None);
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(1, false, "should never run") };
|
||||
var (svc, _) = BuildService(db, fakeVerify);
|
||||
var target = await new GitService().GetCurrentBranchAsync(repo.RepoDir);
|
||||
|
||||
var preview = await svc.PreviewAsync(task.Id, target, runVerify: true, CancellationToken.None);
|
||||
|
||||
Assert.Equal(TaskMergeService.PreviewClean, preview.Status);
|
||||
Assert.Null(preview.VerifyExitCode);
|
||||
Assert.Null(fakeVerify.CapturedCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_RunVerifyFalse_VerifyCommandConfigured_NeverInvokesRunner()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var db = NewDb();
|
||||
var (list, task) = await SeedListAndTask(db, repo.RepoDir, TaskStatus.WaitingForReview);
|
||||
await SeedVerifyCommand(db, list.Id, "dotnet build");
|
||||
|
||||
var wtMgr = BuildWorktreeManager(db);
|
||||
var wtCtx = await wtMgr.CreateAsync(task, list, CancellationToken.None);
|
||||
_wtCleanups.Add((repo.RepoDir, wtCtx.WorktreePath));
|
||||
File.WriteAllText(Path.Combine(wtCtx.WorktreePath, "added.txt"), "x\n");
|
||||
await wtMgr.CommitIfChangedAsync(wtCtx, task, list, CancellationToken.None);
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(1, false, "should never run") };
|
||||
var (svc, _) = BuildService(db, fakeVerify);
|
||||
var target = await new GitService().GetCurrentBranchAsync(repo.RepoDir);
|
||||
|
||||
var preview = await svc.PreviewAsync(task.Id, target, CancellationToken.None);
|
||||
|
||||
Assert.Equal(TaskMergeService.PreviewClean, preview.Status);
|
||||
Assert.Null(preview.VerifyExitCode);
|
||||
Assert.Null(fakeVerify.CapturedCommand);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_RunVerifyTrue_VerifyPasses_ReturnsCleanBuildAndLeavesTargetUntouched()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var db = NewDb();
|
||||
var (list, task) = await SeedListAndTask(db, repo.RepoDir, TaskStatus.WaitingForReview);
|
||||
await SeedVerifyCommand(db, list.Id, "dotnet build");
|
||||
|
||||
var wtMgr = BuildWorktreeManager(db);
|
||||
var wtCtx = await wtMgr.CreateAsync(task, list, CancellationToken.None);
|
||||
_wtCleanups.Add((repo.RepoDir, wtCtx.WorktreePath));
|
||||
File.WriteAllText(Path.Combine(wtCtx.WorktreePath, "added.txt"), "x\n");
|
||||
await wtMgr.CommitIfChangedAsync(wtCtx, task, list, CancellationToken.None);
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner
|
||||
{
|
||||
Result = new VerifyCommandResult(0, false, "build ok"),
|
||||
FileToCheck = "added.txt",
|
||||
};
|
||||
var (svc, _) = BuildService(db, fakeVerify);
|
||||
var git = new GitService();
|
||||
var target = await git.GetCurrentBranchAsync(repo.RepoDir);
|
||||
var statusBefore = await git.GetStatusPorcelainAsync(repo.RepoDir);
|
||||
var headBefore = await git.RevParseHeadAsync(repo.RepoDir);
|
||||
|
||||
var preview = await svc.PreviewAsync(task.Id, target, runVerify: true, CancellationToken.None);
|
||||
|
||||
Assert.Equal(TaskMergeService.PreviewClean, preview.Status);
|
||||
Assert.Equal(0, preview.VerifyExitCode);
|
||||
Assert.NotNull(preview.VerifyDurationMs);
|
||||
Assert.Equal("dotnet build", fakeVerify.CapturedCommand);
|
||||
Assert.NotEqual(repo.RepoDir, fakeVerify.CapturedWorkingDir);
|
||||
Assert.True(fakeVerify.WorkingDirHadFile, "the merged file must be present in the scratch worktree at verify time");
|
||||
|
||||
// Scratch worktree removed afterward; the real target working tree never changed.
|
||||
Assert.False(Directory.Exists(fakeVerify.CapturedWorkingDir));
|
||||
Assert.Equal(statusBefore, await git.GetStatusPorcelainAsync(repo.RepoDir));
|
||||
Assert.Equal(headBefore, await git.RevParseHeadAsync(repo.RepoDir));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_RunVerifyTrue_VerifyFails_ReturnsExitCodeAndOutputTailWithoutTouchingTarget()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var db = NewDb();
|
||||
var (list, task) = await SeedListAndTask(db, repo.RepoDir, TaskStatus.WaitingForReview);
|
||||
await SeedVerifyCommand(db, list.Id, "dotnet build");
|
||||
|
||||
var wtMgr = BuildWorktreeManager(db);
|
||||
var wtCtx = await wtMgr.CreateAsync(task, list, CancellationToken.None);
|
||||
_wtCleanups.Add((repo.RepoDir, wtCtx.WorktreePath));
|
||||
File.WriteAllText(Path.Combine(wtCtx.WorktreePath, "added.txt"), "x\n");
|
||||
await wtMgr.CommitIfChangedAsync(wtCtx, task, list, CancellationToken.None);
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(1, false, "build broke: CS0103") };
|
||||
var (svc, _) = BuildService(db, fakeVerify);
|
||||
var git = new GitService();
|
||||
var target = await git.GetCurrentBranchAsync(repo.RepoDir);
|
||||
var headBefore = await git.RevParseHeadAsync(repo.RepoDir);
|
||||
|
||||
var preview = await svc.PreviewAsync(task.Id, target, runVerify: true, CancellationToken.None);
|
||||
|
||||
// The merge-tree result itself is still clean -- only the build/test of it failed.
|
||||
Assert.Equal(TaskMergeService.PreviewClean, preview.Status);
|
||||
Assert.Equal(1, preview.VerifyExitCode);
|
||||
Assert.Contains("build broke: CS0103", preview.VerifyOutputTail);
|
||||
Assert.False(Directory.Exists(fakeVerify.CapturedWorkingDir));
|
||||
Assert.Equal(headBefore, await git.RevParseHeadAsync(repo.RepoDir));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewAsync_RunVerifyTrue_CleansUpScratchWorktreeRegistration()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var db = NewDb();
|
||||
var (list, task) = await SeedListAndTask(db, repo.RepoDir, TaskStatus.WaitingForReview);
|
||||
await SeedVerifyCommand(db, list.Id, "dotnet build");
|
||||
|
||||
var wtMgr = BuildWorktreeManager(db);
|
||||
var wtCtx = await wtMgr.CreateAsync(task, list, CancellationToken.None);
|
||||
_wtCleanups.Add((repo.RepoDir, wtCtx.WorktreePath));
|
||||
File.WriteAllText(Path.Combine(wtCtx.WorktreePath, "added.txt"), "x\n");
|
||||
await wtMgr.CommitIfChangedAsync(wtCtx, task, list, CancellationToken.None);
|
||||
|
||||
var fakeVerify = new FakeVerifyCommandRunner { Result = new VerifyCommandResult(0, false, "ok") };
|
||||
var (svc, _) = BuildService(db, fakeVerify);
|
||||
var target = await new GitService().GetCurrentBranchAsync(repo.RepoDir);
|
||||
|
||||
await svc.PreviewAsync(task.Id, target, runVerify: true, CancellationToken.None);
|
||||
|
||||
var worktreeListing = GitRepoFixture.RunGit(repo.RepoDir, "worktree", "list", "--porcelain");
|
||||
Assert.DoesNotContain(fakeVerify.CapturedWorkingDir!, worktreeListing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApproveAndMergeAsync_CleanWorktree_MergesAndMarksDone()
|
||||
{
|
||||
@@ -1159,10 +1307,17 @@ internal sealed class FakeVerifyCommandRunner : IVerifyCommandRunner
|
||||
public string? CapturedWorkingDir { get; private set; }
|
||||
public string? CapturedCommand { get; private set; }
|
||||
|
||||
// Set before the call to snapshot, at call time, whether this file exists in workingDir --
|
||||
// lets a preview-verify test prove the scratch worktree actually contains the merged content.
|
||||
public string? FileToCheck { get; set; }
|
||||
public bool? WorkingDirHadFile { get; private set; }
|
||||
|
||||
public Task<VerifyCommandResult> RunAsync(string workingDir, string command, TimeSpan timeout, CancellationToken ct)
|
||||
{
|
||||
CapturedWorkingDir = workingDir;
|
||||
CapturedCommand = command;
|
||||
if (FileToCheck is not null)
|
||||
WorkingDirHadFile = File.Exists(Path.Combine(workingDir, FileToCheck));
|
||||
return Task.FromResult(Result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user