using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Xunit;
namespace TeamUp.IntegrationTests;
///
/// The dynamic per-company skill library: an org authors a skill, versions it, and forks a builtin —
/// all org-scoped (own + shared builtins visible, gated by ManageSkills), with the publish gate intact.
///
public sealed class SkillLibraryTests(PostgresFixture postgres) : IClassFixture
{
private const string BuiltinSkill =
"""
---
id: spec-writing
name: Spec Writing
version: 1.0.0
summary: Turn a request into a spec.
roles: [product-owner]
actions:
- name: write-spec
risk: draft
golden_tests:
- input: "Add a logout button"
expected: "A logout button in the header that ends the session."
---
# Spec Writing
Write a clear, testable spec.
""";
private sealed record BootstrapResponse(string Token, Guid MemberId, Guid OrganizationId);
private sealed record AuthResponse(string Token, Guid MemberId);
private sealed record InviteResponse(Guid InvitationId, string Token);
private sealed record ActionDto(string Name, string Risk, string? Description);
private sealed record GoldenTestDto(string Input, string Expected);
private sealed record SkillSummary(
string SkillKey, string Name, string Version, string? Summary, List Roles,
string Visibility, string MinTier, string Status, string Origin, Guid? OrganizationId,
int GoldenTestCount, List Actions);
private sealed record SkillDetail(
SkillSummary Skill, string? Inputs, string? Outputs, List Tools,
List Context, List GoldenTests, string Body);
[Fact]
public async Task Org_authors_versions_and_forks_skills_scoped_to_itself()
{
await using var factory = new TeamUpWebFactory(postgres.ConnectionString);
using var anon = factory.CreateClient();
var owner = await PostOk(anon, "/api/identity/bootstrap", new
{
organizationName = "AliaSaaS",
ownerEmail = "owner@alia.test",
ownerDisplayName = "Owner",
ownerPassword = "Passw0rd!",
});
using var client = Authed(factory, owner.Token);
client.DefaultRequestHeaders.Add("X-Skills-Admin-Key", TeamUpWebFactory.PlatformAdminKey);
// A builtin exists in the shared (null-org) namespace.
await PostOk(client, "/api/skills/index", new { content = BuiltinSkill });
// The org authors its own skill. Roles + a golden test → Published.
var authored = await PostOk(client, "/api/skills/authored", new
{
organizationId = owner.OrganizationId,
skillKey = "api-design",
name = "API Design",
version = "1.0.0",
summary = "Design an endpoint.",
roles = new[] { "engineer" },
inputs = "A story.",
outputs = "Route + shapes.",
actions = new[] { new { name = "write-design", risk = "draft", description = "Emit a design." } },
tools = Array.Empty(),
context = Array.Empty(),
visibility = "private",
minTier = "free",
body = "You are the engineer. Design the endpoint.",
goldenTests = new[] { new { input = "Delete own comment", expected = "DELETE /comments/{id} 204/403/404" } },
});
Assert.Equal("Published", authored.Skill.Status);
Assert.Equal("Authored", authored.Skill.Origin);
Assert.Equal(owner.OrganizationId, authored.Skill.OrganizationId);
// Bump the version → a new row; both coexist.
await PostOk(client, "/api/skills/authored", new
{
organizationId = owner.OrganizationId,
skillKey = "api-design",
name = "API Design",
version = "1.1.0",
summary = "Design an endpoint (v2).",
roles = new[] { "engineer" },
inputs = (string?)null,
outputs = (string?)null,
actions = Array.Empty