Enhance Discord role management by adding hoist functionality
This commit introduces a new `hoist` attribute for Discord roles, allowing team roles and the registered role to be displayed in separate groups within the Discord member list. The `RoleWorker` has been updated to ensure team roles are positioned above the registered role, improving visibility and organization. Database schema changes have been made to support the new `hoist` field, and corresponding updates have been implemented in the service layer and tests to validate the new behavior.
This commit is contained in:
@@ -125,6 +125,8 @@ func (s *roleStoreFake) GetDiscordRoleRegistration(_ context.Context, eventID st
|
||||
func TestRoleWorkerReconcilesIdempotentlyAndRenames(t *testing.T) {
|
||||
var mu sync.Mutex
|
||||
roleNames := make(map[string]string)
|
||||
roleHoists := make(map[string]bool)
|
||||
rolePositions := map[string]int{"role-rsvp": 1}
|
||||
methods := make([]string, 0)
|
||||
nextID := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
@@ -137,15 +139,38 @@ func TestRoleWorkerReconcilesIdempotentlyAndRenames(t *testing.T) {
|
||||
nextID++
|
||||
id := "role-" + string(rune('0'+nextID))
|
||||
roleNames[id], _ = payload["name"].(string)
|
||||
roleHoists[id], _ = payload["hoist"].(bool)
|
||||
rolePositions[id] = 0
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(response).Encode(map[string]string{"id": id})
|
||||
return
|
||||
}
|
||||
if request.Method == http.MethodGet && strings.HasSuffix(request.URL.Path, "/roles") {
|
||||
roles := []map[string]any{{"id": "role-rsvp", "position": rolePositions["role-rsvp"]}}
|
||||
for id := range roleNames {
|
||||
roles = append(roles, map[string]any{"id": id, "position": rolePositions[id]})
|
||||
}
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(response).Encode(roles)
|
||||
return
|
||||
}
|
||||
if request.Method == http.MethodPatch {
|
||||
var payload map[string]string
|
||||
_ = json.NewDecoder(request.Body).Decode(&payload)
|
||||
parts := strings.Split(request.URL.Path, "/")
|
||||
roleNames[parts[len(parts)-1]] = payload["name"]
|
||||
roleID := parts[len(parts)-1]
|
||||
if roleID == "roles" {
|
||||
var payload []struct {
|
||||
ID string `json:"id"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
_ = json.NewDecoder(request.Body).Decode(&payload)
|
||||
for _, item := range payload {
|
||||
rolePositions[item.ID] = item.Position
|
||||
}
|
||||
} else {
|
||||
var payload map[string]any
|
||||
_ = json.NewDecoder(request.Body).Decode(&payload)
|
||||
roleNames[roleID], _ = payload["name"].(string)
|
||||
}
|
||||
}
|
||||
response.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
@@ -157,7 +182,7 @@ func TestRoleWorkerReconcilesIdempotentlyAndRenames(t *testing.T) {
|
||||
store.active = []application.DiscordRoleRoster{item}
|
||||
rsvpRole := application.DiscordManagedRole{
|
||||
Scope: application.DiscordRoleScopeEvent, EventID: "event-1",
|
||||
Kind: application.DiscordRoleKindRegistered, DiscordRoleID: "role-rsvp", RoleName: "Зарегистрирован: Mix",
|
||||
Kind: application.DiscordRoleKindRegistered, DiscordRoleID: "role-rsvp", RoleName: "Зарегистрирован: Mix", Hoist: true,
|
||||
}
|
||||
store.roles[managedRoleKey(rsvpRole)] = rsvpRole
|
||||
store.jobs = []application.DiscordRoleSyncJob{{ID: 1, EventID: "event-1", Action: application.DiscordRoleActionReconcile}}
|
||||
@@ -179,6 +204,14 @@ func TestRoleWorkerReconcilesIdempotentlyAndRenames(t *testing.T) {
|
||||
if strings.Join(names, ",") != strings.Join(expectedNames, ",") {
|
||||
t.Fatalf("unexpected role names: %v", names)
|
||||
}
|
||||
for id, name := range roleNames {
|
||||
if roleHoists[id] != (name == "Alpha") {
|
||||
t.Fatalf("unexpected hoist for %s: %v", name, roleHoists[id])
|
||||
}
|
||||
if name == "Alpha" && rolePositions[id] <= rolePositions["role-rsvp"] {
|
||||
t.Fatalf("team role was not moved above registered role: team=%d registered=%d", rolePositions[id], rolePositions["role-rsvp"])
|
||||
}
|
||||
}
|
||||
if got := assignmentCount(store.assignments); got != 11 {
|
||||
t.Fatalf("expected 11 managed assignments, got %d", got)
|
||||
}
|
||||
@@ -191,8 +224,10 @@ func TestRoleWorkerReconcilesIdempotentlyAndRenames(t *testing.T) {
|
||||
if _, err = worker.ProcessNext(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(methods) != firstRequestCount {
|
||||
t.Fatalf("idempotent reconcile made %d extra Discord calls", len(methods)-firstRequestCount)
|
||||
for _, method := range methods[firstRequestCount:] {
|
||||
if !strings.HasPrefix(method, http.MethodGet+" ") {
|
||||
t.Fatalf("idempotent reconcile made a mutating Discord call: %s", method)
|
||||
}
|
||||
}
|
||||
|
||||
renamed := testDiscordRoster("Night Owls")
|
||||
@@ -236,6 +271,13 @@ func TestRoleWorkerReconcilesRSVPStatusAndEventRename(t *testing.T) {
|
||||
patches := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
if request.Method == http.MethodPost {
|
||||
var payload map[string]any
|
||||
_ = json.NewDecoder(request.Body).Decode(&payload)
|
||||
name, _ := payload["name"].(string)
|
||||
hoist, _ := payload["hoist"].(bool)
|
||||
if hoist != strings.HasPrefix(name, "Зарегистрирован:") {
|
||||
t.Errorf("unexpected RSVP role hoist: name=%q hoist=%v", name, hoist)
|
||||
}
|
||||
nextID++
|
||||
_ = json.NewEncoder(response).Encode(map[string]string{"id": "rsvp-role-" + string(rune('0'+nextID))})
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user