Forge
csharp3407750f
1using System.Net.Http.Json;
2using System.Text.Json;
3
4namespace AgentForge.Tests;
5
6public sealed class ForgeOrgCatalogGroupStackViewTests
7{
8 private readonly ForgeWebApplicationFactory _factory = new() { PluginBundle = "ams-showcase" };
9 private readonly HttpClient _client;
10 private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true };
11
12 public ForgeOrgCatalogGroupStackViewTests()
13 {
14 const string bootstrap = "test-bootstrap-group-stack";
15 Environment.SetEnvironmentVariable("FORGE_BOOTSTRAP_TOKEN", bootstrap);
16 _client = _factory.CreateClient();
17 _client.DefaultRequestHeaders.Authorization = new("Bearer", bootstrap);
18 }
19
20 [Fact]
21 public async Task GroupStack_visibility_sections_repos_in_catalog_view()
22 {
23 await CreateOrg("groupstack-org", "Group Stack Org");
24 await CreateGroup("groupstack-org", "showcase", displayName: "Showcase");
25
26 await CreateRepo("groupstack-org", "public-repo", group: "showcase", visibility: "public", driveMode: "human-driven");
27 await CreateRepo("groupstack-org", "private-repo", group: "showcase", visibility: "private", driveMode: "agent-driven");
28
29 var view = await _client.GetStringAsync("/view/orgs/groupstack-org?group=showcase&groupStack=visibility");
30
31 Assert.Contains("class=\"meta org-catalog-toolbar org-catalog-grouping\"", view);
32 Assert.Contains("org-catalog-grouping-header", view);
33 Assert.Contains("Add level", view);
34 Assert.Contains("grouping-axis-0", view);
35 Assert.Contains("Created", view);
36 Assert.Contains("Organization", view);
37 Assert.Contains("groupStack=visibility", view);
38 Assert.Contains("view-group-section", view);
39 Assert.Contains("<strong>Public</strong>", view);
40 Assert.Contains("<strong>Private</strong>", view);
41 Assert.Contains("public-repo", view);
42 Assert.Contains("private-repo", view);
43 }
44
45 [Fact]
46 public async Task GroupStack_drive_mode_sections_repos()
47 {
48 await CreateOrg("drive-stack-org", "Drive Stack Org");
49 await CreateGroup("drive-stack-org", "labs", displayName: "Labs");
50
51 await CreateRepo("drive-stack-org", "agent-repo", group: "labs", visibility: "public", driveMode: "agent-driven");
52 await CreateRepo("drive-stack-org", "human-repo", group: "labs", visibility: "public", driveMode: "human-driven");
53
54 var view = await _client.GetStringAsync("/view/orgs/drive-stack-org?group=labs&groupStack=drive_mode");
55
56 Assert.Contains("view-group-section", view);
57 Assert.Contains("<strong>Agent-driven</strong>", view);
58 Assert.Contains("<strong>Human-driven</strong>", view);
59 Assert.Contains("agent-repo", view);
60 Assert.Contains("human-repo", view);
61 }
62
63 [Fact]
64 public async Task GroupStack_nested_visibility_then_drive_mode()
65 {
66 await CreateOrg("nested-org", "Nested Org");
67 await CreateGroup("nested-org", "showcase");
68
69 await CreateRepo("nested-org", "pub-agent", group: "showcase", visibility: "public", driveMode: "agent-driven");
70 await CreateRepo("nested-org", "pub-human", group: "showcase", visibility: "public", driveMode: "human-driven");
71 await CreateRepo("nested-org", "priv-agent", group: "showcase", visibility: "private", driveMode: "agent-driven");
72
73 var view = await _client.GetStringAsync(
74 "/view/orgs/nested-org?group=showcase&groupStack=visibility,drive_mode");
75
76 Assert.Contains("groupStack=visibility%2Cdrive_mode", view);
77 Assert.Contains("grouping-axis-1", view);
78 Assert.Contains("Then by", view);
79 Assert.Contains("<strong>Public</strong>", view);
80 Assert.Contains("<strong>Private</strong>", view);
81 Assert.Contains("<strong>Agent-driven</strong>", view);
82 Assert.Contains("<strong>Human-driven</strong>", view);
83 Assert.Contains("pub-agent", view);
84 Assert.Contains("pub-human", view);
85 Assert.Contains("priv-agent", view);
86 }
87
88 [Fact]
89 public async Task GroupStack_nested_axis_sections_are_dom_nested()
90 {
91 await CreateOrg("dom-nest-org", "Dom Nest Org");
92 await CreateGroup("dom-nest-org", "showcase");
93 await CreateRepo("dom-nest-org", "solo", group: "showcase", visibility: "public", driveMode: "human-driven");
94
95 var view = await _client.GetStringAsync(
96 "/view/orgs/dom-nest-org?group=showcase&groupStack=visibility,drive_mode");
97
98 var first = view.IndexOf("view-group-section", StringComparison.Ordinal);
99 Assert.True(first >= 0);
100 var second = view.IndexOf("view-group-section", first + 1, StringComparison.Ordinal);
101 Assert.True(second >= 0, "Expected nested grouping sections");
102 var firstClose = view.IndexOf("</details>", first, StringComparison.Ordinal);
103 Assert.True(second < firstClose, "Grouping axis sections must nest, not sit as flat siblings");
104 }
105
106 [Fact]
107 public async Task Invalid_groupStack_falls_back_to_flat_repo_list()
108 {
109 await CreateOrg("flat-org", "Flat Org");
110 await CreateGroup("flat-org", "showcase");
111 await CreateRepo("flat-org", "solo-repo", group: "showcase", visibility: "public", driveMode: "human-driven");
112
113 var view = await _client.GetStringAsync("/view/orgs/flat-org?group=showcase&groupStack=not-an-axis");
114
115 Assert.Contains("solo-repo", view);
116 Assert.DoesNotContain("<details class=\"view-group-section\"", view);
117 }
118
119 [Fact]
120 public async Task Global_home_view_uses_catalog_grouping_when_plugins_loaded()
121 {
122 await CreateOrg("global-home-org", "Global Home Org");
123 await CreateGroup("global-home-org", "showcase", displayName: "Showcase");
124 await CreateRepo("global-home-org", "global-public", group: "showcase", visibility: "public", driveMode: "human-driven");
125 await CreateRepo("global-home-org", "global-private", group: "showcase", visibility: "private", driveMode: "agent-driven");
126
127 var view = await _client.GetStringAsync("/view?group=showcase&groupStack=visibility");
128
129 Assert.Contains("class=\"meta org-catalog-toolbar org-catalog-grouping\"", view);
130 Assert.Contains("global-org-catalog", view);
131 Assert.Contains("Global Home Org", view);
132 Assert.Contains("global-public", view);
133 Assert.Contains("global-private", view);
134 Assert.Contains("<strong>Public</strong>", view);
135 Assert.Contains("<strong>Private</strong>", view);
136 }
137
138 private async Task CreateOrg(string slug, string displayName)
139 {
140 var response = await _client.PostAsJsonAsync(
141 "/api/v1/orgs",
142 new { slug, displayName },
143 new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
144 response.EnsureSuccessStatusCode();
145 }
146
147 private async Task CreateGroup(string org, string slug, string? displayName = null)
148 {
149 var response = await _client.PostAsJsonAsync(
150 $"/api/v1/orgs/{org}/repo-groups",
151 new { slug, displayName = displayName ?? slug },
152 new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
153 response.EnsureSuccessStatusCode();
154 }
155
156 private async Task CreateRepo(
157 string org,
158 string name,
159 string group,
160 string visibility,
161 string driveMode)
162 {
163 var response = await _client.PostAsJsonAsync(
164 "/api/v1/repos",
165 new { name, org, group, visibility, driveMode },
166 new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
167 response.EnsureSuccessStatusCode();
168 }
169}
170
View only · write via MCP/CIDE