using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Microsoft.Extensions.DependencyInjection;
using TeamUp.Modules.Assembler.Queue;
using TeamUp.Modules.Assembler.Runtime;
using TeamUp.Modules.Skills.Domain;
using TeamUp.Modules.Skills.Indexing;
using Xunit;
namespace TeamUp.IntegrationTests;
///
/// Closes the library → delivery loop: when an AI seat runs a task, the skill bodies assembled into
/// its prompt are resolved within the org's namespace only — the org's own (authored/forked) skill is
/// preferred over the shared builtin of the same key, and another org's same-key skill never leaks in.
///
public sealed class SkillRunScopingTests(PostgresFixture postgres) : IClassFixture
{
private const string OrgABody = "ACME_ORG_A_CUSTOM_IMPL_BODY — our house implementation steps.";
private const string OrgBPoison = "ORG_B_POISON_BODY_MUST_NEVER_APPEAR_IN_ANOTHER_ORGS_PROMPT.";
private sealed record BootstrapResponse(string Token, Guid MemberId, Guid OrganizationId);
private sealed record IdResponse(Guid Id);
private sealed record TeamResponse(Guid Id, Guid OrganizationId, string Name);
private sealed record SeatResponse(Guid Id, Guid TeamId, string RoleName, string State, Guid? MemberId, Guid? AgentId);
private sealed record TaskResponse(Guid Id);
private sealed record SyncResult(int Indexed);
private sealed record RunResponse(
Guid Id, Guid SeatId, Guid WorkItemId, Guid? AgentId, string Status,
string? ActionType, string? ActionRisk, string? Prompt, string? Output, string? Error);
[Fact]
public async Task A_run_assembles_the_orgs_own_skill_over_builtin_and_never_another_orgs()
{
var settings = new Dictionary
{
["GitSource:Provider"] = "filesystem",
["GitSource:Root"] = LocateSkillsDirectory(),
};
await using var factory = new TeamUpWebFactory(postgres.ConnectionString, settings);
using var anon = factory.CreateClient();
var owner = await PostOk(anon, "/api/identity/bootstrap", new
{
organizationName = "OrgA",
ownerEmail = "owner@alia.test",
ownerDisplayName = "Owner",
ownerPassword = "Passw0rd!",
});
using var client = Authed(factory, owner.Token);
await client.PostAsJsonAsync("/api/orgboard/organizations", new { organizationId = owner.OrganizationId, name = "OrgA" });
var team = await PostOk(client, "/api/orgboard/teams", new { organizationId = owner.OrganizationId, name = "IPNOPS" });
var config = await PostOk(client, "/api/integrations/api-configs", new
{
organizationId = owner.OrganizationId,
name = "Stub",
provider = "stub",
model = "gemini-pro",
apiKey = "sk-demo-key",
});
// The shared builtin "code-implementation" exists (Published) after a sync.
var sync = await PostOk(client, "/api/skills/sync", new { });
Assert.True(sync.Indexed >= 8);
// Org A authors its OWN "code-implementation" (same key as the builtin) with a distinctive body.
await PostOk