using ClawdDotNet.Core.Config; using ClawdDotNet.Core.Organisation; using Shouldly; namespace ClawdDotNet.Core.Tests.Organisation; /// /// #692: A unit holds credentials for its members — needed once, not six times, and the /// accounts department never gets the marketplace key. /// public sealed class UnitCredentialsTests { /// shop (holds DirectAPI.apiKey, Mail.password) › buying (holds DirectAPI.apiKey); accounts beside it. private static OrgChart Org(params AgentConfig[] agents) => new( [ new UnitConfig { Id = "shop", Name = "Shop", Credentials = new() { ["DirectAPI"] = new() { ["apiKey"] = "shop-key" }, ["Mail"] = new() { ["password"] = "shop-mail" } } }, new UnitConfig { Id = "buying", Name = "Buying", ParentId = "shop", Credentials = new() { ["directapi"] = new() { ["apiKey"] = "buying-key" } } }, new UnitConfig { Id = "accounts", Name = "Accounts" } ], agents); private static AgentConfig Agent(string id, string unit, params (string Tool, Dictionary Settings)[] tools) => new() { AgentId = id, UnitId = unit, Tools = tools.ToDictionary(t => t.Tool, t => t.Settings) }; [Fact] public void A_member_gets_the_nearest_units_credential_for_a_tool_it_has() { var buyer = Agent("buyer", "buying", ("DirectAPI", new() { ["baseUrl"] = "https://api.example" }), ("Mail", new())); var org = Org(buyer); org.ToolSettingsFor(buyer, "DirectAPI")["apiKey"].ShouldBe("buying-key", "the nearest unit first"); org.ToolSettingsFor(buyer, "DirectAPI")["baseUrl"].ShouldBe("https://api.example"); org.ToolSettingsFor(buyer, "Mail")["password"].ShouldBe("shop-mail", "from the unit above"); } [Fact] public void Its_own_value_wins_and_an_empty_one_is_filled() { var own = Agent("own", "shop", ("DirectAPI", new() { ["apiKey"] = "own-key" }), ("Mail", new() { ["password"] = "" })); var org = Org(own); org.ToolSettingsFor(own, "DirectAPI")["apiKey"].ShouldBe("own-key"); org.ToolSettingsFor(own, "Mail")["password"].ShouldBe("shop-mail"); } [Fact] public void No_credential_reaches_another_unit_or_a_tool_the_agent_lacks() { var clerk = Agent("clerk", "accounts", ("DirectAPI", new())); var buyer = Agent("buyer", "buying", ("FileRW", new())); var org = Org(clerk, buyer); org.ToolSettingsFor(clerk, "DirectAPI").ContainsKey("apiKey").ShouldBeFalse(); org.ToolSettingsFor(buyer, "DirectAPI").ShouldBeEmpty("a credential grants no tool"); } [Fact] public void A_template_never_carries_what_a_unit_holds() { var instance = new InstanceConfig(); instance.Units.Add(new UnitConfig { Id = "shop", Name = "Shop", Credentials = new() { ["DirectAPI"] = new() { ["apiKey"] = "shop-key" } } }); var template = OrgTemplates.Export(instance, "Shop", "", _ => null, DateTime.UtcNow); template.Units[0].Credentials.ShouldBeNull(); template.Credentials.ShouldContain(new CredentialNeed("unit:shop", "DirectAPI", "apiKey")); template.Units[0].Credentials = new() { ["DirectAPI"] = new() { ["apiKey"] = "smuggled" } }; OrgTemplates.Problems(template).ShouldContain(p => p.Contains("brings credentials")); } }