krz/skunky-art
Alternative privacy frontend for DeviantArt.
clone: git clone https://gitbay.org/krz/skunky-art.git
main: app/api_json_test.go · raw
1package app
2
3import (
4 "encoding/json"
5 "net/http/httptest"
6 "strings"
7 "testing"
8
9 "github.com/krazywarez/devianter"
10)
11
12// TestVisibleDeviationMatchesTheListingRules pins the single predicate the HTML
13// listing and the JSON API both use. If these diverge, the API starts serving
14// what the pages withhold.
15func TestVisibleDeviationMatchesTheListingRules(t *testing.T) {
16 nsfw, hide := CFG.Nsfw, CFG.HideAI
17 defer func() { CFG.Nsfw, CFG.HideAI = nsfw, hide }()
18
19 human := &devianter.Deviation{}
20 robot := &devianter.Deviation{AI: true}
21 adult := &devianter.Deviation{NSFW: true}
22
23 CFG.Nsfw, CFG.HideAI = false, false
24 if !VisibleDeviation(human) {
25 t.Error("plain deviation hidden with both settings off")
26 }
27 if !VisibleDeviation(robot) {
28 t.Error("AI deviation hidden while hide-ai is off")
29 }
30 if VisibleDeviation(adult) {
31 t.Error("NSFW deviation shown while nsfw is off")
32 }
33
34 CFG.HideAI = true
35 if VisibleDeviation(robot) {
36 t.Error("AI deviation shown while hide-ai is on")
37 }
38
39 CFG.Nsfw = true
40 if !VisibleDeviation(adult) {
41 t.Error("NSFW deviation hidden while nsfw is on")
42 }
43}
44
45// TestSearchRequiresAQuery covers the guard before any upstream request: an
46// empty q must not become a search for the empty string.
47func TestSearchRequiresAQuery(t *testing.T) {
48 rec := httptest.NewRecorder()
49 s := skunkyart{Writer: rec, Host: "http://localhost"}
50 API{main: &s}.Search()
51
52 if rec.Code != 400 {
53 t.Errorf("status = %d, want 400", rec.Code)
54 }
55 if !strings.Contains(rec.Body.String(), "q") {
56 t.Errorf("body does not name the missing parameter: %q", rec.Body.String())
57 }
58}
59
60// TestSearchRejectsAnUnsupportedType stops an unknown letter reaching devianter.
61func TestSearchRejectsAnUnsupportedType(t *testing.T) {
62 rec := httptest.NewRecorder()
63 s := skunkyart{Writer: rec, Host: "http://localhost", Query: "cats", Type: 'z'}
64 API{main: &s}.Search()
65
66 if rec.Code != 400 {
67 t.Errorf("status = %d, want 400", rec.Code)
68 }
69}
70
71// TestSearchGalleryTypeNeedsAUser: g and f are scoped to a user, and without one
72// the upstream call is meaningless.
73func TestSearchGalleryTypeNeedsAUser(t *testing.T) {
74 for _, kind := range []rune{'g', 'f'} {
75 rec := httptest.NewRecorder()
76 s := skunkyart{Writer: rec, Host: "http://localhost", Query: "cats", Type: kind}
77 s.Args = map[string][]string{}
78 API{main: &s}.Search()
79
80 if rec.Code != 400 {
81 t.Errorf("type %c: status = %d, want 400", kind, rec.Code)
82 }
83 }
84}
85
86// TestPostRejectsANameWithoutAnID mirrors the HTML route, which pulls the
87// deviation id out of the slug.
88func TestPostRejectsANameWithoutAnID(t *testing.T) {
89 rec := httptest.NewRecorder()
90 s := skunkyart{Writer: rec, Host: "http://localhost"}
91 API{main: &s}.Post("someone", "no-digits-here")
92
93 if rec.Code != 400 {
94 t.Errorf("status = %d, want 400", rec.Code)
95 }
96}
97
98// TestToAPIDeviationShape is the contract consumers depend on: field names and
99// the fact that media points back at this instance rather than at wixmp.
100func TestToAPIDeviationShape(t *testing.T) {
101 d := fullviewDeviation()
102 d.ID = 42
103 d.Title = "A Title"
104 d.Author.Username = "alice"
105 d.Stats.Favourites = 7
106 d.Stats.Views = 99
107 d.Extended.Tags = append(d.Extended.Tags, struct{ Name string }{Name: "cats"})
108
109 s := skunkyart{Host: "http://localhost"}
110 body, err := json.Marshal(s.toAPIDeviation(d))
111 if err != nil {
112 t.Fatalf("marshal: %v", err)
113 }
114
115 var got map[string]any
116 if err := json.Unmarshal(body, &got); err != nil {
117 t.Fatalf("unmarshal: %v", err)
118 }
119
120 for _, key := range []string{"id", "title", "author", "url", "nsfw", "ai", "daily_deviation", "favourites", "views"} {
121 if _, ok := got[key]; !ok {
122 t.Errorf("field %q missing from %s", key, body)
123 }
124 }
125 if got["title"] != "A Title" || got["author"] != "alice" {
126 t.Errorf("unexpected values in %s", body)
127 }
128 if tags, ok := got["tags"].([]any); !ok || len(tags) != 1 || tags[0] != "cats" {
129 t.Errorf("tags not flattened to names: %s", body)
130 }
131}