tinyrelayfile
tinyrelay/tinyclient/avatars_test.go/

tinyclient/avatars_test.go raw
1package tinyclient
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "strings"
7 "testing"
8
9 "github.com/FelineStateMachine/tinyrelay/internal/policy"
10 "github.com/FelineStateMachine/tinyrelay/internal/seedmark"
11)
12
13func TestAvatarEndpointRendersWithoutProfileAccess(t *testing.T) {
14 backend := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))}
15 backend.policy.Features.Grasp08 = true
16 app, err := New(backend, Options{})
17 if err != nil {
18 t.Fatal(err)
19 }
20 key := strings.Repeat("b", 64)
21 path := avatarURL(key)
22 response := httptest.NewRecorder()
23 app.ServeHTTP(response, httptest.NewRequest(http.MethodGet, path, nil))
24 if response.Code != http.StatusOK || response.Body.String() != seedmark.Avatar(key) {
25 t.Fatalf("avatar: status=%d body=%s", response.Code, response.Body.String())
26 }
27 if backend.call != "" || response.Header().Get("Content-Type") != "image/svg+xml; charset=utf-8" || !strings.Contains(response.Header().Get("Cache-Control"), "immutable") {
28 t.Fatalf("avatar accessed relay data or lacks image caching: call=%q headers=%v", backend.call, response.Header())
29 }
30 for _, method := range []string{http.MethodHead, http.MethodGet} {
31 request := httptest.NewRequest(method, path, nil)
32 if method == http.MethodGet {
33 request.Header.Set("If-None-Match", response.Header().Get("ETag"))
34 }
35 next := httptest.NewRecorder()
36 app.ServeHTTP(next, request)
37 if next.Body.Len() != 0 || (method == http.MethodHead && next.Code != http.StatusOK) || (method == http.MethodGet && next.Code != http.StatusNotModified) {
38 t.Fatalf("%s cache response: status=%d body=%s", method, next.Code, next.Body.String())
39 }
40 }
41}
42
43func TestAvatarEndpointRejectsOtherVersionsSeedsAndWrites(t *testing.T) {
44 app, err := New(&fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))}, Options{})
45 if err != nil {
46 t.Fatal(err)
47 }
48 for _, tc := range []struct {
49 method, path string
50 status int
51 }{
52 {http.MethodGet, "/avatars/v2/" + strings.Repeat("a", 64) + ".svg", http.StatusNotFound},
53 {http.MethodGet, "/avatars/v1/short.svg", http.StatusNotFound},
54 {http.MethodGet, "/avatars/v1/" + strings.Repeat("g", 64) + ".svg", http.StatusNotFound},
55 {http.MethodGet, "/avatars/v1/" + strings.Repeat("a", 64) + ".png", http.StatusNotFound},
56 {http.MethodPost, avatarURL(strings.Repeat("a", 64)), http.StatusMethodNotAllowed},
57 } {
58 response := httptest.NewRecorder()
59 app.ServeHTTP(response, httptest.NewRequest(tc.method, tc.path, nil))
60 if response.Code != tc.status {
61 t.Errorf("%s %s: status=%d, want %d", tc.method, tc.path, response.Code, tc.status)
62 }
63 }
64}
65
66func TestAvatarSeedsUseFullKeysAndRoomIdentity(t *testing.T) {
67 key := strings.Repeat("ab", 32)
68 if avatarURL(strings.ToUpper(key)) != avatarURL(key) || avatarURL(key) == avatarURL(key[:62]+"cd") {
69 t.Fatal("profile seed must use the full normalized key")
70 }
71 if avatarURL("invalid") != "/avatars/v1/"+strings.Repeat("0", 64)+".svg" {
72 t.Fatal("invalid keys should use the anonymous fallback")
73 }
74 room := roomAvatarURL("https://relay.test/r/team", "general")
75 if room != roomAvatarURL("https://relay.test/r/team/", "general") || room == roomAvatarURL("https://relay.test/r/other", "general") || room == roomAvatarURL("https://relay.test/r/team", "random") {
76 t.Fatal("room avatars must follow stable relay and room identity")
77 }
78}

About

ownerc01503e8035f
visibilitypublic
headd730679639bf

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs