package cabana_test import ( "net/http" "testing" "git.golem15.com/golem15/summercms/modules/cabana" ) // TestActionExtensionRun (D-14, D-15): a contributed record action on the // Administrators screen is offered and runs only for principals holding both // the screen's and the action's permissions, answers 409 once it no longer // applies, and runs in the row-locked transaction with the record. func TestActionExtensionRun(t *testing.T) { env := newScreensEnv(t) both := env.seedAdmin(t, adminSeed{Name: "ext-both", RolePermissions: map[string]int{cabana.PermissionManageUsers: 1, pingPermission: 1}}) bothToken := env.login(t, both.Login) screenOnly := env.seedAdmin(t, adminSeed{Name: "ext-screen", RolePermissions: manageUsers}) screenToken := env.login(t, screenOnly.Login) actionOnly := env.seedAdmin(t, adminSeed{Name: "ext-action", RolePermissions: map[string]int{pingPermission: 1}}) actionToken := env.login(t, actionOnly.Login) target := env.seedAdmin(t, adminSeed{Name: "ext-target"}) ping := userURL(target.ID, "actions", "acme_ping") names := recordActionNames(env.expect(t, http.StatusOK, http.MethodGet, userURL(target.ID), bothToken, nil)) if len(names) == 0 || names[len(names)-1] != "acme_ping" || names[0] != "deactivate" { t.Fatalf("offered to the holder of both = %v, want the owner's actions then acme_ping", names) } if names := recordActionNames(env.expect(t, http.StatusOK, http.MethodGet, userURL(target.ID), screenToken, nil)); containsName(names, "acme_ping") { t.Fatalf("offered without %s = %v", pingPermission, names) } env.expectRefusal(t, http.StatusForbidden, http.MethodPost, ping, screenToken, map[string]any{}, "") env.expectRefusal(t, http.StatusForbidden, http.MethodGet, userURL(target.ID), actionToken, nil, "") env.expectRefusal(t, http.StatusForbidden, http.MethodPost, ping, actionToken, map[string]any{}, "") if runs, _ := env.spy.runs(); len(runs) != 0 { t.Fatalf("refused requests ran the action: %v", runs) } // Shown while it applies, then Applies turns false before the POST. env.spy.refuse.Store(true) if rec := env.call(t, http.MethodPost, ping, bothToken, map[string]any{}); rec.Code != http.StatusConflict { t.Fatalf("POST after Applies turned false status=%d body=%s", rec.Code, rec.Body.String()) } if names := recordActionNames(env.expect(t, http.StatusOK, http.MethodGet, userURL(target.ID), bothToken, nil)); containsName(names, "acme_ping") { t.Fatalf("offered while it does not apply: %v", names) } env.spy.refuse.Store(false) if runs, _ := env.spy.runs(); len(runs) != 0 { t.Fatalf("a 409 ran the action: %v", runs) } result := dataOf(env.expect(t, http.StatusOK, http.MethodPost, ping, bothToken, map[string]any{})) if result["message"] != "Pinged" { t.Fatalf("result = %v", result) } runs, withTx := env.spy.runs() if len(runs) != 1 || runs[0] != uint64(target.ID) || !withTx[0] { t.Fatalf("runs = %v tx = %v, want one run on %d inside the transaction", runs, withTx, target.ID) } // A record the screen hides is not found for the action either. hidden := env.seedAdmin(t, adminSeed{Name: "ext-hidden", Superuser: true}) env.expectRefusal(t, http.StatusNotFound, http.MethodPost, userURL(hidden.ID, "actions", "acme_ping"), bothToken, map[string]any{}, "") }