using ClawdDotNet.App.Services; using ClawdDotNet.Core.Config; using Shouldly; namespace ClawdDotNet.App.Tests.Services; /// /// The instance's credentials must survive the way to disk and back. /// /// SaveInstanceConfig encrypted when writing, but LoadInstanceConfig /// did not decrypt when reading. Whatever had once been saved via the user interface /// came back as enc:v2:… and was used as the value — the OpenRouter key /// thus went to the API encrypted. /// /// It went undiscovered because ConfigLoader.LoadAsync does it right /// and is not called at all in the running program: only Migrate /// of that class is needed. /// public sealed class InstanceSecretRoundtripTests { [Fact] public void Credentials_come_back_in_plaintext() { using var host = new TestHost(); host.Host.Instance.OpenRouterApiKey = "sk-or-v1-beispielschluessel"; host.Host.Instance.WebUiToken = "ein-hinreichend-langes-token"; host.Host.Instance.Watchdog.AgentToken = "dc-token-beispiel"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); var loaded = host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath); loaded.OpenRouterApiKey.ShouldBe("sk-or-v1-beispielschluessel"); loaded.WebUiToken.ShouldBe("ein-hinreichend-langes-token"); loaded.Watchdog.AgentToken.ShouldBe("dc-token-beispiel"); } [Fact] public void In_the_file_they_are_still_protected() { // The counter-check: decrypting when loading must not mean that nothing is // encrypted any more. using var host = new TestHost(); host.Host.Instance.OpenRouterApiKey = "sk-or-v1-beispielschluessel"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); var rawText = File.ReadAllText( Path.Combine(host.Host.InstancePath, "InstanceSettings.json")); rawText.ShouldNotContain("sk-or-v1-beispielschluessel"); } [Fact] public void Plaintext_from_older_files_stays_readable() { // Decrypting must be safely applicable to something that was never encrypted // — otherwise the first start after the update would break every existing instance. using var host = new TestHost(); var path = Path.Combine(host.Host.InstancePath, "InstanceSettings.json"); File.WriteAllText(path, """ { "instanceId": "alt", "instanceName": "Alt", "openRouterApiKey": "klartext-von-hand-eingetragen", "webUiToken": "auch-klartext" } """); var loaded = host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath); loaded.OpenRouterApiKey.ShouldBe("klartext-von-hand-eingetragen"); loaded.WebUiToken.ShouldBe("auch-klartext"); } [Fact] public void Saving_twice_does_not_encrypt_twice() { using var host = new TestHost(); host.Host.Instance.WebUiToken = "ein-hinreichend-langes-token"; host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); host.Host.SaveInstance(new InstanceChange(InstanceChangeKind.Settings)); host.Host.Directories.LoadInstanceConfig(host.Host.InstancePath) .WebUiToken.ShouldBe("ein-hinreichend-langes-token"); } }