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}