marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

bookmarks_api_test.go (3778B)


      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 	"strings"
     11 	"testing"
     12 )
     13 
     14 func TestBookmarksCRUDAndUnauthorized(t *testing.T) {
     15 	t.Parallel()
     16 	page := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
     17 		w.Header().Set("Content-Type", "text/html")
     18 		io.WriteString(w, "<!doctype html><html><head><title>CRUD Title</title></head><body>body</body></html>")
     19 	}))
     20 	defer page.Close()
     21 
     22 	db, err := OpenAtPath(t.Context(), t.TempDir()+"/crud.db")
     23 	if err != nil {
     24 		t.Fatalf("open: %v", err)
     25 	}
     26 	t.Cleanup(func() { _ = db.Close() })
     27 	h, _ := NewHandler(db)
     28 	srv := httptest.NewServer(h)
     29 	defer srv.Close()
     30 
     31 	// Unauth GET /bookmarks → 401
     32 	if resp, _ := http.Get(srv.URL + "/bookmarks"); resp.StatusCode != http.StatusUnauthorized {
     33 		t.Fatalf("/bookmarks unauth %d", resp.StatusCode)
     34 	}
     35 	// Unauth POST /bookmarks → 401
     36 	rp, _ := http.NewRequest(http.MethodPost, srv.URL+"/bookmarks", strings.NewReader("{}"))
     37 	rp.Header.Set("Content-Type", "application/json")
     38 	if rs, _ := http.DefaultClient.Do(rp); rs.StatusCode != http.StatusUnauthorized {
     39 		t.Fatalf("post unauth %d", rs.StatusCode)
     40 	}
     41 
     42 	// Sign up client
     43 	jar, _ := cookiejar.New(nil)
     44 	client := &http.Client{Jar: jar}
     45 	body, _ := json.Marshal(map[string]string{"username": "crud", "password": "pw"})
     46 	rq, _ := http.NewRequest(http.MethodPost, srv.URL+"/signup", bytes.NewReader(body))
     47 	rq.Header.Set("Content-Type", "application/json")
     48 	rsu, _ := client.Do(rq)
     49 	if rsu.StatusCode != http.StatusOK {
     50 		t.Fatalf("signup %d", rsu.StatusCode)
     51 	}
     52 	rsu.Body.Close()
     53 
     54 	// Wrong content type → 415
     55 	bad, _ := http.NewRequest(http.MethodPost, srv.URL+"/bookmarks", strings.NewReader("not json"))
     56 	bad.Header.Set("Content-Type", "text/plain")
     57 	rbad, _ := client.Do(bad)
     58 	if rbad.StatusCode != http.StatusBadRequest {
     59 		t.Fatalf("ct status %d", rbad.StatusCode)
     60 	}
     61 	rbad.Body.Close()
     62 
     63 	// Create
     64 	create := map[string]any{"url": page.URL}
     65 	cbody, _ := json.Marshal(create)
     66 	rc, _ := http.NewRequest(http.MethodPost, srv.URL+"/bookmarks", bytes.NewReader(cbody))
     67 	rc.Header.Set("Content-Type", "application/json")
     68 	rcr, _ := client.Do(rc)
     69 	if rcr.StatusCode != http.StatusCreated {
     70 		t.Fatalf("create %d", rcr.StatusCode)
     71 	}
     72 	var created map[string]any
     73 	_ = json.NewDecoder(rcr.Body).Decode(&created)
     74 	rcr.Body.Close()
     75 	id := int(created["id"].(float64))
     76 	if created["title"].(string) != "CRUD Title" {
     77 		t.Fatalf("title inferred")
     78 	}
     79 
     80 	// Duplicate POST should 200 and touch updated_at
     81 	rc2, _ := http.NewRequest(http.MethodPost, srv.URL+"/bookmarks", bytes.NewReader(cbody))
     82 	rc2.Header.Set("Content-Type", "application/json")
     83 	rcr2, _ := client.Do(rc2)
     84 	if rcr2.StatusCode != http.StatusOK {
     85 		t.Fatalf("dup %d", rcr2.StatusCode)
     86 	}
     87 	rcr2.Body.Close()
     88 
     89 	// GET list and RSS
     90 	if _, err := client.Get(srv.URL + "/bookmarks"); err != nil {
     91 		t.Fatalf("list err: %v", err)
     92 	}
     93 	if resp, _ := client.Get(srv.URL + "/bookmarks.xml"); resp.StatusCode != 200 {
     94 		t.Fatalf("rss %d", resp.StatusCode)
     95 	}
     96 
     97 	// PUT update (preserve description when omitted)
     98 	put := map[string]any{"url": page.URL, "title": "Changed"}
     99 	pb, _ := json.Marshal(put)
    100 	pr, _ := http.NewRequest(http.MethodPut, srv.URL+"/bookmarks/"+strconvItoa(id), bytes.NewReader(pb))
    101 	pr.Header.Set("Content-Type", "application/json")
    102 	prr, _ := client.Do(pr)
    103 	if prr.StatusCode != 200 {
    104 		t.Fatalf("put %d", prr.StatusCode)
    105 	}
    106 	var upd map[string]any
    107 	_ = json.NewDecoder(prr.Body).Decode(&upd)
    108 	prr.Body.Close()
    109 	if upd["title"].(string) != "Changed" {
    110 		t.Fatalf("put title")
    111 	}
    112 
    113 	// DELETE
    114 	dr, _ := http.NewRequest(http.MethodDelete, srv.URL+"/bookmarks/"+strconvItoa(id), nil)
    115 	drr, _ := client.Do(dr)
    116 	if drr.StatusCode != 204 {
    117 		t.Fatalf("delete %d", drr.StatusCode)
    118 	}
    119 }