more_api_tests.go (4416B)
1 package internal 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "net/http" 8 "net/http/cookiejar" 9 "net/http/httptest" 10 "strconv" 11 "testing" 12 ) 13 14 func TestPublicUserListingJSON(t *testing.T) { 15 t.Parallel() 16 db, _ := OpenAtPath(t.Context(), t.TempDir()+"/u.db") 17 t.Cleanup(func() { _ = db.Close() }) 18 h, _ := NewHandler(db) 19 srv := httptest.NewServer(h) 20 defer srv.Close() 21 22 // Unknown user 23 if resp, _ := http.Get(srv.URL + "/u/nouser"); resp.StatusCode != http.StatusNotFound { 24 t.Fatalf("/u/nouser expected 404, got %d", resp.StatusCode) 25 } 26 27 // Create user and ensure empty list 28 _, _ = db.CreateUser(t.Context(), "eve", "pw") 29 if resp, _ := http.Get(srv.URL + "/u/eve"); resp.StatusCode != 200 { 30 t.Fatalf("/u/eve %d", resp.StatusCode) 31 } 32 } 33 34 func TestBookmarksParamValidationAndRSSAccept(t *testing.T) { 35 t.Parallel() 36 page := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 37 w.Header().Set("Content-Type", "text/html") 38 io.WriteString(w, "<!doctype html><html><head><title>P</title></head><body/></html>") 39 })) 40 defer page.Close() 41 42 db, _ := OpenAtPath(t.Context(), t.TempDir()+"/p.db") 43 t.Cleanup(func() { _ = db.Close() }) 44 h, _ := NewHandler(db) 45 srv := httptest.NewServer(h) 46 defer srv.Close() 47 48 jar, _ := cookiejar.New(nil) 49 client := &http.Client{Jar: jar} 50 // signup 51 body, _ := json.Marshal(map[string]string{"username": "param", "password": "pw"}) 52 req, _ := http.NewRequest(http.MethodPost, srv.URL+"/signup", bytes.NewReader(body)) 53 req.Header.Set("Content-Type", "application/json") 54 rs, _ := client.Do(req) 55 if rs.StatusCode != 200 { 56 t.Fatalf("signup %d", rs.StatusCode) 57 } 58 rs.Body.Close() 59 // One bookmark 60 b2, _ := json.Marshal(map[string]any{"url": page.URL}) 61 rq, _ := http.NewRequest(http.MethodPost, srv.URL+"/bookmarks", bytes.NewReader(b2)) 62 rq.Header.Set("Content-Type", "application/json") 63 cr, _ := client.Do(rq) 64 if cr.StatusCode != 201 { 65 t.Fatalf("create %d", cr.StatusCode) 66 } 67 cr.Body.Close() 68 69 // invalid limit 70 if resp, _ := client.Get(srv.URL + "/bookmarks?limit=abc"); resp.StatusCode != http.StatusBadRequest { 71 t.Fatalf("limit status %d", resp.StatusCode) 72 } 73 // invalid offset 74 if resp, _ := client.Get(srv.URL + "/bookmarks?offset=-1"); resp.StatusCode != http.StatusBadRequest { 75 t.Fatalf("offset status %d", resp.StatusCode) 76 } 77 // Accept RSS 78 req2, _ := http.NewRequest(http.MethodGet, srv.URL+"/bookmarks", nil) 79 req2.Header.Set("Accept", "application/rss+xml") 80 rs2, _ := client.Do(req2) 81 if rs2.StatusCode != 200 { 82 t.Fatalf("accept rss %d", rs2.StatusCode) 83 } 84 rs2.Body.Close() 85 } 86 87 func TestArchiveFetchOwnerOnly(t *testing.T) { 88 t.Parallel() 89 // Page to fetch 90 page := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 91 w.Header().Set("Content-Type", "text/html") 92 io.WriteString(w, "<!doctype html><html><head><title>A</title></head><body/></html>") 93 })) 94 defer page.Close() 95 96 db, _ := OpenAtPath(t.Context(), t.TempDir()+"/arc.db") 97 t.Cleanup(func() { _ = db.Close() }) 98 h, _ := NewHandler(db) 99 srv := httptest.NewServer(h) 100 defer srv.Close() 101 102 // signup owner 103 j1, _ := cookiejar.New(nil) 104 c1 := &http.Client{Jar: j1} 105 s1, _ := json.Marshal(map[string]string{"username": "own", "password": "pw"}) 106 rq1, _ := http.NewRequest(http.MethodPost, srv.URL+"/signup", bytes.NewReader(s1)) 107 rq1.Header.Set("Content-Type", "application/json") 108 _, _ = c1.Do(rq1) 109 // create via DB without archive 110 u, _ := db.GetUserByUsername(t.Context(), "own") 111 bmk, err := db.CreateBookmark(t.Context(), u.ID, page.URL+"/priv", "PVT", nil) 112 if err != nil { 113 t.Fatalf("create raw: %v", err) 114 } 115 // owner can fetch with ?fetch=1 116 resp, _ := c1.Get(srv.URL + "/bookmarks/" + strconv.Itoa(int(bmk.ID)) + "/archive?fetch=1") 117 if resp.StatusCode != 200 { 118 t.Fatalf("owner fetch %d", resp.StatusCode) 119 } 120 resp.Body.Close() 121 122 // signup other user 123 j2, _ := cookiejar.New(nil) 124 c2 := &http.Client{Jar: j2} 125 s2, _ := json.Marshal(map[string]string{"username": "other", "password": "pw"}) 126 rq2, _ := http.NewRequest(http.MethodPost, srv.URL+"/signup", bytes.NewReader(s2)) 127 rq2.Header.Set("Content-Type", "application/json") 128 _, _ = c2.Do(rq2) 129 // other user cannot see private archive 130 resp2, _ := c2.Get(srv.URL + "/bookmarks/" + strconv.Itoa(int(bmk.ID)) + "/archive") 131 if resp2.StatusCode != 404 { 132 t.Fatalf("other archive %d", resp2.StatusCode) 133 } 134 resp2.Body.Close() 135 } 136 137 // no helper needed; use strconv.Itoa inline