4416d99360
The core product thesis made tangible beyond PO/QA:
- Four new golden-tested skill atoms in skills/: code-implementation + bug-diagnosis
(engineer — output is a reviewable patch/diagnosis artifact; Git write-back stays Phase 2),
ui-design-spec (designer), requirements-analysis (analyst, also tagged product-owner).
The catalogue now spans five roles with eight atoms.
- Seat configurator: SuggestedSkills — maps the seat's free-text role name to skill role
tags and offers the matching set one click ("Use set"). Any role name → staffed with AI.
- AnyRoleSeatTests: an "Backend Engineer" seat (Edison, gated) runs the same pipeline —
skills assemble, implement-code/Draft parsed, proposal held in the review inbox like any
governed action. SkillSyncTests updated for the larger catalogue.
Verified: IntegrationTests 44/44, client build green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
3.1 KiB
C#
78 lines
3.1 KiB
C#
using System.Net;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using Xunit;
|
|
|
|
namespace TeamUp.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// M2 acceptance: syncing from the Git source (the repo's skills/ dir via the filesystem provider)
|
|
/// indexes the four V1 atoms, published and queryable by their role.
|
|
/// </summary>
|
|
public sealed class SkillSyncTests(PostgresFixture postgres) : IClassFixture<PostgresFixture>
|
|
{
|
|
private sealed record BootstrapResponse(string Token, Guid MemberId, Guid OrganizationId);
|
|
|
|
private sealed record SyncResult(int Indexed);
|
|
|
|
private sealed record ActionDto(string Name, string Risk);
|
|
|
|
private sealed record SkillSummary(
|
|
string SkillKey, string Name, string Version, string? Summary, List<string> Roles,
|
|
string Visibility, string MinTier, string Status, List<ActionDto> Actions);
|
|
|
|
[Fact]
|
|
public async Task Sync_indexes_the_four_atoms_queryable_by_role()
|
|
{
|
|
var settings = new Dictionary<string, string?>
|
|
{
|
|
["GitSource:Provider"] = "filesystem",
|
|
["GitSource:Root"] = LocateSkillsDirectory(),
|
|
};
|
|
|
|
await using var factory = new TeamUpWebFactory(postgres.ConnectionString, settings);
|
|
using var anon = factory.CreateClient();
|
|
|
|
var bootstrap = await anon.PostAsJsonAsync("/api/identity/bootstrap", new
|
|
{
|
|
organizationName = "AliaSaaS",
|
|
ownerEmail = "owner@alia.test",
|
|
ownerDisplayName = "Owner",
|
|
ownerPassword = "Passw0rd!",
|
|
});
|
|
var owner = await bootstrap.Content.ReadFromJsonAsync<BootstrapResponse>();
|
|
Assert.NotNull(owner);
|
|
|
|
using var client = factory.CreateClient();
|
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", owner!.Token);
|
|
|
|
var syncResponse = await client.PostAsync("/api/skills/sync", content: null);
|
|
Assert.Equal(HttpStatusCode.OK, syncResponse.StatusCode);
|
|
var result = await syncResponse.Content.ReadFromJsonAsync<SyncResult>();
|
|
Assert.True(result!.Indexed >= 8, $"expected all atoms indexed, got {result.Indexed}");
|
|
|
|
var productOwner = await client.GetFromJsonAsync<List<SkillSummary>>("/api/skills/?role=product-owner");
|
|
Assert.Contains(productOwner!, s => s.SkillKey == "spec-writing");
|
|
Assert.Contains(productOwner!, s => s.SkillKey == "story-breakdown");
|
|
Assert.All(productOwner!, s => Assert.Equal("Published", s.Status));
|
|
|
|
var qa = await client.GetFromJsonAsync<List<SkillSummary>>("/api/skills/?role=qa");
|
|
Assert.Contains(qa!, s => s.SkillKey == "test-plan-generation");
|
|
Assert.Contains(qa!, s => s.SkillKey == "diff-review");
|
|
}
|
|
|
|
private static string LocateSkillsDirectory()
|
|
{
|
|
var dir = new DirectoryInfo(AppContext.BaseDirectory);
|
|
while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "TeamUp.slnx")))
|
|
{
|
|
dir = dir.Parent;
|
|
}
|
|
|
|
Assert.NotNull(dir);
|
|
var skills = Path.Combine(dir!.FullName, "skills");
|
|
Assert.True(Directory.Exists(skills), $"skills directory not found at {skills}");
|
|
return skills;
|
|
}
|
|
}
|