From d650bb4acdd56bf9541db82cad117a679edfb897 Mon Sep 17 00:00:00 2001 From: Dima Krasner Date: Fri, 5 Jan 2024 15:24:35 +0200 Subject: [PATCH] allow users to set their bio (#26) --- README.md | 2 + ap/actor.go | 4 +- front/bio.go | 75 +++++++++++++++++++++++++++++ front/handler.go | 4 +- front/menu.go | 3 +- front/static/users/help.gmi | 4 ++ front/user/create.go | 4 +- test/bio_test.go | 96 +++++++++++++++++++++++++++++++++++++ test/edit_test.go | 54 ++++++++++++++------- test/reply_test.go | 5 +- 10 files changed, 227 insertions(+), 24 deletions(-) create mode 100644 front/bio.go create mode 100644 test/bio_test.go diff --git a/README.md b/README.md index 3fe327d0..97baf162 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Welcome, fedinaut! localhost.localdomain:8443 is an instance of tootik, a federa 🔭 Find user 🔥 Hashtags 📊 Statistics +📜 Set bio 🔔 New post 📣 New public post 🛟 Help @@ -154,6 +155,7 @@ Users are authenticated using TLS client certificates; see [Gemini protocol spec * /users/follow sends a follow request to a user. * /users/unfollow deletes a follow request. * /users/outbox is equivalent to /outbox but also includes a link to /users/follow or /users/unfollow. +* /users/bio allows users to edit their bio. Some clients generate a certificate for / (all pages of this capsule) when /foo requests a client certificate, while others use the certificate requested by /foo only for /foo and /foo/bar. Therefore, pages that don't require authentication are also mirrored under /users: diff --git a/ap/actor.go b/ap/actor.go index e28d39dc..4bc6aa64 100644 --- a/ap/actor.go +++ b/ap/actor.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Dima Krasner +Copyright 2023, 2024 Dima Krasner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -38,4 +38,6 @@ type Actor struct { Icon Attachment `json:"icon,omitempty"` ManuallyApprovesFollowers bool `json:"manuallyApprovesFollowers"` AlsoKnownAs Audience `json:"alsoKnownAs,omitempty"` + Published Time `json:"published"` + Updated *Time `json:"updated,omitempty"` } diff --git a/front/bio.go b/front/bio.go new file mode 100644 index 00000000..ebab97ab --- /dev/null +++ b/front/bio.go @@ -0,0 +1,75 @@ +/* +Copyright 2024 Dima Krasner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package front + +import ( + "crypto/sha256" + "github.com/dimkr/tootik/front/text" + "github.com/dimkr/tootik/front/text/plain" + "net/url" + "time" + "unicode/utf8" +) + +const ( + minBioEditInterval = time.Minute * 30 + maxBioLength = 500 +) + +func bio(w text.Writer, r *request) { + if r.User == nil { + w.Redirect("/users") + return + } + + now := time.Now() + + if (r.User.Updated != nil && now.Sub(r.User.Updated.Time) < minBioEditInterval) || (r.User.Updated == nil && now.Sub(r.User.Published.Time) < minBioEditInterval) { + r.Log.Warn("Throttled request to set summary") + w.Status(40, "Please try again later") + return + } + + if r.URL.RawQuery == "" { + w.Status(10, "Summary") + return + } + + summary, err := url.QueryUnescape(r.URL.RawQuery) + if err != nil { + w.Status(40, "Bad input") + return + } + + if utf8.RuneCountInString(summary) > maxBioLength { + w.Status(40, "Summary is too long") + return + } + + if _, err := r.Exec( + "update persons set actor = json_set(actor, '$.summary', $1, '$.updated', $2) where id = $3", + plain.ToHTML(summary, nil), + now.Format(time.RFC3339Nano), + r.User.ID, + ); err != nil { + r.Log.Error("Failed to update summary", "error", err) + w.Error() + return + } + + w.Redirectf("/users/outbox/%x", sha256.Sum256([]byte(r.User.ID))) +} diff --git a/front/handler.go b/front/handler.go index 24ff51c6..5ceb9a4e 100644 --- a/front/handler.go +++ b/front/handler.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Dima Krasner +Copyright 2023, 2024 Dima Krasner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -72,6 +72,8 @@ func NewHandler(closed bool) Handler { h[regexp.MustCompile(`^/outbox/[0-9a-f]{64}$`)] = withUserMenu(userOutbox) h[regexp.MustCompile(`^/users/outbox/[0-9a-f]{64}$`)] = withUserMenu(userOutbox) + h[regexp.MustCompile(`^/users/bio$`)] = bio + h[regexp.MustCompile(`^/view/[0-9a-f]{64}$`)] = withUserMenu(view) h[regexp.MustCompile(`^/users/view/[0-9a-f]{64}$`)] = withUserMenu(view) diff --git a/front/menu.go b/front/menu.go index 5bb503e9..c9520b4c 100644 --- a/front/menu.go +++ b/front/menu.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Dima Krasner +Copyright 2023, 2024 Dima Krasner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ func writeUserMenu(w text.Writer, user *ap.Actor) { if user == nil { w.Link(fmt.Sprintf("gemini://%s/users", cfg.Domain), "🔑 Sign in") } else { + w.Link("/users/bio", "📜 Set bio") w.Link("/users/whisper", "🔔 New post") w.Link("/users/say", "📣 New public post") } diff --git a/front/static/users/help.gmi b/front/static/users/help.gmi index db83677d..26f42a0a 100644 --- a/front/static/users/help.gmi +++ b/front/static/users/help.gmi @@ -57,6 +57,10 @@ This page shows popular hashtags, allowing you to discover trends and shared int This page shows various statistics about this server and the parts of the fediverse it's connected to. +> 📜 Set bio + +Follow this link to set the short (up to 500 characters long) description that appears at the top of your profile. + > 🔔 New post Follow this link to publish a private post and send it to your followers. diff --git a/front/user/create.go b/front/user/create.go index 95e751ba..ec8411f9 100644 --- a/front/user/create.go +++ b/front/user/create.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Dima Krasner +Copyright 2023, 2024 Dima Krasner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import ( "github.com/dimkr/tootik/ap" "github.com/dimkr/tootik/cfg" "github.com/dimkr/tootik/fed/icon" + "time" ) func gen(ctx context.Context) ([]byte, []byte, error) { @@ -95,6 +96,7 @@ func Create(ctx context.Context, db *sql.DB, id, name, certHash string) (*ap.Act PublicKeyPem: string(pub), }, ManuallyApprovesFollowers: false, + Published: ap.Time{Time: time.Now()}, } body, err := json.Marshal(actor) diff --git a/test/bio_test.go b/test/bio_test.go new file mode 100644 index 00000000..747aee59 --- /dev/null +++ b/test/bio_test.go @@ -0,0 +1,96 @@ +/* +Copyright 2024 Dima Krasner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package test + +import ( + "crypto/sha256" + "fmt" + "github.com/stretchr/testify/assert" + "strings" + "testing" + "time" +) + +func TestBio_Throttled(t *testing.T) { + server := newTestServer() + defer server.Shutdown() + + assert := assert.New(t) + + summary := server.Handle("/users/bio?Hello%20world", server.Alice) + assert.Equal("40 Please try again later\r\n", summary) +} + +func TestBio_HappyFlow(t *testing.T) { + server := newTestServer() + defer server.Shutdown() + + assert := assert.New(t) + + server.Alice.Published.Time = server.Alice.Published.Time.Add(-time.Hour) + + summary := server.Handle("/users/bio?Hello%20world", server.Alice) + assert.Equal(fmt.Sprintf("30 /users/outbox/%x\r\n", sha256.Sum256([]byte(server.Alice.ID))), summary) + + outbox := server.Handle(fmt.Sprintf("/users/outbox/%x", sha256.Sum256([]byte(server.Alice.ID))), server.Bob) + assert.Contains(strings.Split(outbox, "\n"), "> Hello world") +} + +func TestBio_TooLong(t *testing.T) { + server := newTestServer() + defer server.Shutdown() + + assert := assert.New(t) + + server.Alice.Published.Time = server.Alice.Published.Time.Add(-time.Hour) + + summary := server.Handle("/users/bio?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", server.Alice) + assert.Equal("40 Summary is too long\r\n", summary) +} + +func TestBio_MultiLine(t *testing.T) { + server := newTestServer() + defer server.Shutdown() + + assert := assert.New(t) + + server.Alice.Published.Time = server.Alice.Published.Time.Add(-time.Hour) + + summary := server.Handle("/users/bio?Hello%0Aworld", server.Alice) + assert.Equal(fmt.Sprintf("30 /users/outbox/%x\r\n", sha256.Sum256([]byte(server.Alice.ID))), summary) + + outbox := strings.Split(server.Handle(fmt.Sprintf("/users/outbox/%x", sha256.Sum256([]byte(server.Alice.ID))), server.Bob), "\n") + assert.Contains(outbox, "> Hello") + assert.Contains(outbox, "> world") +} + +func TestBio_MultiLineWithLink(t *testing.T) { + server := newTestServer() + defer server.Shutdown() + + assert := assert.New(t) + + server.Alice.Published.Time = server.Alice.Published.Time.Add(-time.Hour) + + summary := server.Handle("/users/bio?Hi%21%0A%0AI%27m%20a%20friend%20of%20https%3a%2f%2flocalhost.localdomain%3a8443%2fuser%2fbob", server.Alice) + assert.Equal(fmt.Sprintf("30 /users/outbox/%x\r\n", sha256.Sum256([]byte(server.Alice.ID))), summary) + + outbox := strings.Split(server.Handle(fmt.Sprintf("/users/outbox/%x", sha256.Sum256([]byte(server.Alice.ID))), server.Bob), "\n") + assert.Contains(outbox, "> Hi!") + assert.Contains(outbox, "> I'm a friend of https://localhost.localdomain:8443/user/bob") + assert.Contains(outbox, "=> https://localhost.localdomain:8443/user/bob https://localhost.localdomain:8443/user/bob") +} diff --git a/test/edit_test.go b/test/edit_test.go index 517b1578..63f05caa 100644 --- a/test/edit_test.go +++ b/test/edit_test.go @@ -75,7 +75,8 @@ func TestEdit_HappyFlow(t *testing.T) { hash := whisper[15 : len(whisper)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20followers", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -116,7 +117,8 @@ func TestEdit_EmptyContent(t *testing.T) { hash := whisper[15 : len(whisper)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?", hash), server.Bob) assert.Equal("10 Post content\r\n", edit) @@ -147,7 +149,8 @@ func TestEdit_LongContent(t *testing.T) { hash := whisper[15 : len(whisper)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hash), server.Bob) assert.Equal("40 Post is too long\r\n", edit) @@ -178,7 +181,8 @@ func TestEdit_InvalidEscapeSequence(t *testing.T) { hash := whisper[15 : len(whisper)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%zzworld", hash), server.Bob) assert.Equal("40 Bad input\r\n", edit) @@ -264,7 +268,8 @@ func TestEdit_AddHashtag(t *testing.T) { hash := say[15 : len(say)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?%%23Hello%%20%%23world", hash), server.Alice) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -293,7 +298,8 @@ func TestEdit_RemoveHashtag(t *testing.T) { hash := say[15 : len(say)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?%%23Hello%%20world", hash), server.Alice) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -322,7 +328,8 @@ func TestEdit_KeepHashtags(t *testing.T) { hash := say[15 : len(say)-2] - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?%%23Hello%%20%%20%%23world", hash), server.Alice) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -354,7 +361,8 @@ func TestEdit_AddMention(t *testing.T) { assert.NotContains(lines, "> Hello @alice") assert.NotContains(lines, fmt.Sprintf("=> /users/outbox/%x alice", sha256.Sum256([]byte(server.Alice.ID)))) - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20%%40alice", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -385,7 +393,8 @@ func TestEdit_RemoveMention(t *testing.T) { assert.Contains(lines, "> Hello @alice") assert.Contains(lines, fmt.Sprintf("=> /users/outbox/%x alice", sha256.Sum256([]byte(server.Alice.ID)))) - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20world", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -416,7 +425,8 @@ func TestEdit_KeepMention(t *testing.T) { assert.Contains(lines, "> Hello @alice") assert.Contains(lines, fmt.Sprintf("=> /users/outbox/%x alice", sha256.Sum256([]byte(server.Alice.ID)))) - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20%%20%%40alice", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -455,7 +465,8 @@ func TestEdit_AddGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.NotContains(today, "Hello people") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20people%%20in%%20%%40people%%40other.localdomain", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -492,7 +503,8 @@ func TestEdit_RemoveGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.Contains(today, "Hello people") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20people%%20in%%20people%%40other.localdomain", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -537,7 +549,8 @@ func TestEdit_ChangeGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.NotContains(today, "Hello people") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20people%%20in%%20%%40people%%40other.localdomain", hash), server.Bob) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -579,7 +592,8 @@ func TestEdit_AddReplyGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.NotContains(today, "Hello there") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20people%%20in%%20%%40people%%40other.localdomain", replyHash), server.Carol) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", replyHash), edit) @@ -629,7 +643,8 @@ func TestEdit_ChangeReplyGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.Contains(today, "Hello there") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20adults%%20in%%20%%40adults%%40other.localdomain", replyHash), server.Carol) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", replyHash), edit) @@ -679,7 +694,8 @@ func TestEdit_RemoveReplyGroup(t *testing.T) { today := server.Handle("/users/inbox/today", server.Alice) assert.Contains(today, "Hello adults") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + _, err = server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), replyHash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?Hello%%20adults", replyHash), server.Carol) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", replyHash), edit) @@ -713,7 +729,8 @@ func TestEdit_PollAddOption(t *testing.T) { assert.NotContains(strings.Split(view, "\n"), "0 I couldn't care less") assert.NotContains(strings.Split(view, "\n"), "1 ████████ I couldn't care less") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?%%5bPOLL%%20So%%2c%%20polls%%20on%%20Station%%20are%%20pretty%%20cool%%2c%%20right%%3f%%5d%%20Nope%%20%%7c%%20Hell%%20yeah%%21%%20%%7c%%20I%%20couldn%%27t%%20care%%20less", hash), server.Alice) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) @@ -758,7 +775,8 @@ func TestEdit_RemoveQuestion(t *testing.T) { assert.NotContains(strings.Split(view, "\n"), "0 I couldn't care less") assert.NotContains(strings.Split(view, "\n"), "1 ████████ I couldn't care less") - server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + _, err := server.db.Exec("update notes set inserted = inserted - 3600, object = json_set(object, '$.published', ?) where hash = ?", time.Now().Add(-time.Hour).Format(time.RFC3339Nano), hash) + assert.NoError(err) edit := server.Handle(fmt.Sprintf("/users/edit/%s?This%%20is%%20not%%20a%%20poll", hash), server.Alice) assert.Equal(fmt.Sprintf("30 /users/view/%s\r\n", hash), edit) diff --git a/test/reply_test.go b/test/reply_test.go index 285121c2..55c01bbf 100644 --- a/test/reply_test.go +++ b/test/reply_test.go @@ -1,5 +1,5 @@ /* -Copyright 2023 Dima Krasner +Copyright 2023, 2024 Dima Krasner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -285,7 +285,8 @@ func TestReply_SelfReply(t *testing.T) { assert.Contains(view, "Hello world") assert.NotContains(view, "Welcome Bob") - server.db.Exec("update outbox set inserted = inserted - 3600 where activity->>'type' = 'Create'") + _, err := server.db.Exec("update outbox set inserted = inserted - 3600 where activity->>'type' = 'Create'") + assert.NoError(err) reply := server.Handle(fmt.Sprintf("/users/reply/%s?Welcome%%20me", hash), server.Bob) assert.Regexp("30 /users/view/[0-9a-f]{64}", reply)