Stufe 4c (#764): Bezeichner auf Englisch - Typen, Methoden, Felder, Parameter, Locals, Testnamen
Mit Roslyn umbenannt (Verweise folgen, Zeichenketten bleiben unberuehrt). Nicht umbenannt, weil gespeichert oder ueber die API gebunden (Stufe 4e): Properties, Record-Parameter, Enum-Member sowie die Parameter der Endpunkt-Handler (Routen-/Query-Bindung ueber den Namen). Die JSON-Felder anonymer Objekte bleiben ausdruecklich benannt (JsonFilter: pfad). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
837a634a22
commit
77062facc9
@@ -9,7 +9,7 @@ namespace ClawdDotNet.Web.Tests;
|
||||
public sealed class AgentCommunicationTests
|
||||
{
|
||||
[Fact]
|
||||
public void AgentCommunicationDto_speichert_Felder_korrekt()
|
||||
public void AgentCommunicationDto_stores_fields_correctly()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var dto = new AgentCommunicationDto(
|
||||
|
||||
@@ -37,7 +37,7 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
catch { /* A leftover test folder does not turn a run red. */ }
|
||||
}
|
||||
|
||||
private AppHost NeuerHost(bool mitWeboberflaeche, int port = 0)
|
||||
private AppHost NewHost(bool withWebUi, int port = 0)
|
||||
{
|
||||
var instancesDir = Path.Combine(_root, "Instances");
|
||||
var instancePath = Path.Combine(instancesDir, "Test");
|
||||
@@ -51,9 +51,9 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
{
|
||||
InstanceName = "Test",
|
||||
WorkingDirectory = instancePath,
|
||||
WebUiEnabled = mitWeboberflaeche,
|
||||
WebUiEnabled = withWebUi,
|
||||
WebUiBinding = "loopback",
|
||||
WebUiToken = mitWeboberflaeche ? Token : "",
|
||||
WebUiToken = withWebUi ? Token : "",
|
||||
WebServerPort = port
|
||||
},
|
||||
InstancePath = instancePath,
|
||||
@@ -63,7 +63,7 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
};
|
||||
}
|
||||
|
||||
private static int FreierPort()
|
||||
private static int FreePort()
|
||||
{
|
||||
using var probe = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
|
||||
probe.Start();
|
||||
@@ -75,27 +75,27 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
// ─── The socket holds ───
|
||||
|
||||
[Fact]
|
||||
public async Task Der_Socket_antwortet_auch_ohne_Weboberflaeche()
|
||||
public async Task The_socket_answers_even_without_web_UI()
|
||||
{
|
||||
// The default: webUiEnabled is off. Formerly nothing ran at all then, and the
|
||||
// instance was not addressable from outside.
|
||||
await using var host = NeuerHost(mitWeboberflaeche: false);
|
||||
await using var host = NewHost(withWebUi: false);
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
|
||||
web.ShouldNotBeNull();
|
||||
web!.Url.ShouldBeNull();
|
||||
|
||||
using var client = InstanceEndpoint.CreateClient(host.InstancePath);
|
||||
var antwort = await client.GetAsync("/api/info");
|
||||
var answer = await client.GetAsync("/api/info");
|
||||
|
||||
antwort.EnsureSuccessStatusCode();
|
||||
(await antwort.Content.ReadAsStringAsync()).ShouldContain("Test");
|
||||
answer.EnsureSuccessStatusCode();
|
||||
(await answer.Content.ReadAsStringAsync()).ShouldContain("Test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Ueber_den_Socket_braucht_es_kein_Token()
|
||||
public async Task Over_the_socket_no_token_is_needed()
|
||||
{
|
||||
await using var host = NeuerHost(mitWeboberflaeche: true, port: FreierPort());
|
||||
await using var host = NewHost(withWebUi: true, port: FreePort());
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
|
||||
web.ShouldNotBeNull();
|
||||
@@ -108,11 +108,11 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
// ─── The port stays guarded ───
|
||||
|
||||
[Fact]
|
||||
public async Task Ueber_TCP_gilt_die_Token_Pflicht_weiter()
|
||||
public async Task Over_TCP_the_token_requirement_still_applies()
|
||||
{
|
||||
var port = FreierPort();
|
||||
var port = FreePort();
|
||||
|
||||
await using var host = NeuerHost(mitWeboberflaeche: true, port: port);
|
||||
await using var host = NewHost(withWebUi: true, port: port);
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
|
||||
web.ShouldNotBeNull();
|
||||
@@ -130,7 +130,7 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
// ─── Addresses ───
|
||||
|
||||
[Fact]
|
||||
public void Zwei_Instanzen_bekommen_zwei_Adressen()
|
||||
public void Two_instances_get_two_addresses()
|
||||
{
|
||||
var a = Path.Combine(_root, "Instances", "Eins");
|
||||
var b = Path.Combine(_root, "Instances", "Zwei");
|
||||
@@ -140,7 +140,7 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dieselbe_Instanz_bekommt_dieselbe_Adresse()
|
||||
public void The_same_instance_gets_the_same_address()
|
||||
{
|
||||
// The manager computes the address from the path it knows itself — so it
|
||||
// must come out the same from every spelling of the same directory.
|
||||
@@ -161,12 +161,12 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
/// <c>EADDRINUSE</c> from a process that no longer exists.</para>
|
||||
/// </summary>
|
||||
[SocketFileFact]
|
||||
public async Task Eine_liegengebliebene_Socketdatei_haelt_den_Start_nicht_auf()
|
||||
public async Task A_leftover_socket_file_does_not_hold_up_the_start()
|
||||
{
|
||||
await using var host = NeuerHost(mitWeboberflaeche: false);
|
||||
await using var host = NewHost(withWebUi: false);
|
||||
|
||||
var leiche = InstanceEndpoint.SocketPath(host.InstancePath);
|
||||
File.WriteAllText(leiche, "Rest eines abgestuerzten Prozesses");
|
||||
var corpse = InstanceEndpoint.SocketPath(host.InstancePath);
|
||||
File.WriteAllText(corpse, "Rest eines abgestuerzten Prozesses");
|
||||
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
web.ShouldNotBeNull();
|
||||
@@ -176,9 +176,9 @@ public sealed class InstanceSocketTests : IDisposable
|
||||
}
|
||||
|
||||
[SocketFileFact]
|
||||
public async Task Beim_Beenden_bleibt_keine_Socketdatei_liegen()
|
||||
public async Task Shutdown_leaves_no_socket_file_behind()
|
||||
{
|
||||
await using var host = NeuerHost(mitWeboberflaeche: false);
|
||||
await using var host = NewHost(withWebUi: false);
|
||||
|
||||
var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
web.ShouldNotBeNull();
|
||||
@@ -212,7 +212,7 @@ public sealed class InstanceControlTests : IDisposable
|
||||
catch { /* A leftover test folder does not turn a run red. */ }
|
||||
}
|
||||
|
||||
private AppHost NeuerHost(int port)
|
||||
private AppHost NewHost(int port)
|
||||
{
|
||||
var instancesDir = Path.Combine(_root, "Instances");
|
||||
var instancePath = Path.Combine(instancesDir, "Test");
|
||||
@@ -238,7 +238,7 @@ public sealed class InstanceControlTests : IDisposable
|
||||
};
|
||||
}
|
||||
|
||||
private static int FreierPort()
|
||||
private static int FreePort()
|
||||
{
|
||||
using var probe = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
|
||||
probe.Start();
|
||||
@@ -248,18 +248,18 @@ public sealed class InstanceControlTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Ueber_TCP_gibt_es_die_Steuerung_nicht()
|
||||
public async Task Over_TCP_there_is_no_control()
|
||||
{
|
||||
var port = FreierPort();
|
||||
var port = FreePort();
|
||||
|
||||
await using var host = NeuerHost(port);
|
||||
await using var host = NewHost(port);
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
|
||||
web.ShouldNotBeNull();
|
||||
web!.Url.ShouldNotBeNull();
|
||||
|
||||
var gebeten = false;
|
||||
web.Control.ShutdownRequested += () => gebeten = true;
|
||||
var requested = false;
|
||||
web.Control.ShutdownRequested += () => requested = true;
|
||||
|
||||
using var client = new HttpClient { BaseAddress = new Uri($"http://127.0.0.1:{port}") };
|
||||
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {Token}");
|
||||
@@ -268,6 +268,6 @@ public sealed class InstanceControlTests : IDisposable
|
||||
(await client.PostAsync("/api/instanz/beenden", content: null)).StatusCode
|
||||
.ShouldBe(System.Net.HttpStatusCode.NotFound);
|
||||
|
||||
gebeten.ShouldBeFalse();
|
||||
requested.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,16 @@ namespace ClawdDotNet.Web.Tests;
|
||||
public sealed class SecretMaskingTests
|
||||
{
|
||||
[Fact]
|
||||
public void Ein_Passwort_kommt_nicht_heraus()
|
||||
public void A_password_does_not_come_out()
|
||||
{
|
||||
var einstellungen = new Dictionary<string, object?>
|
||||
var settings = new Dictionary<string, object?>
|
||||
{
|
||||
["host"] = "mail.example",
|
||||
["port"] = 993,
|
||||
["password"] = "sehr-geheim"
|
||||
};
|
||||
|
||||
var dto = Mapping.ToDto("Mail", einstellungen);
|
||||
var dto = Mapping.ToDto("Mail", settings);
|
||||
|
||||
// Keys.ShouldContain instead of ShouldContainKey: the DTOs hand out
|
||||
// IReadOnlyDictionary, and Shouldly's dictionary extensions sit
|
||||
@@ -39,7 +39,7 @@ public sealed class SecretMaskingTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_leeres_Geheimnis_gilt_als_nicht_gesetzt()
|
||||
public void An_empty_secret_counts_as_not_set()
|
||||
{
|
||||
var dto = Mapping.ToDto("Mail", new Dictionary<string, object?>
|
||||
{
|
||||
@@ -63,25 +63,25 @@ public sealed class SecretMaskingTests
|
||||
[InlineData("connectionString")]
|
||||
[InlineData("apiHash")]
|
||||
[InlineData("webUiToken")]
|
||||
public void Jeder_bekannte_Geheimnisname_wird_maskiert(string schluessel)
|
||||
public void Every_known_secret_name_is_masked(string key)
|
||||
{
|
||||
// The list comes from ConfigSecrets and not from a second one here: a
|
||||
// second would be the one forgotten with the next new tool.
|
||||
ConfigSecrets.IsSecretKey(schluessel).ShouldBeTrue();
|
||||
ConfigSecrets.IsSecretKey(key).ShouldBeTrue();
|
||||
|
||||
Mapping.ToDto("X", new Dictionary<string, object?> { [schluessel] = "geheim" })
|
||||
.Values.Keys.ShouldNotContain(schluessel);
|
||||
Mapping.ToDto("X", new Dictionary<string, object?> { [key] = "geheim" })
|
||||
.Values.Keys.ShouldNotContain(key);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Die_Schreibweise_des_Schluessels_spielt_keine_Rolle()
|
||||
public void The_spelling_of_the_key_does_not_matter()
|
||||
{
|
||||
Mapping.ToDto("X", new Dictionary<string, object?> { ["PASSWORD"] = "geheim" })
|
||||
.Values.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_serialisiertes_DTO_enthaelt_das_Geheimnis_nicht()
|
||||
public void A_serialised_DTO_does_not_contain_the_secret()
|
||||
{
|
||||
// The test that really counts: not the structure, but what goes over the
|
||||
// line.
|
||||
|
||||
@@ -35,9 +35,9 @@ public sealed class ShutdownTests : IDisposable
|
||||
/// seconds). If the test stays below two, the promise is kept without a
|
||||
/// slow build server turning it red.
|
||||
/// </summary>
|
||||
private static readonly TimeSpan Grenze = TimeSpan.FromSeconds(2);
|
||||
private static readonly TimeSpan Limit = TimeSpan.FromSeconds(2);
|
||||
|
||||
private AppHost NeuerHost(int port)
|
||||
private AppHost NewHost(int port)
|
||||
{
|
||||
var instancesDir = Path.Combine(_root, "Instances");
|
||||
var instancePath = Path.Combine(instancesDir, "Test");
|
||||
@@ -66,7 +66,7 @@ public sealed class ShutdownTests : IDisposable
|
||||
/// A free port from the operating system. A fixed number would let two test runs
|
||||
/// fail side by side — and those are exactly the errors you search for the longest.
|
||||
/// </summary>
|
||||
private static int FreierPort()
|
||||
private static int FreePort()
|
||||
{
|
||||
using var probe = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
|
||||
probe.Start();
|
||||
@@ -76,10 +76,10 @@ public sealed class ShutdownTests : IDisposable
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Beenden_wartet_nicht_auf_einen_offenen_Ereignisstrom()
|
||||
public async Task Shutdown_does_not_wait_for_an_open_event_stream()
|
||||
{
|
||||
var port = FreierPort();
|
||||
await using var host = NeuerHost(port);
|
||||
var port = FreePort();
|
||||
await using var host = NewHost(port);
|
||||
|
||||
var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
web.ShouldNotBeNull();
|
||||
@@ -89,39 +89,39 @@ public sealed class ShutdownTests : IDisposable
|
||||
|
||||
// Really open the stream and wait for the first line: only then does
|
||||
// Kestrel hold the request, and only then does the test check what it wants to check.
|
||||
using var antwort = await client.GetAsync(
|
||||
using var answer = await client.GetAsync(
|
||||
$"http://127.0.0.1:{port}/events?token={Token}",
|
||||
HttpCompletionOption.ResponseHeadersRead);
|
||||
|
||||
antwort.EnsureSuccessStatusCode();
|
||||
answer.EnsureSuccessStatusCode();
|
||||
|
||||
await using var strom = await antwort.Content.ReadAsStreamAsync();
|
||||
using var leser = new StreamReader(strom);
|
||||
(await leser.ReadLineAsync()).ShouldBe(": bereit");
|
||||
await using var stream = await answer.Content.ReadAsStreamAsync();
|
||||
using var reader = new StreamReader(stream);
|
||||
(await reader.ReadLineAsync()).ShouldBe(": bereit");
|
||||
|
||||
var uhr = Stopwatch.StartNew();
|
||||
var clock = Stopwatch.StartNew();
|
||||
await web!.DisposeAsync();
|
||||
uhr.Stop();
|
||||
clock.Stop();
|
||||
|
||||
uhr.Elapsed.ShouldBeLessThan(Grenze,
|
||||
$"Das Beenden dauerte {uhr.Elapsed.TotalSeconds:0.0}s — der Ereignisstrom "
|
||||
clock.Elapsed.ShouldBeLessThan(Limit,
|
||||
$"Das Beenden dauerte {clock.Elapsed.TotalSeconds:0.0}s — der Ereignisstrom "
|
||||
+ "hat das Herunterfahren nicht bemerkt.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Beenden_geht_auch_ohne_offene_Verbindung_zuegig()
|
||||
public async Task Shutdown_is_quick_even_without_an_open_connection()
|
||||
{
|
||||
var port = FreierPort();
|
||||
await using var host = NeuerHost(port);
|
||||
var port = FreePort();
|
||||
await using var host = NewHost(port);
|
||||
|
||||
var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
web.ShouldNotBeNull();
|
||||
|
||||
var uhr = Stopwatch.StartNew();
|
||||
var clock = Stopwatch.StartNew();
|
||||
await web!.DisposeAsync();
|
||||
uhr.Stop();
|
||||
clock.Stop();
|
||||
|
||||
uhr.Elapsed.ShouldBeLessThan(Grenze);
|
||||
clock.Elapsed.ShouldBeLessThan(Limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -135,14 +135,14 @@ public sealed class ShutdownTests : IDisposable
|
||||
/// exists for.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Ein_belegter_Port_kostet_nur_die_Weboberflaeche()
|
||||
public async Task An_occupied_port_only_costs_the_web_UI()
|
||||
{
|
||||
var port = FreierPort();
|
||||
var port = FreePort();
|
||||
|
||||
using var blockierer = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, port);
|
||||
blockierer.Start();
|
||||
|
||||
await using var host = NeuerHost(port);
|
||||
await using var host = NewHost(port);
|
||||
|
||||
await using var web = InstanceHttpHost.TryStart(host, NullLoggerFactory.Instance);
|
||||
|
||||
|
||||
+37
-37
@@ -11,40 +11,40 @@ namespace ClawdDotNet.Web.Tests;
|
||||
/// Findings from reviewing the web UI in test operation (#572): #591 (field names) and
|
||||
/// #536 (direction of the A2A answers).
|
||||
/// </summary>
|
||||
public sealed class TestbetriebWebTests
|
||||
public sealed class TestOperationWebTests
|
||||
{
|
||||
// ─── #591 event stream and transfer types ───
|
||||
|
||||
[Fact]
|
||||
public void Protokollzeilen_im_Ereignisstrom_kommen_in_camelCase()
|
||||
public void Log_lines_in_the_event_stream_come_in_camelCase()
|
||||
{
|
||||
var ereignis = EventEndpoints.Format("log", new LogLineDto("Startup", "INF", "ClawdDotNet startet"));
|
||||
var evt = EventEndpoints.Format("log", new LogLineDto("Startup", "INF", "ClawdDotNet startet"));
|
||||
|
||||
ereignis.ShouldStartWith("event: log\n");
|
||||
ereignis.ShouldContain("\"module\":\"Startup\"");
|
||||
ereignis.ShouldContain("\"level\":\"INF\"");
|
||||
ereignis.ShouldContain("\"text\":\"ClawdDotNet startet\"");
|
||||
ereignis.ShouldNotContain("\"Text\"", Case.Sensitive);
|
||||
evt.ShouldStartWith("event: log\n");
|
||||
evt.ShouldContain("\"module\":\"Startup\"");
|
||||
evt.ShouldContain("\"level\":\"INF\"");
|
||||
evt.ShouldContain("\"text\":\"ClawdDotNet startet\"");
|
||||
evt.ShouldNotContain("\"Text\"", Case.Sensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Freigabe_traegt_ihren_Zeitpunkt()
|
||||
public void Approval_carries_its_time()
|
||||
{
|
||||
var angelegt = new DateTime(2026, 9, 14, 10, 18, 53, DateTimeKind.Utc);
|
||||
var created = new DateTime(2026, 9, 14, 10, 18, 53, DateTimeKind.Utc);
|
||||
|
||||
var dto = new StagedCall
|
||||
{
|
||||
Id = 1, AgentId = "hausmeister", Tool = "FileRW", Action = "delete",
|
||||
ArgumentsJson = "{\"path\":\"betrieb/probe-loeschung.txt\"}", CreatedAt = angelegt
|
||||
ArgumentsJson = "{\"path\":\"betrieb/probe-loeschung.txt\"}", CreatedAt = created
|
||||
}.ToDto();
|
||||
|
||||
dto.CreatedAt.ShouldBe(angelegt);
|
||||
dto.CreatedAt.ShouldBe(created);
|
||||
dto.Tool.ShouldBe("FileRW");
|
||||
dto.Action.ShouldBe("delete");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Aufgabe_traegt_ihren_Termin()
|
||||
public void Task_carries_its_schedule()
|
||||
{
|
||||
var dto = new TaskItem
|
||||
{
|
||||
@@ -60,47 +60,47 @@ public sealed class TestbetriebWebTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Einmalige_Aufgabe_hat_keinen_Termin()
|
||||
public void One_off_task_has_no_schedule()
|
||||
=> new TaskItem { Id = "t-1", Title = "Einrichtung" }.ToDto().When.ShouldBeNull();
|
||||
|
||||
// ─── #536 sender and recipient ───
|
||||
|
||||
private static ChatEntry Anfrage(string vonName, string vonId, string text, int minute)
|
||||
=> new("user", $"[Nachricht von Agent \"{vonName}\" ({vonId})]\n\n{text}",
|
||||
private static ChatEntry Request(string fromName, string fromId, string text, int minute)
|
||||
=> new("user", $"[Nachricht von Agent \"{fromName}\" ({fromId})]\n\n{text}",
|
||||
new DateTime(2026, 9, 14, 12, minute, 0), ChatSource.AgentComm);
|
||||
|
||||
private static ChatEntry Antwort(string text, int minute)
|
||||
private static ChatEntry Answer(string text, int minute)
|
||||
=> new("assistant", text, new DateTime(2026, 9, 14, 12, minute, 30), ChatSource.AgentComm);
|
||||
|
||||
[Fact]
|
||||
public void Antwort_geht_vom_Antwortenden_an_den_Fragenden()
|
||||
public void Answer_goes_from_the_responder_to_the_asker()
|
||||
{
|
||||
var liste = AgentCommunicationBuilder.Build(
|
||||
var list = AgentCommunicationBuilder.Build(
|
||||
[
|
||||
("redaktion", "Redaktion", (IEnumerable<ChatEntry>)
|
||||
[
|
||||
Anfrage("Hausmeister", "hausmeister", "Testnachricht", 48),
|
||||
Antwort("Empfang bestaetigt", 48)
|
||||
Request("Hausmeister", "hausmeister", "Testnachricht", 48),
|
||||
Answer("Empfang bestaetigt", 48)
|
||||
])
|
||||
]);
|
||||
|
||||
liste.Count.ShouldBe(2);
|
||||
list.Count.ShouldBe(2);
|
||||
|
||||
var anfrage = liste[0];
|
||||
anfrage.SourceAgentId.ShouldBe("hausmeister");
|
||||
anfrage.TargetAgentId.ShouldBe("redaktion");
|
||||
anfrage.Content.ShouldBe("Testnachricht");
|
||||
var request = list[0];
|
||||
request.SourceAgentId.ShouldBe("hausmeister");
|
||||
request.TargetAgentId.ShouldBe("redaktion");
|
||||
request.Content.ShouldBe("Testnachricht");
|
||||
|
||||
var antwort = liste[1];
|
||||
antwort.SourceAgentId.ShouldBe("redaktion");
|
||||
antwort.TargetAgentId.ShouldBe("hausmeister", "vorher stand hier der Antwortende selbst");
|
||||
antwort.TargetAgentName.ShouldBe("Hausmeister");
|
||||
var answer = list[1];
|
||||
answer.SourceAgentId.ShouldBe("redaktion");
|
||||
answer.TargetAgentId.ShouldBe("hausmeister", "vorher stand hier der Antwortende selbst");
|
||||
answer.TargetAgentName.ShouldBe("Hausmeister");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Nur_Agentenkommunikation_wird_aufgenommen()
|
||||
public void Only_agent_communication_is_recorded()
|
||||
{
|
||||
var liste = AgentCommunicationBuilder.Build(
|
||||
var list = AgentCommunicationBuilder.Build(
|
||||
[
|
||||
("hausmeister", "Hausmeister", (IEnumerable<ChatEntry>)
|
||||
[
|
||||
@@ -109,20 +109,20 @@ public sealed class TestbetriebWebTests
|
||||
])
|
||||
]);
|
||||
|
||||
liste.ShouldBeEmpty();
|
||||
list.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("[Nachricht von Agent \"Hausmeister\" ohne Klammer]\n\nText")]
|
||||
[InlineData("[Nachricht von Agent \"Bo\"b\" (bob)]\n\nText")]
|
||||
[InlineData("[Nachricht von Agent \"")]
|
||||
public void Absender_lesen_wirft_bei_unerwarteter_Form_nicht(string inhalt)
|
||||
=> Should.NotThrow(() => AgentCommunicationBuilder.LeseAbsender(inhalt));
|
||||
public void Reading_the_sender_does_not_throw_on_unexpected_form(string content)
|
||||
=> Should.NotThrow(() => AgentCommunicationBuilder.ReadSender(content));
|
||||
|
||||
[Fact]
|
||||
public void Absender_lesen_liefert_Kennung_Name_und_Text()
|
||||
public void Reading_the_sender_returns_id_name_and_text()
|
||||
{
|
||||
var (id, name, text) = AgentCommunicationBuilder.LeseAbsender(
|
||||
var (id, name, text) = AgentCommunicationBuilder.ReadSender(
|
||||
"[Nachricht von Agent \"Hausmeister\" (hausmeister)]\n\nHallo Redaktion!");
|
||||
|
||||
id.ShouldBe("hausmeister");
|
||||
@@ -11,7 +11,7 @@ namespace ClawdDotNet.Web.Tests;
|
||||
/// </summary>
|
||||
public sealed class WebUiOptionsTests
|
||||
{
|
||||
private static InstanceConfig Instanz(
|
||||
private static InstanceConfig Instance(
|
||||
bool enabled = true,
|
||||
string binding = "loopback",
|
||||
string token = "ein-hinreichend-langes-token",
|
||||
@@ -25,7 +25,7 @@ public sealed class WebUiOptionsTests
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Sie_ist_in_der_Vorbelegung_aus()
|
||||
public void It_is_off_by_default()
|
||||
{
|
||||
// Whoever wants a network service switches it on. Not the other way round.
|
||||
new InstanceConfig().WebUiEnabled.ShouldBeFalse();
|
||||
@@ -34,25 +34,25 @@ public sealed class WebUiOptionsTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Abgeschaltet_ist_kein_Fehler()
|
||||
public void Switched_off_is_not_an_error()
|
||||
{
|
||||
WebUiOptions.TryCreate(Instanz(enabled: false), out var problem).ShouldBeNull();
|
||||
WebUiOptions.TryCreate(Instance(enabled: false), out var problem).ShouldBeNull();
|
||||
problem.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ohne_Token_startet_sie_nicht_einmal_auf_Loopback()
|
||||
public void Without_token_it_does_not_start_even_on_loopback()
|
||||
{
|
||||
// On a machine on which agents operate browser tools,
|
||||
// "localhost" is no trust boundary.
|
||||
WebUiOptions.TryCreate(Instanz(token: ""), out var problem).ShouldBeNull();
|
||||
WebUiOptions.TryCreate(Instance(token: ""), out var problem).ShouldBeNull();
|
||||
problem.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alle_Adressen_ohne_Token_werden_abgelehnt_und_nicht_bemaengelt()
|
||||
public void All_addresses_without_token_are_rejected_and_not_flagged()
|
||||
{
|
||||
var options = WebUiOptions.TryCreate(Instanz(binding: "any", token: ""), out var problem);
|
||||
var options = WebUiOptions.TryCreate(Instance(binding: "any", token: ""), out var problem);
|
||||
|
||||
options.ShouldBeNull();
|
||||
problem.ShouldNotBeNull();
|
||||
@@ -60,16 +60,16 @@ public sealed class WebUiOptionsTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ein_zu_kurzes_Token_zaehlt_nicht()
|
||||
public void A_too_short_token_does_not_count()
|
||||
{
|
||||
WebUiOptions.TryCreate(Instanz(token: "kurz"), out var problem).ShouldBeNull();
|
||||
WebUiOptions.TryCreate(Instance(token: "kurz"), out var problem).ShouldBeNull();
|
||||
problem.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Loopback_bindet_an_127_0_0_1()
|
||||
public void Loopback_binds_to_127_0_0_1()
|
||||
{
|
||||
var options = WebUiOptions.TryCreate(Instanz(port: 9001), out _);
|
||||
var options = WebUiOptions.TryCreate(Instance(port: 9001), out _);
|
||||
|
||||
options.ShouldNotBeNull();
|
||||
options!.Url.ShouldBe("http://127.0.0.1:9001");
|
||||
@@ -77,17 +77,17 @@ public sealed class WebUiOptionsTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alles_ausser_any_bleibt_Loopback()
|
||||
public void Everything_except_any_stays_loopback()
|
||||
{
|
||||
// A typo in the instance file must not put the user interface onto the network.
|
||||
WebUiOptions.TryCreate(Instanz(binding: "alle"), out _)!.LoopbackOnly.ShouldBeTrue();
|
||||
WebUiOptions.TryCreate(Instanz(binding: ""), out _)!.LoopbackOnly.ShouldBeTrue();
|
||||
WebUiOptions.TryCreate(Instance(binding: "alle"), out _)!.LoopbackOnly.ShouldBeTrue();
|
||||
WebUiOptions.TryCreate(Instance(binding: ""), out _)!.LoopbackOnly.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mit_Token_darf_sie_an_alle_Adressen()
|
||||
public void With_token_it_may_reach_all_addresses()
|
||||
{
|
||||
var options = WebUiOptions.TryCreate(Instanz(binding: "ANY"), out _);
|
||||
var options = WebUiOptions.TryCreate(Instance(binding: "ANY"), out _);
|
||||
|
||||
options.ShouldNotBeNull();
|
||||
options!.LoopbackOnly.ShouldBeFalse();
|
||||
@@ -98,20 +98,20 @@ public sealed class WebUiOptionsTests
|
||||
[InlineData(0)]
|
||||
[InlineData(-1)]
|
||||
[InlineData(70000)]
|
||||
public void Ein_unmoeglicher_Port_wird_abgelehnt(int port)
|
||||
public void An_impossible_port_is_rejected(int port)
|
||||
{
|
||||
WebUiOptions.TryCreate(Instanz(port: port), out var problem).ShouldBeNull();
|
||||
WebUiOptions.TryCreate(Instance(port: port), out var problem).ShouldBeNull();
|
||||
problem.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Das_Token_steht_geschuetzt_in_der_Instanzdatei()
|
||||
public void The_token_is_stored_protected_in_the_instance_file()
|
||||
{
|
||||
// Without the entry in ConfigSecrets it would be in plain text next to the
|
||||
// protected OpenRouter key.
|
||||
Core.Security.ConfigSecrets.IsSecretKey("webUiToken").ShouldBeTrue();
|
||||
|
||||
var config = Instanz(token: "ein-hinreichend-langes-token");
|
||||
var config = Instance(token: "ein-hinreichend-langes-token");
|
||||
Core.Security.ConfigSecrets.Protect(config);
|
||||
|
||||
config.WebUiToken.ShouldNotBe("ein-hinreichend-langes-token");
|
||||
|
||||
Reference in New Issue
Block a user