app/wrapper_test.go
117 lines · 3715 bytes
1package app
2
3import (
4 "encoding/json"
5 "errors"
6 "net/http/httptest"
7 "net/url"
8 "strings"
9 "testing"
10
11 "github.com/krazywarez/devianter"
12)
13
14// withAvatarCache turns the media cache on over a temporary directory and
15// swaps in a counting avatar fetcher for the test's duration.
16func withAvatarCache(t *testing.T, enabled bool, fetch func(string, rune) (string, error)) *int {
17 t.Helper()
18 cache, orig := CFG.Cache, fetchAvatar
19 CFG.Cache.Enabled = enabled
20 CFG.Cache.MemCache = false
21 CFG.Cache.Path = t.TempDir()
22 calls := 0
23 fetchAvatar = func(name string, r rune) (string, error) {
24 calls++
25 return fetch(name, r)
26 }
27 t.Cleanup(func() { CFG.Cache, fetchAvatar = cache, orig })
28 return &calls
29}
30
31func emojitar(name string) *httptest.ResponseRecorder {
32 rec := httptest.NewRecorder()
33 skunkyart{Writer: rec, Host: "http://localhost", Type: 'a'}.Emojitar(name)
34 return rec
35}
36
37func TestEmojitarServesFromCacheAfterFirstFetch(t *testing.T) {
38 calls := withAvatarCache(t, true, func(string, rune) (string, error) { return "png", nil })
39
40 first := emojitar("Alice")
41 second := emojitar("alice") // case-insensitive, as DeviantArt's paths are
42
43 if *calls != 1 {
44 t.Errorf("avatar fetched %d times, want 1", *calls)
45 }
46 if first.Body.String() != "png" || second.Body.String() != "png" {
47 t.Errorf("bodies %q and %q, want both png", first.Body.String(), second.Body.String())
48 }
49}
50
51func TestEmojitarDoesNotCacheAMiss(t *testing.T) {
52 calls := withAvatarCache(t, true, func(string, rune) (string, error) { return "", errors.New("user not exists") })
53
54 first := emojitar("nobody")
55 emojitar("nobody")
56
57 if first.Code != 404 {
58 t.Errorf("status %d, want 404", first.Code)
59 }
60 if *calls != 2 {
61 t.Errorf("avatar fetched %d times, want 2: a miss must not be cached", *calls)
62 }
63}
64
65func TestEmojitarFetchesEveryTimeWithCacheOff(t *testing.T) {
66 calls := withAvatarCache(t, false, func(string, rune) (string, error) { return "png", nil })
67
68 emojitar("alice")
69 emojitar("alice")
70
71 if *calls != 2 {
72 t.Errorf("avatar fetched %d times, want 2 with the cache off", *calls)
73 }
74}
75
76// TestUserAboutRendersProfileDetails is the regression test for the branch
77// upstream had disabled with `else if false`: a person's about tab must show
78// their interests, social links and how long they have been registered. The
79// profile is decoded from JSON shaped like DeviantArt's, since the module
80// slice's element type embeds an unexported struct and cannot be built by
81// name from here.
82func TestUserAboutRendersProfileDetails(t *testing.T) {
83 const profile = `{
84 "owner": {"isGroup": false, "username": "alice"},
85 "gruser": {"gruserId": 42, "page": {"modules": [
86 {"name": "about", "moduleData": {"about": {
87 "deviantFor": 86400,
88 "interests": [{"label": "Favourite animal", "value": "skunk"}],
89 "socialLinks": [{"value": "https://social.example/alice"}]
90 }}}
91 ]}}
92 }`
93 orig := fetchProfile
94 fetchProfile = func(string) (devianter.GRuser, devianter.Error, error) {
95 var p devianter.GRuser
96 if err := json.Unmarshal([]byte(profile), &p); err != nil {
97 t.Fatal(err)
98 }
99 return p, devianter.Error{}, nil
100 }
101 t.Cleanup(func() { fetchProfile = orig })
102
103 loadTemplates()
104 rec := httptest.NewRecorder()
105 s := skunkyart{Writer: rec, Host: "http://localhost", BasePath: "/", Type: 'a', Query: "alice", Args: url.Values{}, _pth: "/group_user"}
106 s.GRUser()
107
108 body := rec.Body.String()
109 for _, want := range []string{"Favourite animal: <b>skunk</b>", `href="https://social.example/alice"`, "Registration date"} {
110 if !strings.Contains(body, want) {
111 t.Errorf("about page lacks %q:\n%s", want, body)
112 }
113 }
114 if strings.Contains(body, "0001-01-01") {
115 t.Error("registration date is the zero time")
116 }
117}