| 1 | package callstest |
| 2 | |
| 3 | import ( |
| 4 | "net/http" |
| 5 | "net/url" |
| 6 | "strings" |
| 7 | "testing" |
| 8 | "time" |
| 9 | ) |
| 10 | |
| 11 | func enableWaitingRoom(t *testing.T, x *harness, sqid string) { |
| 12 | t.Helper() |
| 13 | c := x.browser(t) |
| 14 | c.signIn("paul@example.com") |
| 15 | if r := c.post("/meetings/"+sqid+"/settings", url.Values{"waiting_room": {"on"}}); r.Code != 303 { |
| 16 | t.Fatal(r.Code, r.Body.String()) |
| 17 | } |
| 18 | } |
| 19 | |
| 20 | func TestMeetingDefaultsAndIndependentOverrides(t *testing.T) { |
| 21 | x := newHarness(t, "paul@example.com") |
| 22 | c := x.browser(t) |
| 23 | c.signIn("paul@example.com") |
| 24 | old, _ := createMeeting(t, c, "Original", "") |
| 25 | if r := c.post("/settings/defaults", url.Values{"require_signin": {"on"}, "waiting_room": {"on"}}); r.Code != 303 { |
| 26 | t.Fatal(r.Code) |
| 27 | } |
| 28 | next, token := createMeeting(t, c, "Restricted", "") |
| 29 | if r := x.browser(t).get("/j/" + token); r.Code != 303 || r.Header().Get("Location") != "/signin" { |
| 30 | t.Fatal("sign-in not required", r.Code) |
| 31 | } |
| 32 | for _, tc := range []struct { |
| 33 | sqid string |
| 34 | checked bool |
| 35 | }{{old, false}, {next, true}} { |
| 36 | r := c.get("/meetings/" + tc.sqid + "/settings") |
| 37 | if r.Code != 200 || strings.Contains(r.Body.String(), " checked") != tc.checked { |
| 38 | t.Fatal("settings not retained", r.Body.String()) |
| 39 | } |
| 40 | } |
| 41 | if r := c.post("/meetings/"+next+"/settings", url.Values{}); r.Code != 303 { |
| 42 | t.Fatal(r.Code) |
| 43 | } |
| 44 | if r := x.browser(t).get("/j/" + token); r.Code != 200 { |
| 45 | t.Fatal("guest override not applied", r.Code) |
| 46 | } |
| 47 | if r := c.get("/settings"); !strings.Contains(r.Body.String(), " checked") { |
| 48 | t.Fatal("override altered defaults") |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | func TestAnonymousGuestJoinsWithoutMemberPowers(t *testing.T) { |
| 53 | x, sqid, token := memberWithMeeting(t) |
| 54 | g := x.browser(t) |
| 55 | r := g.get("/j/" + token) |
| 56 | if r.Code != 200 || !strings.Contains(r.Body.String(), "oficina-calls://join?") { |
| 57 | t.Fatal("missing guest/app choice", r.Code) |
| 58 | } |
| 59 | if r := g.post("/j/"+token+"/guest", url.Values{"name": {" "}}); r.Code != 422 { |
| 60 | t.Fatal("empty name accepted") |
| 61 | } |
| 62 | if r := g.post("/j/"+token+"/guest", url.Values{"name": {"Visitor"}}); r.Code != 303 { |
| 63 | t.Fatal(r.Code, r.Body.String()) |
| 64 | } |
| 65 | if r := g.get("/m/" + sqid + "/join"); r.Code != 200 || !strings.Contains(r.Body.String(), "Visitor") { |
| 66 | t.Fatal("guest holding", r.Code, r.Body.String()) |
| 67 | } |
| 68 | for _, path := range []string{"/settings", "/meetings/" + sqid + "/settings", "/"} { |
| 69 | if r := g.get(path); r.Code != 404 { |
| 70 | t.Fatal("guest can reach", path, r.Code) |
| 71 | } |
| 72 | } |
| 73 | if r := g.post("/meetings/"+sqid+"/settings", url.Values{"waiting_room": {"on"}}); r.Code != 404 { |
| 74 | t.Fatal("guest can change settings") |
| 75 | } |
| 76 | s := g.stream("/m/" + sqid + "/stream") |
| 77 | defer s.close() |
| 78 | peer, _ := s.hello() |
| 79 | if res, b := g.signal(sqid, peer, `{"type":"join"}`); res.StatusCode != http.StatusOK { |
| 80 | t.Fatal(res.StatusCode, string(b)) |
| 81 | } |
| 82 | if _, data, ok := s.nextOf("roster", 2*time.Second); !ok || !strings.Contains(string(data), `"kind":"guest"`) { |
| 83 | t.Fatal("guest not directly admitted", string(data)) |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | func TestAnonymousEntryRefusesRestrictedAndRevokedInvitations(t *testing.T) { |
| 88 | x, sqid, token := memberWithMeeting(t) |
| 89 | m := x.browser(t) |
| 90 | m.signIn("paul@example.com") |
| 91 | g := x.browser(t) |
| 92 | m.post("/meetings/"+sqid+"/settings", url.Values{"require_signin": {"on"}}) |
| 93 | if r := g.post("/j/"+token+"/guest", url.Values{"name": {"Visitor"}}); r.Code != 303 || r.Header().Get("Location") != "/signin" { |
| 94 | t.Fatal("restricted guest POST accepted") |
| 95 | } |
| 96 | m.post("/meetings/"+sqid+"/settings", url.Values{}) |
| 97 | g.post("/j/"+token+"/guest", url.Values{"name": {"Visitor"}}) |
| 98 | m.post("/meetings/"+sqid+"/settings", url.Values{"require_signin": {"on"}}) |
| 99 | if r := g.get("/m/" + sqid + "/join"); r.Code != 404 { |
| 100 | t.Fatal("guest bypassed new sign-in requirement") |
| 101 | } |
| 102 | m.post("/meetings/"+sqid+"/links", nil) |
| 103 | if r := x.browser(t).post("/j/"+token+"/guest", url.Values{"name": {"Visitor"}}); r.Code != 404 { |
| 104 | t.Fatal("revoked invitation accepted") |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | func TestTurningOffWaitingRoomAdmitsQueuedAnonymousGuest(t *testing.T) { |
| 109 | x, sqid, token := memberWithMeeting(t) |
| 110 | enableWaitingRoom(t, x, sqid) |
| 111 | g := x.browser(t) |
| 112 | if r := g.post("/j/"+token+"/guest", url.Values{"name": {"Waiting visitor"}}); r.Code != 303 { |
| 113 | t.Fatal(r.Code) |
| 114 | } |
| 115 | s := g.stream("/m/" + sqid + "/stream") |
| 116 | defer s.close() |
| 117 | peer, _ := s.hello() |
| 118 | if r, b := g.signal(sqid, peer, `{"type":"join"}`); r.StatusCode != 200 { |
| 119 | t.Fatal(r.StatusCode, string(b)) |
| 120 | } |
| 121 | if r := g.getStream("/m/" + sqid + "/ice?peer=" + peer); r.Code != 404 { |
| 122 | t.Fatal("waiting guest admitted prematurely", r.Code) |
| 123 | } |
| 124 | member := x.browser(t) |
| 125 | member.signIn("paul@example.com") |
| 126 | if r := member.post("/meetings/"+sqid+"/settings", url.Values{}); r.Code != 303 { |
| 127 | t.Fatal(r.Code) |
| 128 | } |
| 129 | if _, _, ok := s.nextOf("roster", 2*time.Second); !ok { |
| 130 | t.Fatal("queued guest not admitted after disabling waiting room") |
| 131 | } |
| 132 | } |
| 133 | |