package lighthouse import ( "context" "math" "reflect" "testing" ) // TestChannelIDMatchesPHP pins ChannelID to PHP's (int) cast of the id // segment. The expected values were printed by PHP 8.5.10: // // php -r 'foreach ([...] as $s) var_dump((int)$s);' func TestChannelIDMatchesPHP(t *testing.T) { cases := []struct { segment string want int64 }{ {"5", 5}, {"05", 5}, {" 5", 5}, {"5abc", 5}, {"abc", 0}, {"-3", -3}, {"", 0}, {"1e3", 1000}, {"0x1A", 0}, {"9999999999999999999", math.MaxInt64}, {"\t5", 5}, {"\n5", 5}, {"5 ", 5}, {"+5", 5}, {" -7x", -7}, {"1.9", 1}, {"-0", 0}, {"1e20", math.MaxInt64}, {"0b11", 0}, {"٥", 0}, {" ", 0}, {"-9999999999999999999", math.MinInt64}, {"-1e20", math.MinInt64}, {"1e-3", 0}, {".5", 0}, {"5.", 5}, {"1e", 1}, {"1e+2", 100}, {"\v5", 5}, {"\f5", 5}, {"\r5", 5}, {"NaN", 0}, {"INF", 0}, {"1E3", 1000}, {"9223372036854775807", math.MaxInt64}, {"9223372036854775808", math.MaxInt64}, {"1.5e3", 1500}, {"-.5e1", -5}, {"0005", 5}, {"00", 0}, {"- 5", 0}, {"5e3abc", 5000}, {"12.7e1x", 127}, {"1e400", 0}, } for _, tc := range cases { if got := ChannelID("collection:" + tc.segment); got != tc.want { t.Errorf("ChannelID(collection:%q) = %d, want %d", tc.segment, got, tc.want) } } if got := ChannelID("collection"); got != 0 { t.Errorf("missing segment = %d, want 0", got) } if got := ChannelID("presence:collection:5"); got != 0 { t.Errorf("presence:collection:5 = %d, want 0 (segment 1 is \"collection\")", got) } if got := ChannelID("wishlist:12:extra"); got != 12 { t.Errorf("wishlist:12:extra = %d, want 12", got) } } func TestParseChannel(t *testing.T) { cases := []struct { channel string namespace string presence bool }{ {"collection:5", "collection", false}, {"ns:entity:id", "ns", false}, {"presence:acme:room:1", "acme", true}, {"presence:collection:5", "collection", true}, {"presence:presence:x", "", true}, {"a:b:c:d", "", false}, {"presence:a:b:c:d", "", true}, {"", "", false}, {":5", "", false}, {"Collection:5", "Collection", false}, } for _, tc := range cases { ns, presence := ParseChannel(tc.channel) if ns != tc.namespace || presence != tc.presence { t.Errorf("ParseChannel(%q) = (%q, %t), want (%q, %t)", tc.channel, ns, presence, tc.namespace, tc.presence) } } } func TestFormatChannels(t *testing.T) { got := FormatChannels("Acme", []string{"Collection:5", "acme:user:1", "ACME:x"}) want := []string{"acme:collection:5", "acme:user:1", "acme:x"} if !reflect.DeepEqual(got, want) { t.Fatalf("FormatChannels = %v, want %v", got, want) } if got := FormatChannels("", []string{"Collection:5"}); !reflect.DeepEqual(got, []string{"collection:5"}) { t.Fatalf("no namespace = %v", got) } } func TestClientID(t *testing.T) { if ClientID(context.Background()) != "" { t.Fatal("empty ctx has a client id") } if got := ClientID(WithClientID(context.Background(), "c-1")); got != "c-1" { t.Fatalf("ClientID = %q", got) } } func TestRegistry(t *testing.T) { r := NewRegistry() allow := AuthorizerFunc(func(context.Context, uint, string) Result { return Allowed(nil) }) if err := r.Register("b", allow); err != nil { t.Fatal(err) } if err := r.Register("a", allow); err != nil { t.Fatal(err) } for _, bad := range []struct { ns string a Authorizer }{{"", allow}, {"x:y", allow}, {"c", nil}, {"a", allow}} { if err := r.Register(bad.ns, bad.a); err == nil { t.Errorf("Register(%q) accepted", bad.ns) } } if got := r.Namespaces(); !reflect.DeepEqual(got, []string{"a", "b"}) { t.Fatalf("Namespaces = %v", got) } if _, ok := r.Get("A"); ok { t.Fatal("lookup is not case-sensitive") } if a, ok := r.Get("a"); !ok || !a.Authorize(context.Background(), 1, "a:1").Allowed { t.Fatal("Get(a) failed") } d := Denied("why") if d.Allowed || d.Reason() != "why" { t.Fatalf("Denied = %+v", d) } }