Die zufaelligen roten Laeufe der vollen Suite (jedes Mal ein anderer Test, zuletzt PromptHistoryTests mit einer reinen Funktion) hatten eine Ursache: Zwanzig Testklassen riefen beim Aufraeumen SqliteConnection.ClearAllPools() auf. Das ist prozessweit und nahm einem parallel laufenden Test die Verbindung mitten im Oeffnen weg - ObjectDisposedException in ApplyPragmas. - SqliteStorage.ReleasePooledConnections(pfad) leert nur die Pools der Datenbanken unter einer Datei oder einem Verzeichnis. - BackupService oeffnet die Quelle fuer VACUUM INTO ohne Pool: eine einmalige Kopie soll die Datei danach nicht offen halten. - PoolReleaseTests: die freigegebene Datenbank laesst sich loeschen, die andere arbeitet weiter. Vier volle Laeufe hintereinander gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
3.9 KiB
C#
99 lines
3.9 KiB
C#
using System.Text.Json;
|
|
using ClawdDotNet.Core.Config;
|
|
using ClawdDotNet.Core.Organisation;
|
|
using ClawdDotNet.Core.Storage;
|
|
using ClawdDotNet.Core.Tasks;
|
|
using ClawdDotNet.Core.Tools;
|
|
using ClawdDotNet.Tools.Taskboard;
|
|
using ClawdDotNet.Tools.Tests.FileRW;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Shouldly;
|
|
|
|
namespace ClawdDotNet.Tools.Tests.Taskboard;
|
|
|
|
/// <summary>
|
|
/// #683: A task for another agent is a way of reaching it. The address book that holds for
|
|
/// messages holds here too — otherwise it could be walked around with task_create.
|
|
/// </summary>
|
|
public sealed class AssignmentReachTests : IDisposable
|
|
{
|
|
private readonly string _directory;
|
|
private readonly SqliteStorage _storage;
|
|
private readonly SqliteTaskRepository _repo;
|
|
private readonly TaskboardTool _tool = new();
|
|
|
|
private readonly OrgChart _org = new(
|
|
[
|
|
new UnitConfig { Id = "amazon", Name = "Amazon", LeaderAgentId = "amazon-lead" },
|
|
new UnitConfig { Id = "accounting", Name = "Accounting", LeaderAgentId = "accounting-lead" }
|
|
],
|
|
[
|
|
new AgentConfig { AgentId = "amazon-lead", UnitId = "amazon" },
|
|
new AgentConfig { AgentId = "ads", UnitId = "amazon" },
|
|
new AgentConfig { AgentId = "accounting-lead", UnitId = "accounting" },
|
|
new AgentConfig { AgentId = "bookkeeper", UnitId = "accounting" }
|
|
]);
|
|
|
|
public AssignmentReachTests()
|
|
{
|
|
_directory = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N"));
|
|
_storage = new SqliteStorage(Path.Combine(_directory, "state.db"));
|
|
_repo = new SqliteTaskRepository(_storage);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ClawdDotNet.Core.Storage.SqliteStorage.ReleasePooledConnections(_directory);
|
|
try { Directory.Delete(_directory, recursive: true); }
|
|
catch { /* cleanup is secondary */ }
|
|
}
|
|
|
|
private Task<ToolResult> Run(string agentId, string json)
|
|
{
|
|
var context = new AgentToolContext(
|
|
agentId, "inst", new Dictionary<string, object?>(), new NullStateStore(), NullLogger.Instance,
|
|
CancellationToken.None, SharedWorkspacePath: Path.Combine(_directory, "SharedWorkspace"), Tasks: _repo)
|
|
{
|
|
Organisation = _org
|
|
};
|
|
return _tool.ExecuteAsync(JsonDocument.Parse(json).RootElement, context, CancellationToken.None);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_task_for_an_agent_outside_the_address_book_is_refused()
|
|
{
|
|
var result = await Run("ads", """{"action":"task_create","title":"Rechnung buchen","assignee":"@bookkeeper"}""");
|
|
|
|
result.Success.ShouldBeFalse();
|
|
result.ErrorMessage.ShouldNotBeNull().ShouldContain("not in your address book");
|
|
result.ErrorMessage.ShouldContain("amazon-lead");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_fresh_run_of_an_agent_outside_is_refused_too()
|
|
{
|
|
var result = await Run("ads", """{"action":"task_create","title":"Rechnung buchen","assignee":"@new:bookkeeper"}""");
|
|
|
|
result.Success.ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task A_task_for_the_own_lead_or_the_human_goes_through()
|
|
{
|
|
(await Run("ads", """{"action":"task_create","title":"Frage an die Leitung","assignee":"@amazon-lead"}""")).Success.ShouldBeTrue();
|
|
(await Run("ads", """{"action":"task_create","title":"Frage an den Menschen","assignee":"@human"}""")).Success.ShouldBeTrue();
|
|
(await Run("ads", """{"action":"task_create","title":"Eigene Aufgabe","assignee":"@ads"}""")).Success.ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Reassigning_is_held_to_the_same_rule()
|
|
{
|
|
var created = await Run("ads", """{"action":"task_create","title":"Eigene Aufgabe","assignee":"@ads"}""");
|
|
var id = created.Content!.Split(' ', ':', '\n').First(w => w.StartsWith("t-"));
|
|
|
|
var result = await Run("ads", $$"""{"action":"task_update","id":"{{id}}","assignee":"@bookkeeper"}""");
|
|
|
|
result.Success.ShouldBeFalse();
|
|
}
|
|
}
|