Changeset 0.37.14 (#226)

Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
This commit is contained in:
2026-03-23 16:47:48 +00:00
committed by xcaliber
parent fcb998bff9
commit b7746c3004
164 changed files with 6972 additions and 3527 deletions

View File

@@ -252,6 +252,10 @@ func setupHarness(t *testing.T) *testHarness {
permH := NewProfilePermissionsHandler(stores)
protected.GET("/profile/permissions", permH.GetMyPermissions)
// Boot payload (v0.37.15)
bootH := NewProfileBootstrapHandler(stores)
protected.GET("/profile/bootstrap", bootH.GetBootstrap)
// Profile / Settings
settings := NewSettingsHandler(stores, nil)
protected.GET("/profile", settings.GetProfile)
@@ -518,6 +522,10 @@ func (h *testHarness) registerUser(username, email, password string) (userID, to
var resp map[string]interface{}
decode(w, &resp)
token, _ = resp["access_token"].(string)
if token == "" {
h.t.Fatalf("register %s: no access_token in response (user inactive? default_user_active policy may not have committed): %s",
username, w.Body.String())
}
// Extract user_id from profile
w2 := h.request("GET", "/api/v1/profile", token, nil)
@@ -605,9 +613,9 @@ func TestIntegration_AdminListUsers(t *testing.T) {
}
var resp map[string]interface{}
decode(w, &resp)
users, ok := resp["users"].([]interface{})
users, ok := resp["data"].([]interface{})
if !ok {
t.Fatalf("response must have 'users' array, got %T", resp["users"])
t.Fatalf("response must have 'data' array, got %T", resp["data"])
}
if len(users) < 1 {
t.Fatal("should have at least 1 user (admin)")
@@ -906,10 +914,10 @@ func TestIntegration_TeamMemberManagement(t *testing.T) {
}
var usersResp map[string]interface{}
decode(w, &usersResp)
if _, ok := usersResp["users"]; !ok {
t.Fatal("admin/users response MUST have 'users' key (not 'data')")
if _, ok := usersResp["data"]; !ok {
t.Fatal("admin/users response MUST have 'data' key")
}
users := usersResp["users"].([]interface{})
users := usersResp["data"].([]interface{})
if len(users) < 2 {
t.Fatalf("expected at least 2 users (admin + alice), got %d", len(users))
}
@@ -1166,13 +1174,13 @@ func TestIntegration_AdminModelFetchEnableUserSees(t *testing.T) {
}
var adminResp map[string]interface{}
decode(w, &adminResp)
adminModels := adminResp["models"].([]interface{})
adminModels := adminResp["data"].([]interface{})
if len(adminModels) != 3 {
t.Fatalf("admin should see 3 disabled models, got %d", len(adminModels))
}
// Verify admin response is non-null array (not {"models": null})
if adminResp["models"] == nil {
// Verify admin response is non-null array (not {"data": null})
if adminResp["data"] == nil {
t.Fatal("admin models must be [] not null — causes frontend fallback chain to break")
}
@@ -1863,7 +1871,7 @@ func TestUserJourney_FullMatrix(t *testing.T) {
}
var resp map[string]interface{}
decode(w, &resp)
allModels := resp["models"].([]interface{})
allModels := resp["data"].([]interface{})
// Admin should see only global-scope models (3), not team(2) or BYOK(2)
if len(allModels) != 3 {
t.Fatalf("admin/models should show 3 global entries, got %d", len(allModels))
@@ -2004,12 +2012,8 @@ func TestIntegration_TeamRoles_CRUD(t *testing.T) {
}
var roleResp map[string]interface{}
decode(w, &roleResp)
roleData, _ := roleResp["data"].(map[string]interface{})
if roleData == nil {
roleData = map[string]interface{}{}
}
if len(roleData) != 0 {
t.Fatalf("team roles should be empty initially, got %d", len(roleData))
if len(roleResp) != 0 {
t.Fatalf("team roles should be empty initially, got %d", len(roleResp))
}
// Set team role override
@@ -2028,11 +2032,10 @@ func TestIntegration_TeamRoles_CRUD(t *testing.T) {
w = h.request("GET", fmt.Sprintf("/api/v1/teams/%s/roles", teamID), teamAdminToken, nil)
roleResp = map[string]interface{}{}
decode(w, &roleResp)
roleData, _ = roleResp["data"].(map[string]interface{})
if roleData == nil || len(roleData) == 0 {
if len(roleResp) == 0 {
t.Fatal("team roles should have utility after update")
}
if _, ok := roleData["utility"]; !ok {
if _, ok := roleResp["utility"]; !ok {
t.Fatal("team roles should have utility after update")
}
@@ -2046,11 +2049,8 @@ func TestIntegration_TeamRoles_CRUD(t *testing.T) {
w = h.request("GET", fmt.Sprintf("/api/v1/teams/%s/roles", teamID), teamAdminToken, nil)
roleResp = map[string]interface{}{}
decode(w, &roleResp)
roleData, _ = roleResp["data"].(map[string]interface{})
if roleData != nil {
if _, ok := roleData["utility"]; ok {
t.Fatal("team roles should not have utility after delete")
}
if _, ok := roleResp["utility"]; ok {
t.Fatal("team roles should not have utility after delete")
}
}
@@ -3749,7 +3749,7 @@ func TestIntegration_Messages_TreePath(t *testing.T) {
}
var pathEnv map[string]interface{}
decode(w, &pathEnv)
pathResp := pathEnv["messages"].([]interface{})
pathResp := pathEnv["data"].([]interface{})
if len(pathResp) != 3 {
t.Fatalf("expected 3 messages in path, got %d", len(pathResp))
}
@@ -4291,8 +4291,8 @@ func TestAudit_TeamProvidersEnvelope(t *testing.T) {
}
// ── H9: Team Roles Envelope ────────────────
// BUG: ListTeamRoles returns bare map, not wrapped in {"data": ...}.
// Roles endpoints return composite named-key maps (not lists).
// Convention: composite endpoints return object directly, no {"data": ...} wrapper.
func TestAudit_TeamRolesEnvelope(t *testing.T) {
h := setupHarness(t)
_, adminToken := h.createAdminUser("admin", "admin@test.com")
@@ -4318,10 +4318,10 @@ func TestAudit_TeamRolesEnvelope(t *testing.T) {
var resp map[string]interface{}
decode(w, &resp)
// Should be wrapped in {"data": {...}} per composite convention
if _, ok := resp["data"]; !ok {
t.Fatalf("H9 BUG: GET /teams/:teamId/roles should return 'data' key (composite convention), "+
"got keys: %v — bare map returned without wrapper", mapKeys(resp))
// Composite endpoint — must NOT have a "data" wrapper
if _, ok := resp["data"]; ok {
t.Fatalf("GET /teams/:teamId/roles should return named keys directly (composite convention), "+
"not wrapped in {\"data\": ...}")
}
}