fix: restore green test suite across all projects

* TaskRepository.UpdateAsync defensively detaches any locally tracked
  entity with the same Id before attaching the patched copy, preventing
  EF identity conflicts when callers load via AsNoTracking and write
  back through the same DbContext (surfaced by ExternalMcpService
  UpdateTask integration tests).
* TasksIslandViewModel auto-collapse now only fires for Finalized
  planning parents that are not yet Done. Active-phase parents stay
  expanded while the user is editing the plan, and Done parents stay
  expanded so all completed children land in CompletedItems alongside
  the parent.
* Update three Ui.Tests fakes (ConflictResolution, PlanningDiff,
  DetailsIslandPlanning) to implement the two new IWorkerClient
  members (OpenInteractiveTerminalAsync, QueuePlanningSubtasksAsync).
* Rewrite StreamLineFormatterTests to exercise the current
  assistant/user/result/system message format instead of the legacy
  stream_event parsing that was removed in the formatter rewrite.
* Align AppSettingsRepository seed-default assertion with the
  permission-mode default that flipped from bypassPermissions to auto.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mika Kuns
2026-04-28 08:30:26 +02:00
parent dc3fc443b4
commit 8eafa71ed3
7 changed files with 35 additions and 24 deletions

View File

@@ -6,38 +6,38 @@ public class StreamLineFormatterTests
{
private readonly StreamLineFormatter _formatter = new();
// --- Text deltas ---
// --- Assistant text blocks ---
[Fact]
public void FormatLine_TextDelta_ReturnsTextContent()
public void FormatLine_AssistantTextBlock_ReturnsTextContent()
{
var line = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello world"}}}""";
Assert.Equal("Hello world", _formatter.FormatLine(line));
var line = """{"type":"assistant","message":{"content":[{"type":"text","text":"Hello world"}]}}""";
Assert.Equal("Hello world\n", _formatter.FormatLine(line));
}
[Fact]
public void FormatLine_ConsecutiveTextDeltas_ReturnEachDelta()
public void FormatLine_AssistantConsecutiveTextBlocks_ReturnEachAppended()
{
var line1 = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello "}}}""";
var line2 = """{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"world"}}}""";
Assert.Equal("Hello ", _formatter.FormatLine(line1));
Assert.Equal("world", _formatter.FormatLine(line2));
var line1 = """{"type":"assistant","message":{"content":[{"type":"text","text":"Hello "}]}}""";
var line2 = """{"type":"assistant","message":{"content":[{"type":"text","text":"world"}]}}""";
Assert.Equal("Hello \n", _formatter.FormatLine(line1));
Assert.Equal("world\n", _formatter.FormatLine(line2));
}
[Fact]
public void FormatLine_ContentBlockStop_ReturnsNewline()
public void FormatLine_AssistantThinkingBlock_IsFiltered()
{
var line = """{"type":"stream_event","event":{"type":"content_block_stop","index":0}}""";
Assert.Equal("\n", _formatter.FormatLine(line));
var line = """{"type":"assistant","message":{"content":[{"type":"thinking","text":"hidden"}]}}""";
Assert.Null(_formatter.FormatLine(line));
}
// --- Tool use, result, system, fallback ---
[Fact]
public void FormatLine_ToolUseStart_ReturnsToolNameLine()
public void FormatLine_AssistantToolUseBlock_ReturnsToolNameLine()
{
var line = """{"type":"stream_event","event":{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"x","name":"bash"}}}""";
Assert.Equal("\n[Tool: bash]\n", _formatter.FormatLine(line));
var line = """{"type":"assistant","message":{"content":[{"type":"tool_use","id":"x","name":"Bash","input":{"command":"ls"}}]}}""";
Assert.Equal("[Bash] $ ls\n", _formatter.FormatLine(line));
}
[Fact]
@@ -58,13 +58,13 @@ public class StreamLineFormatterTests
public void FormatLine_ApiRetry_ReturnsRetryNotice()
{
var line = """{"type":"system","subtype":"api_retry"}""";
Assert.Equal("\n[Retrying API call...]\n", _formatter.FormatLine(line));
Assert.Equal("[Retrying API call...]\n", _formatter.FormatLine(line));
}
[Fact]
public void FormatLine_SystemNonRetry_ReturnsNull()
public void FormatLine_SystemUnknownSubtype_ReturnsNull()
{
var line = """{"type":"system","subtype":"init"}""";
var line = """{"type":"system","subtype":"some_unknown_subtype"}""";
Assert.Null(_formatter.FormatLine(line));
}
@@ -98,8 +98,8 @@ public class StreamLineFormatterTests
{
var lines = new[]
{
"""{"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}}""",
"""{"type":"stream_event","event":{"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"x","name":"bash"}}}""",
"""{"type":"assistant","message":{"content":[{"type":"text","text":"Hello"}]}}""",
"""{"type":"assistant","message":{"content":[{"type":"tool_use","id":"x","name":"Bash","input":{"command":"ls"}}]}}""",
"""{"type":"result","result":"Done."}""",
};
var file = Path.GetTempFileName();
@@ -108,7 +108,7 @@ public class StreamLineFormatterTests
File.WriteAllLines(file, lines);
var result = _formatter.FormatFile(file);
Assert.Contains("Hello", result);
Assert.Contains("[Tool: bash]", result);
Assert.Contains("[Bash]", result);
Assert.Contains("Done.", result);
}
finally
@@ -121,7 +121,7 @@ public class StreamLineFormatterTests
public void FormatFile_TrimsLargeContent()
{
var chunk = new string('x', 1000);
var line = "{\"type\":\"stream_event\",\"event\":{\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"" + chunk + "\"}}}";
var line = "{\"type\":\"assistant\",\"message\":{\"content\":[{\"type\":\"text\",\"text\":\"" + chunk + "\"}]}}";
var lines = Enumerable.Repeat(line, 65).ToArray();
var file = Path.GetTempFileName();
try