update_archive_test.go (3263B)
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 "testing" 11 ) 12 13 func TestUpdateURL_RefetchArchiveAndRejectOnError(t *testing.T) { 14 t.Parallel() 15 16 // HTML servers 17 good := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 18 if r.Method == http.MethodHead { 19 w.Header().Set("Content-Type", "text/html; charset=utf-8") 20 return 21 } 22 w.Header().Set("Content-Type", "text/html; charset=utf-8") 23 io.WriteString(w, "<!doctype html><html><head><title>T1</title></head><body>alpha</body></html>") 24 })) 25 defer good.Close() 26 changed := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 27 if r.Method == http.MethodHead { 28 w.Header().Set("Content-Type", "text/html; charset=utf-8") 29 return 30 } 31 w.Header().Set("Content-Type", "text/html; charset=utf-8") 32 io.WriteString(w, "<!doctype html><html><head><title>T2</title></head><body>bravo</body></html>") 33 })) 34 defer changed.Close() 35 failing := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 36 if r.Method == http.MethodHead { 37 w.Header().Set("Content-Type", "text/html; charset=utf-8") 38 return 39 } 40 http.Error(w, "nope", http.StatusNotFound) 41 })) 42 defer failing.Close() 43 44 db, _ := OpenAtPath(t.Context(), t.TempDir()+"/upd.db") 45 t.Cleanup(func() { _ = db.Close() }) 46 h, _ := NewHandler(db) 47 srv := httptest.NewServer(h) 48 defer srv.Close() 49 50 jar, _ := cookiejar.New(nil) 51 client := &http.Client{Jar: jar} 52 // signup 53 b, _ := json.Marshal(map[string]string{"username": "upd", "password": "pw"}) 54 r, _ := http.NewRequest(http.MethodPost, srv.URL+"/signup", bytes.NewReader(b)) 55 r.Header.Set("Content-Type", "application/json") 56 rs, _ := client.Do(r) 57 if rs.StatusCode != 200 { 58 t.Fatalf("signup %d", rs.StatusCode) 59 } 60 rs.Body.Close() 61 62 // create initial 63 cr := postJSONWithClient[map[string]any](t, client, srv.URL+"/bookmarks", map[string]any{"url": good.URL}, 201) 64 id := int(cr["id"].(float64)) 65 66 // update URL to changed (should refetch and succeed) 67 putJSONWithClient[map[string]any](t, client, srv.URL+"/bookmarks/"+itoa(id), map[string]any{"url": changed.URL, "title": "First"}, 200) 68 // archive should contain 'bravo' 69 resp := getWithClient(t, client, srv.URL+"/bookmarks/"+itoa(id)+"/archive", 200) 70 body, _ := io.ReadAll(resp.Body) 71 resp.Body.Close() 72 if !bytes.Contains(bytes.ToLower(body), []byte("bravo")) { 73 t.Fatalf("expected new archive to contain bravo, got %q", string(body)) 74 } 75 76 // update URL to failing (fetch GET 404) should reject 77 req, _ := http.NewRequest(http.MethodPut, srv.URL+"/bookmarks/"+itoa(id), bytes.NewReader([]byte("{\"url\":\""+failing.URL+"\",\"title\":\"First\"}"))) 78 req.Header.Set("Content-Type", "application/json") 79 rs2, _ := client.Do(req) 80 if rs2.StatusCode != http.StatusUnprocessableEntity { 81 t.Fatalf("expected 422 on fetch failure, got %d", rs2.StatusCode) 82 } 83 rs2.Body.Close() 84 85 // ensure URL not changed 86 list := getJSONWithClient[[]map[string]any](t, client, srv.URL+"/bookmarks", 200) 87 if len(list) != 1 { 88 t.Fatalf("expected 1 bookmark, got %d", len(list)) 89 } 90 if list[0]["url"].(string) != changed.URL { 91 t.Fatalf("expected url to remain %q, got %q", changed.URL, list[0]["url"]) 92 } 93 }