deleteUser now succeeds for a user who has no comments (e.g. one who only logged in) instead of failing on the missing per-user bucket. In hard mode the per-user bucket is deleted, tolerating bbolt's ErrBucketNotFound so a bucket left behind by an earlier partial removal is still removed; the comment-deletion failure path now wraps the actual error. Because the engine cannot distinguish a valid login-only user from a never-existed one, deletion is idempotent: /admin/deleteme returns 200 for an unknown (but validly signed) token rather than 400. The deleteme test is updated to this contract, engine tests cover hard and soft deletion of login-only and unknown users, and the API docs note the idempotent behaviour.
884 lines
34 KiB
Go
884 lines
34 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"compress/gzip"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-pkgz/auth/v2/token"
|
|
cache "github.com/go-pkgz/lcw/v2"
|
|
R "github.com/go-pkgz/rest"
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/umputun/remark42/backend/app/store"
|
|
"github.com/umputun/remark42/backend/app/store/service"
|
|
)
|
|
|
|
func TestAdmin_Delete(t *testing.T) {
|
|
ts, _, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", User: store.User{ID: "id", Name: "name"},
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
c2 := store.Comment{Text: "test test #2", User: store.User{ID: "id", Name: "name"}, ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
|
|
id1 := addComment(t, c1, ts)
|
|
addComment(t, c2, ts)
|
|
|
|
// check last comments
|
|
res, code := get(t, ts.URL+"/api/v1/last/2?site=remark42")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments := []store.Comment{}
|
|
err := json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, len(comments), "should have 2 comments")
|
|
|
|
// check multi count
|
|
resp, err := post(t, ts.URL+"/api/v1/counts?site=remark42", `["https://radio-t.com/blah","https://radio-t.com/blah2"]`)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
bb, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.NoError(t, err)
|
|
j := []store.PostInfo{}
|
|
err = json.Unmarshal(bb, &j)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []store.PostInfo{{URL: "https://radio-t.com/blah", Count: 2},
|
|
{URL: "https://radio-t.com/blah2", Count: 0}}, j)
|
|
|
|
// delete a comment
|
|
req, err := http.NewRequest(http.MethodDelete,
|
|
fmt.Sprintf("%s/api/v1/admin/comment/%s?site=remark42&url=https://radio-t.com/blah", ts.URL, id1), http.NoBody)
|
|
require.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
body, code := getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=remark42&url=https://radio-t.com/blah", ts.URL, id1))
|
|
assert.Equal(t, http.StatusOK, code)
|
|
cr := store.Comment{}
|
|
err = json.Unmarshal([]byte(body), &cr)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "", cr.Text)
|
|
assert.True(t, cr.Deleted)
|
|
|
|
time.Sleep(250 * time.Millisecond)
|
|
// check last comments updated
|
|
res, code = get(t, ts.URL+"/api/v1/last/2?site=remark42")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments = []store.Comment{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, len(comments), "should have 1 comments")
|
|
|
|
// check count updated
|
|
res, code = get(t, ts.URL+"/api/v1/count?site=remark42&url=https://radio-t.com/blah")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
b := map[string]any{}
|
|
err = json.Unmarshal([]byte(res), &b)
|
|
assert.NoError(t, err)
|
|
t.Logf("%#v", b)
|
|
assert.Equal(t, 1.0, b["count"], "should report 1 comments")
|
|
|
|
// check multi count updated
|
|
resp, err = post(t, ts.URL+"/api/v1/counts?site=remark42", `["https://radio-t.com/blah","https://radio-t.com/blah2"]`)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
bb, err = io.ReadAll(resp.Body)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.NoError(t, err)
|
|
j = []store.PostInfo{}
|
|
err = json.Unmarshal(bb, &j)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []store.PostInfo{{URL: "https://radio-t.com/blah", Count: 1},
|
|
{URL: "https://radio-t.com/blah2", Count: 0}}, j)
|
|
}
|
|
|
|
func TestAdmin_Title(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
srv.DataService.TitleExtractor = service.NewTitleExtractor(http.Client{Timeout: time.Second}, []string{"127.0.0.1"})
|
|
tss := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.String() == "/post1" {
|
|
_, err := w.Write([]byte("<html><title>post1 blah 123</title><body> 2222</body></html>"))
|
|
assert.NoError(t, err)
|
|
return
|
|
}
|
|
if r.URL.String() == "/post2" {
|
|
_, err := w.Write([]byte("<html><title>post2 blah 123</title><body> 2222</body></html>"))
|
|
assert.NoError(t, err)
|
|
return
|
|
}
|
|
w.WriteHeader(404)
|
|
}))
|
|
defer tss.Close()
|
|
|
|
c1 := store.Comment{Text: "test test #1", User: store.User{ID: "id", Name: "name"},
|
|
Locator: store.Locator{SiteID: "remark42", URL: tss.URL + "/post1"}}
|
|
c2 := store.Comment{Text: "test test #2", User: store.User{ID: "id", Name: "name"}, ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: tss.URL + "/post2"}}
|
|
|
|
id1 := addComment(t, c1, ts)
|
|
addComment(t, c2, ts)
|
|
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/title/%s?site=remark42&url=%s/post1", ts.URL, id1, tss.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
body, code := get(t, fmt.Sprintf("%s/api/v1/id/%s?site=remark42&url=%s/post1", ts.URL, id1, tss.URL))
|
|
require.Equal(t, http.StatusOK, code)
|
|
cr := store.Comment{}
|
|
err = json.Unmarshal([]byte(body), &cr)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "post1 blah 123", cr.PostTitle)
|
|
}
|
|
|
|
func TestAdmin_DeleteUser(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Orig: "o test test #1", User: store.User{ID: "id1", Name: "name"},
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
c2 := store.Comment{Text: "test test #2", Orig: "o test test #2", User: store.User{ID: "id2", Name: "name"}, ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
c3 := store.Comment{Text: "test test #3", Orig: "o test test #3", User: store.User{ID: "id2", Name: "name"}, ParentID: "",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
|
|
// write comments directly to store to keep user id
|
|
id1, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c3)
|
|
assert.NoError(t, err)
|
|
|
|
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/v1/admin/user/%s?site=remark42", ts.URL, "id2"), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
assert.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
// all 3 comments here, but for id2 they deleted
|
|
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
cmntWithInfo := commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &cmntWithInfo)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 3, len(cmntWithInfo.Comments), "should have 3 comment")
|
|
|
|
// id1 comment untouched
|
|
assert.Equal(t, id1, cmntWithInfo.Comments[0].ID)
|
|
assert.Equal(t, "o test test #1", cmntWithInfo.Comments[0].Orig)
|
|
assert.False(t, cmntWithInfo.Comments[0].Deleted)
|
|
t.Logf("%+v", cmntWithInfo.Comments[0].User)
|
|
|
|
// id2 comments fully deleted
|
|
assert.Equal(t, "", cmntWithInfo.Comments[1].Text)
|
|
assert.Equal(t, "", cmntWithInfo.Comments[1].Orig)
|
|
assert.Equal(t, store.User{Name: "deleted", ID: "deleted", Picture: "", Admin: false, Blocked: false, IP: ""}, cmntWithInfo.Comments[1].User)
|
|
assert.True(t, cmntWithInfo.Comments[1].Deleted)
|
|
|
|
assert.Equal(t, "", cmntWithInfo.Comments[2].Text)
|
|
assert.Equal(t, "", cmntWithInfo.Comments[2].Orig)
|
|
assert.Equal(t, store.User{Name: "deleted", ID: "deleted", Picture: "", Admin: false, Blocked: false, IP: ""}, cmntWithInfo.Comments[1].User)
|
|
assert.True(t, cmntWithInfo.Comments[2].Deleted)
|
|
}
|
|
|
|
func TestAdmin_Pin(t *testing.T) {
|
|
ts, _, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
|
|
id1 := addComment(t, c1, ts)
|
|
addComment(t, c2, ts)
|
|
|
|
pin := func(val int) int {
|
|
client := http.Client{}
|
|
defer client.CloseIdleConnections()
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/pin/%s?site=remark42&url=https://radio-t.com/blah&pin=%d", ts.URL, id1, val), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
return resp.StatusCode
|
|
}
|
|
|
|
code := pin(1)
|
|
assert.Equal(t, http.StatusOK, code)
|
|
|
|
body, code := get(t, fmt.Sprintf("%s/api/v1/id/%s?site=remark42&url=https://radio-t.com/blah", ts.URL, id1))
|
|
assert.Equal(t, http.StatusOK, code)
|
|
cr := store.Comment{}
|
|
err := json.Unmarshal([]byte(body), &cr)
|
|
assert.NoError(t, err)
|
|
assert.True(t, cr.Pin)
|
|
|
|
code = pin(-1)
|
|
assert.Equal(t, http.StatusOK, code)
|
|
body, code = get(t, fmt.Sprintf("%s/api/v1/id/%s?site=remark42&url=https://radio-t.com/blah", ts.URL, id1))
|
|
assert.Equal(t, http.StatusOK, code)
|
|
cr = store.Comment{}
|
|
err = json.Unmarshal([]byte(body), &cr)
|
|
assert.NoError(t, err)
|
|
assert.False(t, cr.Pin)
|
|
}
|
|
|
|
func TestAdmin_Block(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
makeTwoComments := func() {
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
require.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
block := func(val int, ttl string) (code int, body []byte) {
|
|
url := fmt.Sprintf("%s/api/v1/admin/user/%s?site=remark42&block=%d", ts.URL, "user1", val)
|
|
if ttl != "" {
|
|
url = url + "&ttl=" + ttl
|
|
}
|
|
req, err := http.NewRequest(http.MethodPut, url, http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
body, err = io.ReadAll(resp.Body)
|
|
assert.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
return resp.StatusCode, body
|
|
}
|
|
|
|
makeTwoComments()
|
|
|
|
// block permanently
|
|
code, body := block(1, "")
|
|
require.Equal(t, http.StatusOK, code)
|
|
j := R.JSON{}
|
|
err := json.Unmarshal(body, &j)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "user1", j["user_id"])
|
|
assert.Equal(t, true, j["block"])
|
|
assert.Equal(t, "remark42", j["site_id"])
|
|
|
|
assert.True(t, srv.adminRest.dataService.IsBlocked("remark42", "user1"))
|
|
assert.False(t, srv.adminRest.dataService.IsBlocked("remark42", "user2"))
|
|
|
|
// get last to confirm one comment deleted
|
|
bodyStr, code := get(t, ts.URL+"/api/v1/last/10?site=remark42")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
pi := []store.PostInfo{}
|
|
assert.NoError(t, json.Unmarshal([]byte(bodyStr), &pi))
|
|
assert.Equal(t, 1, len(pi), "last status updated, one comment left")
|
|
|
|
// check if count call has one comment left
|
|
resp, err := post(t, ts.URL+"/api/v1/counts?site=remark42", `["https://radio-t.com/blah"]`)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
body, err = io.ReadAll(resp.Body)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
pi = []store.PostInfo{}
|
|
err = json.Unmarshal(body, &pi)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []store.PostInfo{{URL: "https://radio-t.com/blah", Count: 1}}, pi)
|
|
|
|
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments := commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 2, len(comments.Comments), "should have 2 comments")
|
|
assert.Equal(t, "", comments.Comments[0].Text, "permanent block clear comment")
|
|
assert.True(t, comments.Comments[0].Deleted, "permanent block set deleted comment's status")
|
|
|
|
// unblock
|
|
code, body = block(-1, "")
|
|
require.Equal(t, http.StatusOK, code)
|
|
err = json.Unmarshal(body, &j)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, false, j["block"])
|
|
|
|
// block with ttl
|
|
makeTwoComments()
|
|
code, _ = block(1, "50ms")
|
|
require.Equal(t, http.StatusOK, code)
|
|
|
|
// get as regular user
|
|
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments = commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 4, len(comments.Comments), "should have 4 comments")
|
|
assert.Equal(t, "test test #1", comments.Comments[2].Text, "comment not removed and not cleared")
|
|
assert.False(t, comments.Comments[2].Deleted, "not deleted")
|
|
|
|
srv.pubRest.cache = cache.NewScache[[]byte](cache.NewNopCache[[]byte]()) // TODO: with lru cache it won't be refreshed and invalidated for long
|
|
// time
|
|
time.Sleep(50 * time.Millisecond)
|
|
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments = commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 4, len(comments.Comments), "should have 4 comments")
|
|
assert.Equal(t, "test test #1", comments.Comments[2].Text, "restored")
|
|
assert.False(t, comments.Comments[2].Deleted)
|
|
|
|
assert.False(t, srv.adminRest.dataService.IsBlocked("remark42", "user1"))
|
|
assert.False(t, srv.adminRest.dataService.IsBlocked("remark42", "user2"))
|
|
}
|
|
|
|
func TestAdmin_BlockedList(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2 name", ID: "user2"}}
|
|
|
|
// write comments for user1 and user2
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
// block user1
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/user/%s?site=remark42&block=%d", ts.URL, "user1", 1), http.NoBody)
|
|
assert.NoError(t, err)
|
|
res, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, res.Body.Close())
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
// block user2
|
|
req, err = http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/user/%s?site=remark42&block=%d&ttl=150ms", ts.URL, "user2", 1), http.NoBody)
|
|
assert.NoError(t, err)
|
|
res, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, res.Body.Close())
|
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
|
|
|
req, err = http.NewRequest("GET", ts.URL+"/api/v1/admin/blocked?site=remark42", http.NoBody)
|
|
require.NoError(t, err)
|
|
res, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
users := []store.BlockedUser{}
|
|
err = json.NewDecoder(res.Body).Decode(&users)
|
|
assert.NoError(t, err)
|
|
require.NoError(t, res.Body.Close())
|
|
require.Equal(t, 2, len(users), "two users blocked")
|
|
assert.Equal(t, "user1", users[0].ID)
|
|
assert.Equal(t, "user1 name", users[0].Name)
|
|
assert.Equal(t, "user2", users[1].ID)
|
|
assert.Equal(t, "user2 name", users[1].Name)
|
|
t.Logf("%+v", users)
|
|
time.Sleep(150 * time.Millisecond)
|
|
|
|
req, err = http.NewRequest("GET", ts.URL+"/api/v1/admin/blocked?site=remark42", http.NoBody)
|
|
require.NoError(t, err)
|
|
res, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, res.StatusCode)
|
|
users = []store.BlockedUser{}
|
|
err = json.NewDecoder(res.Body).Decode(&users)
|
|
assert.NoError(t, err)
|
|
require.NoError(t, res.Body.Close())
|
|
assert.Equal(t, 1, len(users), "one user left blocked")
|
|
}
|
|
|
|
func TestAdmin_ReadOnly(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
info, err := srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.NoError(t, err)
|
|
assert.False(t, info.ReadOnly)
|
|
|
|
// set post to read-only
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/readonly?site=remark42&url=https://radio-t.com/blah&ro=1", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
resp, err := sendReq(t, req, "") // non-admin user
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
info, err = srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.ReadOnly)
|
|
|
|
// try to write comment
|
|
c := store.Comment{Text: "test test #2", ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
b, err := json.Marshal(c)
|
|
assert.NoError(t, err, "can't marshal comment %+v", c)
|
|
req, err = http.NewRequest("POST", ts.URL+"/api/v1/comment?site=remark42", bytes.NewBuffer(b))
|
|
require.NoError(t, err)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
|
|
// reset post's read-only
|
|
req, err = http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/readonly?site=remark42&url=https://radio-t.com/blah&ro=0", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
info, err = srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.NoError(t, err)
|
|
assert.False(t, info.ReadOnly)
|
|
|
|
// try to write comment
|
|
c = store.Comment{Text: "test test #2", ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}}
|
|
b, err = json.Marshal(c)
|
|
assert.NoError(t, err, "can't marshal comment %+v", c)
|
|
req, err = http.NewRequest("POST", ts.URL+"/api/v1/comment?site="+c.Locator.SiteID, bytes.NewBuffer(b))
|
|
require.NoError(t, err)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusCreated, resp.StatusCode)
|
|
}
|
|
|
|
func TestAdmin_ReadOnlyNoComments(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
// set post to read-only
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/readonly?site=remark42&url=https://radio-t.com/blah&ro=1", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
_, err = srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.Error(t, err)
|
|
|
|
// test format "tree"
|
|
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&format=tree")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments := commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, len(comments.Comments), "should have 0 comments")
|
|
assert.True(t, comments.Info.ReadOnly)
|
|
t.Logf("%+v", comments)
|
|
|
|
// test format "plain"
|
|
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments = commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, len(comments.Comments), "should have 0 comments")
|
|
assert.True(t, comments.Info.ReadOnly)
|
|
t.Logf("%+v", comments)
|
|
}
|
|
|
|
func TestAdmin_ReadOnlyWithAge(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"},
|
|
Timestamp: time.Date(2001, 1, 1, 1, 1, 1, 0, time.Local)}
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
|
|
info, err := srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 10)
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.ReadOnly, "ro by age")
|
|
|
|
// set post to read-only
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/readonly?site=remark42&url=https://radio-t.com/blah&ro=1", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
info, err = srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.ReadOnly)
|
|
|
|
// reset post's read-only
|
|
req, err = http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/readonly?site=remark42&url=https://radio-t.com/blah&ro=0", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
info, err = srv.DataService.Info(store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah"}, 0)
|
|
assert.NoError(t, err)
|
|
assert.True(t, info.ReadOnly)
|
|
}
|
|
func TestAdmin_Verify(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
verified := srv.DataService.IsVerified("remark42", "user1")
|
|
assert.False(t, verified)
|
|
|
|
req, err := http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/verify/user1?site=remark42&verified=1", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
verified = srv.DataService.IsVerified("remark42", "user1")
|
|
assert.True(t, verified)
|
|
|
|
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments := commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 2, len(comments.Comments), "should have 2 comments")
|
|
assert.Equal(t, "test test #1", comments.Comments[0].Text)
|
|
assert.True(t, comments.Comments[0].User.Verified)
|
|
|
|
req, err = http.NewRequest(http.MethodPut,
|
|
fmt.Sprintf("%s/api/v1/admin/verify/user1?site=remark42&verified=0", ts.URL), http.NoBody)
|
|
assert.NoError(t, err)
|
|
resp, err = sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
verified = srv.DataService.IsVerified("remark42", "user1")
|
|
assert.False(t, verified)
|
|
|
|
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah&sort=+time")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
comments = commentsWithInfo{}
|
|
err = json.Unmarshal([]byte(res), &comments)
|
|
assert.NoError(t, err)
|
|
require.Equal(t, 2, len(comments.Comments), "should have 2 comments")
|
|
assert.Equal(t, "test test #1", comments.Comments[0].Text)
|
|
assert.False(t, comments.Comments[0].User.Verified)
|
|
}
|
|
|
|
func TestAdmin_ExportStream(t *testing.T) {
|
|
ts, _, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah2"}}
|
|
|
|
addComment(t, c1, ts)
|
|
addComment(t, c2, ts)
|
|
|
|
body, code := getWithAdminAuth(t, ts.URL+"/api/v1/admin/export?site=remark42&mode=stream")
|
|
assert.Equal(t, http.StatusOK, code)
|
|
assert.Equal(t, 3, strings.Count(body, "\n"))
|
|
assert.Equal(t, 2, strings.Count(body, "\"text\""))
|
|
t.Logf("%s", body)
|
|
}
|
|
|
|
func TestAdmin_ExportFile(t *testing.T) {
|
|
ts, _, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1",
|
|
Locator: store.Locator{SiteID: "remark42", URL: "https://radio-t.com/blah2"}}
|
|
|
|
addComment(t, c1, ts)
|
|
addComment(t, c2, ts)
|
|
|
|
req, err := http.NewRequest("GET", ts.URL+"/api/v1/admin/export?site=remark42&mode=file", http.NoBody)
|
|
require.NoError(t, err)
|
|
requireAdminOnly(t, req)
|
|
resp, err := sendReq(t, req, adminUmputunToken)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
assert.Equal(t, "application/gzip", resp.Header.Get("Content-Type"))
|
|
|
|
ungzReader, err := gzip.NewReader(resp.Body)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
ungzBody, err := io.ReadAll(ungzReader)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 3, strings.Count(string(ungzBody), "\n"))
|
|
assert.Equal(t, 2, strings.Count(string(ungzBody), "\"text\""))
|
|
t.Logf("%s", string(ungzBody))
|
|
}
|
|
|
|
func TestAdmin_DeleteMeRequest(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
comments, err := srv.DataService.User("remark42", "user1", 0, 0, store.User{})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, len(comments), "a comment for user1")
|
|
|
|
email, err := srv.DataService.SetUserEmail("remark42", "user1", "test@example.org")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "test@example.org", email, "new email for user1")
|
|
|
|
email, err = srv.DataService.GetUserEmail("remark42", "user1")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "test@example.org", email, "new email for user1 is readable")
|
|
|
|
claims := token.Claims{
|
|
SessionOnly: true,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Audience: jwt.ClaimStrings{"remark42"},
|
|
ID: "1234567",
|
|
Issuer: "remark42",
|
|
NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Minute)),
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
|
|
},
|
|
User: &token.User{
|
|
ID: "user1",
|
|
Picture: "pic.image",
|
|
Attributes: map[string]any{
|
|
"delete_me": true,
|
|
},
|
|
},
|
|
}
|
|
|
|
require.NoError(t, os.MkdirAll(os.TempDir()+"/ava-remark42/42", 0o700))
|
|
require.NoError(t, os.WriteFile(os.TempDir()+"/ava-remark42/42/pic.image", []byte("some image data"), 0o600))
|
|
|
|
tkn, err := srv.Authenticator.TokenService().Token(claims)
|
|
assert.NoError(t, err)
|
|
|
|
client := http.Client{}
|
|
defer client.CloseIdleConnections()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, tkn), http.NoBody)
|
|
assert.NoError(t, err)
|
|
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
require.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
_, err = srv.DataService.User("remark42", "user1", 0, 0, store.User{})
|
|
assert.EqualError(t, err, "no comments for user user1 in store")
|
|
|
|
email, err = srv.DataService.GetUserEmail("remark42", "user1")
|
|
assert.NoError(t, err)
|
|
assert.Empty(t, email, "user1 email was deleted")
|
|
}
|
|
|
|
func TestAdmin_DeleteMeRequestFailed(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "provider1_user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "provider1_user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
// try with bad token
|
|
client := http.Client{}
|
|
defer client.CloseIdleConnections()
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, "bad token"), http.NoBody)
|
|
assert.NoError(t, err)
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err := client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
|
|
// try with bad auth
|
|
claims := token.Claims{
|
|
SessionOnly: true,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
Audience: jwt.ClaimStrings{"remark42"},
|
|
ID: "provider1_1234567",
|
|
Issuer: "remark42",
|
|
NotBefore: jwt.NewNumericDate(time.Now().Add(-1 * time.Minute)),
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(30 * time.Minute)),
|
|
},
|
|
User: &token.User{
|
|
ID: "provider1_user1",
|
|
Attributes: map[string]any{
|
|
"delete_me": true,
|
|
},
|
|
},
|
|
}
|
|
|
|
tkn, err := srv.Authenticator.TokenService().Token(claims)
|
|
assert.NoError(t, err)
|
|
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, tkn), http.NoBody)
|
|
assert.NoError(t, err)
|
|
req.SetBasicAuth("admin", "bad-password")
|
|
resp, err = client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
|
|
// unknown user: deletion is idempotent, so a valid (signed) delete_me token for a user with
|
|
// no stored data is a no-op success rather than an error
|
|
badClaimsUser := claims
|
|
badClaimsUser.User.ID = "no-such-id"
|
|
tkn, err = srv.Authenticator.TokenService().Token(badClaimsUser)
|
|
assert.NoError(t, err)
|
|
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, tkn), http.NoBody)
|
|
assert.NoError(t, err)
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err = client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode, resp.Status)
|
|
badClaimsUser.User.ID = "provider1_user1"
|
|
|
|
// try without deleteme flag
|
|
badClaimsWithoutDeleteMe := claims
|
|
badClaimsWithoutDeleteMe.User.SetBoolAttr("delete_me", false)
|
|
tkn, err = srv.Authenticator.TokenService().Token(badClaimsWithoutDeleteMe)
|
|
assert.NoError(t, err)
|
|
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, tkn), http.NoBody)
|
|
assert.NoError(t, err)
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err = client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
b, err := io.ReadAll(resp.Body)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Contains(t, string(b), "can't use provided token")
|
|
badClaimsWithoutDeleteMe.User.SetBoolAttr("delete_me", true)
|
|
|
|
// try with wrong audience
|
|
badClaimsMultipleAudience := claims
|
|
badClaimsMultipleAudience.Audience = jwt.ClaimStrings{"remark42", "something else"}
|
|
tkn, err = srv.Authenticator.TokenService().Token(badClaimsMultipleAudience)
|
|
assert.NoError(t, err)
|
|
req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, tkn), http.NoBody)
|
|
assert.NoError(t, err)
|
|
req.SetBasicAuth("admin", "password")
|
|
resp, err = client.Do(req)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
b, err = io.ReadAll(resp.Body)
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, resp.Body.Close())
|
|
assert.Contains(t, string(b), "can't process token, claims.Audience expected to be a single element but it's not")
|
|
badClaimsMultipleAudience.Audience = jwt.ClaimStrings{"remark42"}
|
|
}
|
|
|
|
func TestAdmin_GetUserInfo(t *testing.T) {
|
|
ts, srv, teardown := startupT(t)
|
|
defer teardown()
|
|
|
|
c1 := store.Comment{Text: "test test #1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user1 name", ID: "user1"}}
|
|
c2 := store.Comment{Text: "test test #2", ParentID: "p1", Locator: store.Locator{SiteID: "remark42",
|
|
URL: "https://radio-t.com/blah"}, User: store.User{Name: "user2", ID: "user2"}}
|
|
|
|
_, err := srv.DataService.Create(c1)
|
|
assert.NoError(t, err)
|
|
_, err = srv.DataService.Create(c2)
|
|
assert.NoError(t, err)
|
|
|
|
body, code := getWithAdminAuth(t, fmt.Sprintf("%s/api/v1/admin/user/user1?site=remark42&url=https://radio-t.com/blah",
|
|
ts.URL))
|
|
assert.Equal(t, http.StatusOK, code)
|
|
u := store.User{}
|
|
err = json.Unmarshal([]byte(body), &u)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, store.User{Name: "user1 name", ID: "user1", Picture: "", IP: "823688dafca7393d24c871a2da98a84d8732e927",
|
|
Admin: false, Blocked: false, Verified: false}, u)
|
|
|
|
_, code = get(t, fmt.Sprintf("%s/api/v1/admin/user/user1?site=remark42&url=https://radio-t.com/blah", ts.URL))
|
|
assert.Equal(t, http.StatusUnauthorized, code, "no auth")
|
|
|
|
_, code = getWithAdminAuth(t, fmt.Sprintf("%s/api/v1/admin/user/userX?site=remark42&url=https://radio-t.com/blah", ts.URL))
|
|
assert.Equal(t, http.StatusBadRequest, code, "no info about user")
|
|
}
|