Stufe 1 (#764): Agent bearbeiten, Lizenz und Einstellungen in der WebUI
- PUT /api/agents/{id} und /texts: Stammdaten, Grenzen, Budget, Identity/Soul/
Systemprompt (L6/L7); "Jetzt ausfuehren" in der Agentenansicht (L9)
- Lizenzschluessel ueber die Weboberflaeche (L1): erst vom Lizenzserver
bestaetigt, dann gespeichert; hebt die Sperre ohne Neustart auf
- GET/PUT /api/settings: Instanz- und Anwendungseinstellungen inkl.
OpenRouter-Key (L3/L4/L5), Geheimnisse nur schreibend
- GET/PUT /api/verwaltung/settings: Deploymentcenter und Update-Kanal des Managers
- Einstellungsseite zeigt echte Werte statt fester Texte
- NonInteractiveLicensePrompt verwies auf --license-set-key, den es nie gab
- Sprachpakete werden bei jedem Lauf geprueft (LanguagePackTests)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a9ce72abe2
commit
8552755bb6
@@ -0,0 +1,116 @@
|
||||
using ClawdDotNet.App.Services;
|
||||
using ClawdDotNet.App.Settings;
|
||||
using Shouldly;
|
||||
|
||||
namespace ClawdDotNet.App.Tests.Services;
|
||||
|
||||
/// <summary>
|
||||
/// #764: The settings the WebUI reads and writes — what the desktop settings page edited.
|
||||
/// </summary>
|
||||
public sealed class SettingsAdministrationTests
|
||||
{
|
||||
[Fact]
|
||||
public void The_snapshot_never_carries_a_secret()
|
||||
{
|
||||
using var host = new TestHost();
|
||||
host.Host.Instance.OpenRouterApiKey = "sk-or-secret";
|
||||
host.Host.Settings.AppSettings.DeploymentcenterToken = "dc-secret";
|
||||
|
||||
var snapshot = host.Host.SettingsAdmin.Snapshot();
|
||||
|
||||
snapshot.OpenRouterApiKeySet.ShouldBeTrue();
|
||||
snapshot.DeploymentcenterTokenSet.ShouldBeTrue();
|
||||
System.Text.Json.JsonSerializer.Serialize(snapshot).ShouldNotContain("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Instance_settings_survive_a_restart()
|
||||
{
|
||||
using var host = new TestHost();
|
||||
host.Host.Directories.SaveInstanceConfig(host.Host.InstancePath, host.Host.Instance);
|
||||
|
||||
var result = host.Host.SettingsAdmin.Update(new SettingsUpdate(
|
||||
InstanceName: "Renamed", AutostartByManager: true, DailyCostUsd: 2.5m, DailyTokens: 900,
|
||||
AutoBackupEnabled: true, AutoBackupTime: "4:30", BackupKeepCount: 7));
|
||||
|
||||
result.Ok.ShouldBeTrue(result.Message);
|
||||
|
||||
var reloaded = host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath);
|
||||
reloaded.InstanceName.ShouldBe("Renamed");
|
||||
reloaded.AutostartByManager.ShouldBeTrue();
|
||||
reloaded.Budget.DailyCostUsd.ShouldBe(2.5m);
|
||||
reloaded.Budget.DailyTokens.ShouldBe(900);
|
||||
reloaded.AutoBackupEnabled.ShouldBeTrue();
|
||||
reloaded.AutoBackupTime.ShouldBe("04:30");
|
||||
reloaded.BackupKeepCount.ShouldBe(7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_empty_secret_keeps_the_one_there_is()
|
||||
{
|
||||
// The form never gets the key back, so it sends an empty field. That must not wipe it.
|
||||
using var host = new TestHost();
|
||||
host.Host.Instance.OpenRouterApiKey = "sk-or-keep";
|
||||
host.Host.Settings.AppSettings.DeploymentcenterToken = "dc-keep";
|
||||
|
||||
host.Host.SettingsAdmin.Update(new SettingsUpdate(OpenRouterApiKey: "", DeploymentcenterToken: " ")).Ok.ShouldBeTrue();
|
||||
|
||||
host.Host.Instance.OpenRouterApiKey.ShouldBe("sk-or-keep");
|
||||
host.Host.Settings.AppSettings.DeploymentcenterToken.ShouldBe("dc-keep");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_new_key_says_it_needs_a_restart()
|
||||
{
|
||||
// The engine is built at start; without saying so the save button would seem to do nothing.
|
||||
using var host = new TestHost();
|
||||
|
||||
var result = host.Host.SettingsAdmin.Update(new SettingsUpdate(OpenRouterApiKey: "sk-or-new"));
|
||||
|
||||
result.Ok.ShouldBeTrue();
|
||||
result.Message.ShouldContain("restart");
|
||||
host.Host.Instance.OpenRouterApiKey.ShouldBe("sk-or-new");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Application_settings_are_written_to_settings_json()
|
||||
{
|
||||
using var host = new TestHost();
|
||||
|
||||
host.Host.SettingsAdmin.Update(new SettingsUpdate(
|
||||
MinimumLogLevel: "warn", UpdateChannel: "beta", ErrorReportingEnabled: false)).Ok.ShouldBeTrue();
|
||||
|
||||
var root = Path.GetDirectoryName(host.Host.Settings.SettingsPath)!;
|
||||
var reloaded = new SettingsManager(root);
|
||||
reloaded.Load();
|
||||
reloaded.AppSettings.MinimumLogLevel.ShouldBe("Warn");
|
||||
reloaded.AppSettings.UpdateChannel.ShouldBe("beta");
|
||||
reloaded.AppSettings.ErrorReportingEnabled.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("time")]
|
||||
[InlineData("level")]
|
||||
[InlineData("url")]
|
||||
[InlineData("channel")]
|
||||
[InlineData("name")]
|
||||
[InlineData("keep")]
|
||||
public void One_bad_value_changes_nothing(string bad)
|
||||
{
|
||||
using var host = new TestHost();
|
||||
var update = bad switch
|
||||
{
|
||||
"time" => new SettingsUpdate(DailyTokens: 5, AutoBackupTime: "25:00"),
|
||||
"level" => new SettingsUpdate(DailyTokens: 5, MinimumLogLevel: "Chatty"),
|
||||
"url" => new SettingsUpdate(DailyTokens: 5, OpenRouterBaseUrl: "file:///etc/passwd"),
|
||||
"channel" => new SettingsUpdate(DailyTokens: 5, UpdateChannel: "nightly"),
|
||||
"name" => new SettingsUpdate(DailyTokens: 5, InstanceName: " "),
|
||||
_ => new SettingsUpdate(DailyTokens: 5, BackupKeepCount: -1)
|
||||
};
|
||||
|
||||
host.Host.SettingsAdmin.Update(update).Ok.ShouldBeFalse();
|
||||
|
||||
host.Host.Instance.Budget.DailyTokens.ShouldBe(0);
|
||||
host.Changes.ShouldBeEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user