mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-09 13:15:33 +00:00
132 lines
5.3 KiB
Go
132 lines
5.3 KiB
Go
package core
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Black-box coverage for the org-management POST endpoints (create/edit/links/
|
|
// members and join/leave). Each asserts the 303 redirect target and a cheap store
|
|
// side-effect; none render anything.
|
|
|
|
// #43 create, #44 edit, #104 links, #48 member role.
|
|
func TestOrgManagementPosts(t *testing.T) {
|
|
t.Parallel()
|
|
st, ctx, ts, h := splitServer(t)
|
|
_, sess := appLogin(t, ts, st, ctx, h.app, "orgadmin")
|
|
|
|
// #43 create → 303 to the new org's page.
|
|
create := post(t, ts, h.app, "/orgs", url.Values{"name": {"Test Org"}}, sess)
|
|
create.Body.Close()
|
|
loc, _ := url.Parse(create.Header.Get("Location"))
|
|
if create.StatusCode != http.StatusSeeOther || !strings.HasPrefix(loc.Path, "/orgs/") {
|
|
t.Fatalf("create org = %d %q, want 303 → /orgs/{slug}", create.StatusCode, create.Header.Get("Location"))
|
|
}
|
|
slug := strings.TrimPrefix(loc.Path, "/orgs/")
|
|
|
|
// #44 edit (renames the slug) → 303 to the new canonical URL.
|
|
edit := post(t, ts, h.app, "/orgs/"+slug+"/edit",
|
|
url.Values{"name": {"Renamed Org"}, "slug": {"renamed-org"}, "description": {"desc"}, "region": {"NA"}}, sess)
|
|
edit.Body.Close()
|
|
if loc, _ := url.Parse(edit.Header.Get("Location")); edit.StatusCode != http.StatusSeeOther || loc.Path != "/orgs/renamed-org" {
|
|
t.Fatalf("edit org = %d %q, want 303 → /orgs/renamed-org", edit.StatusCode, edit.Header.Get("Location"))
|
|
}
|
|
slug = "renamed-org"
|
|
|
|
// #104 links → 303 back to the org page.
|
|
links := post(t, ts, h.app, "/orgs/"+slug+"/links",
|
|
url.Values{"link_platform": {"website"}, "link_label": {"Home"}, "link_url": {"https://example.org"}}, sess)
|
|
links.Body.Close()
|
|
if loc, _ := url.Parse(links.Header.Get("Location")); links.StatusCode != http.StatusSeeOther || loc.Path != "/orgs/"+slug {
|
|
t.Fatalf("set org links = %d %q, want 303 → /orgs/%s", links.StatusCode, links.Header.Get("Location"), slug)
|
|
}
|
|
|
|
// #48 member role — promote a second member to admin.
|
|
orgID, ok := func() (int64, bool) { id, err := st.OrgIDBySlug(ctx, slug); return id, err == nil }()
|
|
if !ok {
|
|
t.Fatal("could not resolve renamed org slug")
|
|
}
|
|
other, err := st.CreateUser(ctx, "orgmember", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.AddOrgMember(ctx, orgID, other.ID, "member"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
promote := post(t, ts, h.app, "/orgs/"+slug+"/members/"+strconv.FormatInt(other.ID, 10),
|
|
url.Values{"action": {"promote"}}, sess)
|
|
promote.Body.Close()
|
|
if loc, _ := url.Parse(promote.Header.Get("Location")); promote.StatusCode != http.StatusSeeOther || loc.Path != "/orgs/"+slug+"/members" {
|
|
t.Fatalf("promote member = %d %q, want 303 → members", promote.StatusCode, promote.Header.Get("Location"))
|
|
}
|
|
if admin, _ := st.IsOrgAdmin(ctx, orgID, other.ID); !admin {
|
|
t.Fatal("promote did not make the member an admin")
|
|
}
|
|
}
|
|
|
|
// #56 update config profile, #57 delete config profile. (Create #54 and regions
|
|
// #59 are covered by TestOrgConfigProfilesFlow.)
|
|
func TestOrgConfigProfileUpdateDelete(t *testing.T) {
|
|
t.Parallel()
|
|
st, ctx, ts, h := splitServer(t)
|
|
owner, sess := appLogin(t, ts, st, ctx, h.app, "cfgadmin")
|
|
org, err := st.CreateOrg(ctx, "Cfg Org", owner.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pid, err := st.CreateProfile(ctx, org.ID, "ESP32", nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base := "/orgs/" + org.Slug + "/config"
|
|
|
|
upd := post(t, ts, h.app, base+"/profiles/"+strconv.FormatInt(pid, 10),
|
|
url.Values{"profile_name": {"Heltec"}, "profile_steps": {"# base"}}, sess)
|
|
upd.Body.Close()
|
|
if loc, _ := url.Parse(upd.Header.Get("Location")); upd.StatusCode != http.StatusSeeOther || loc.Path != base+"/edit" {
|
|
t.Fatalf("update profile = %d %q, want 303 → config/edit", upd.StatusCode, upd.Header.Get("Location"))
|
|
}
|
|
|
|
del := post(t, ts, h.app, base+"/profiles/"+strconv.FormatInt(pid, 10)+"/delete", url.Values{}, sess)
|
|
del.Body.Close()
|
|
if loc, _ := url.Parse(del.Header.Get("Location")); del.StatusCode != http.StatusSeeOther || loc.Path != base+"/edit" {
|
|
t.Fatalf("delete profile = %d %q, want 303 → config/edit", del.StatusCode, del.Header.Get("Location"))
|
|
}
|
|
}
|
|
|
|
// #46 join, #47 leave — against an org owned by someone else.
|
|
func TestOrgJoinLeavePosts(t *testing.T) {
|
|
t.Parallel()
|
|
st, ctx, ts, h := splitServer(t)
|
|
owner, err := st.CreateUser(ctx, "otherowner", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
org, err := st.CreateOrg(ctx, "Joinable Org", owner.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
joiner, sess := appLogin(t, ts, st, ctx, h.app, "joiner")
|
|
|
|
join := post(t, ts, h.app, "/orgs/"+org.Slug+"/join", url.Values{}, sess)
|
|
join.Body.Close()
|
|
if loc, _ := url.Parse(join.Header.Get("Location")); join.StatusCode != http.StatusSeeOther || loc.Path != "/orgs/"+org.Slug {
|
|
t.Fatalf("join = %d %q, want 303 → /orgs/%s", join.StatusCode, join.Header.Get("Location"), org.Slug)
|
|
}
|
|
if _, isMember, _ := st.OrgRole(ctx, org.ID, joiner.ID); !isMember {
|
|
t.Fatal("join did not add membership")
|
|
}
|
|
|
|
leave := post(t, ts, h.app, "/orgs/"+org.Slug+"/leave", url.Values{}, sess)
|
|
leave.Body.Close()
|
|
if loc, _ := url.Parse(leave.Header.Get("Location")); leave.StatusCode != http.StatusSeeOther || loc.Path != "/orgs" {
|
|
t.Fatalf("leave = %d %q, want 303 → /orgs", leave.StatusCode, leave.Header.Get("Location"))
|
|
}
|
|
if _, isMember, _ := st.OrgRole(ctx, org.ID, joiner.ID); isMember {
|
|
t.Fatal("leave did not remove membership")
|
|
}
|
|
}
|