app/api_test.go
147 lines · 4991 bytes
1package app
2
3import (
4 "net/http"
5 "net/http/httptest"
6 "net/url"
7 "strings"
8 "testing"
9
10 "github.com/krazywarez/devianter"
11)
12
13// fullviewDeviation returns a deviation whose media assembles into a non-empty
14// wixmp URL, i.e. one that sendMedia is meant to serve.
15func fullviewDeviation() *devianter.Deviation {
16 d := &devianter.Deviation{}
17 d.Media.BaseUri = "https://images-wixmp-abc.wixmp.com/f/u/x.png"
18 d.Media.Name = "x"
19 d.Media.Types = append(d.Media.Types, struct {
20 T string
21 H, W int
22 }{T: "fullview", H: 1920, W: 1280})
23 return d
24}
25
26// TestSendMediaServesRealMedia is the regression test for the inverted guard: a
27// deviation that has media must be sent, not dropped. In non-proxy mode that is
28// a 302 to the wixmp URL; the pre-fix guard returned before writing anything.
29func TestSendMediaServesRealMedia(t *testing.T) {
30 proxy := CFG.Proxy
31 CFG.Proxy = false
32 defer func() { CFG.Proxy = proxy }()
33
34 w := httptest.NewRecorder()
35 API{main: &skunkyart{Writer: w}}.sendMedia(fullviewDeviation())
36
37 if w.Code != 302 {
38 t.Errorf("status is %d, want a 302 redirect to the media", w.Code)
39 }
40 if w.Header().Get("Location") == "" {
41 t.Error("no Location header set — the media was dropped")
42 }
43}
44
45// TestSendMediaIgnoresEmptyMedia pins the other half of the bug: a deviation
46// with no media must be a no-op. With proxy on, the pre-fix code fell through to
47// mediaURL[21:] on an empty string and panicked.
48func TestSendMediaIgnoresEmptyMedia(t *testing.T) {
49 proxy := CFG.Proxy
50 CFG.Proxy = true
51 defer func() { CFG.Proxy = proxy }()
52
53 w := httptest.NewRecorder()
54 API{main: &skunkyart{Writer: w}}.sendMedia(&devianter.Deviation{})
55
56 if w.Code != 200 {
57 t.Errorf("status is %d, want nothing written (recorder default 200)", w.Code)
58 }
59 if loc := w.Header().Get("Location"); loc != "" {
60 t.Errorf("Location %q set for a media-less deviation, want none", loc)
61 }
62}
63
64// withDailyDeviations scripts the daily deviations fetch with the given
65// entries and counts the calls.
66func withDailyDeviations(t *testing.T, devs ...devianter.Deviation) *int {
67 t.Helper()
68 orig := fetchDailyDeviations
69 calls := 0
70 fetchDailyDeviations = func(int) (devianter.DailyDeviations, devianter.Error) {
71 calls++
72 return devianter.DailyDeviations{Deviations: devs}, devianter.Error{}
73 }
74 t.Cleanup(func() { fetchDailyDeviations = orig })
75 return &calls
76}
77
78// TestRandomPicksFromTheDailyDeviations pins the new source: one fetch of the
79// daily page, and the pick is served as media.
80func TestRandomPicksFromTheDailyDeviations(t *testing.T) {
81 proxy, nsfw := CFG.Proxy, CFG.Nsfw
82 CFG.Proxy, CFG.Nsfw = false, true
83 defer func() { CFG.Proxy, CFG.Nsfw = proxy, nsfw }()
84 calls := withDailyDeviations(t, *fullviewDeviation())
85
86 w := httptest.NewRecorder()
87 API{main: &skunkyart{Writer: w}}.Random()
88
89 if *calls != 1 {
90 t.Errorf("daily deviations fetched %d times, want 1", *calls)
91 }
92 if w.Code != 302 || w.Header().Get("Location") == "" {
93 t.Errorf("status %d, Location %q; want a 302 to the pick's media", w.Code, w.Header().Get("Location"))
94 }
95}
96
97// TestRandomHonoursNSFW pins that a pick is drawn only from what the instance
98// may show: with nsfw off and only mature entries there is nothing to serve.
99func TestRandomHonoursNSFW(t *testing.T) {
100 proxy, nsfw := CFG.Proxy, CFG.Nsfw
101 CFG.Proxy, CFG.Nsfw = false, false
102 defer func() { CFG.Proxy, CFG.Nsfw = proxy, nsfw }()
103 mature := *fullviewDeviation()
104 mature.NSFW = true
105 withDailyDeviations(t, mature)
106
107 w := httptest.NewRecorder()
108 API{main: &skunkyart{Writer: w}}.Random()
109
110 if w.Code != 404 || w.Header().Get("Location") != "" {
111 t.Errorf("status %d, Location %q; want 404 and no media", w.Code, w.Header().Get("Location"))
112 }
113}
114
115// TestSendMediaProxiesWithTheTokenInTheQuery is the regression test for
116// /api/random answering 401 with proxying on: the signing token was passed
117// inside the path, so wixmp never saw it as a parameter.
118func TestSendMediaProxiesWithTheTokenInTheQuery(t *testing.T) {
119 proxy, cache := CFG.Proxy, CFG.Cache.Enabled
120 CFG.Proxy, CFG.Cache.Enabled = true, false
121 defer func() { CFG.Proxy, CFG.Cache.Enabled = proxy, cache }()
122
123 var fetched string
124 orig := fetchMedia
125 fetchMedia = func(u string) Downloaded {
126 fetched = u
127 return Downloaded{Status: 200, Body: []byte("png"), Headers: http.Header{"Content-Type": {"image/png"}}}
128 }
129 defer func() { fetchMedia = orig }()
130
131 d := fullviewDeviation()
132 d.Media.Token = []string{"tok.en.sig"}
133
134 w := httptest.NewRecorder()
135 API{main: &skunkyart{Writer: w, Args: url.Values{}}}.sendMedia(d)
136
137 u, err := url.Parse(fetched)
138 if err != nil || u.Host != "images-wixmp-abc.wixmp.com" {
139 t.Fatalf("fetched %q, want a wixmp URL on the deviation's subdomain", fetched)
140 }
141 if strings.Contains(u.Path, "token") || u.Query().Get("token") == "" {
142 t.Errorf("token not passed as a query parameter: path %q query %q", u.Path, u.RawQuery)
143 }
144 if w.Code != 200 || w.Body.String() != "png" {
145 t.Errorf("response %d %q, want the proxied image", w.Code, w.Body.String())
146 }
147}