Files
gitea-tea/internal/app/notifications_test.go
T
bircni 390e8e04f6 fix(notification): word --all confirmations and flag values clearly
notification mark-read --all asked "Marked as read all … ?"; each command now asks in its own words, such as "Pin all unread notifications in your inbox?". The status and type filters list their values in help, the api parameter flags read like gh's, and tea auth login --help aligns its method list.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-11 23:50:47 +02:00

146 lines
7.5 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package app
import (
"context"
"net/http"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestNotificationBoundsShareClockAndReachServer(t *testing.T) {
now := time.Date(2028, 2, 1, 12, 0, 0, 0, time.UTC)
calls := 0
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
require.Equal(t, "/api/v1/notifications", r.URL.Path)
require.Equal(t, "2028-01-31T12:00:00Z", r.URL.Query().Get("since"))
require.Equal(t, "2028-02-01T11:00:00Z", r.URL.Query().Get("before"))
require.Equal(t, []string{"unread", "pinned"}, r.URL.Query()["status-types"])
_, _ = w.Write([]byte(`[]`))
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
reads := 0
deps.Now = func() time.Time { reads++; return now }
err := Execute(context.Background(), deps, []string{"notification", "list", "--mine", "--since=24h", "--before=1h", "--json"})
require.NoError(t, err)
require.Equal(t, 1, reads)
require.Equal(t, 1, calls)
require.JSONEq(t, `[]`, out.String())
}
// notificationThreads is a page of two notification threads as Gitea sends them.
const notificationThreads = `[
{"id":12,"unread":true,"repository":{"full_name":"alice/project"},"updated_at":"2026-10-11T18:00:00Z",
"subject":{"title":"Improve greeting","type":"Pull","state":"open","html_url":"https://gitea.test/alice/project/pulls/17","url":"https://gitea.test/api/v1/repos/alice/project/issues/17"}},
{"id":3,"unread":false,"pinned":true,"repository":{"full_name":"alice/project"},"updated_at":"2026-10-11T17:00:00Z",
"subject":{"title":"Improve error messages","type":"Issue","state":"closed","html_url":"https://gitea.test/alice/project/issues/8","url":"https://gitea.test/api/v1/repos/alice/project/issues/8"}}
]`
func TestNotificationsListTheInboxByDefault(t *testing.T) {
var path atomic.Value
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path.Store(r.URL.Path)
w.Header().Set("X-Total-Count", "2")
_, _ = w.Write([]byte(notificationThreads))
}))
defer server.Close()
for _, tc := range []struct {
args []string
git map[string]string
path string
}{
{nil, nil, "/api/v1/notifications"},
{nil, map[string]string{"remote": "origin", "remote get-url origin": server.URL + "/alice/project.git"}, "/api/v1/notifications"},
{[]string{"-R", "alice/project"}, nil, "/api/v1/repos/alice/project/notifications"},
{[]string{"--this-repo"}, map[string]string{"remote": "origin", "remote get-url origin": server.URL + "/bob/fork.git"}, "/api/v1/repos/bob/fork/notifications"},
} {
deps, _, _ := serverDeps(t, server.URL)
deps.Git = &fakeGit{results: tc.git}
require.NoError(t, Execute(context.Background(), deps, append([]string{"notification", "list"}, tc.args...)), tc.args)
require.Equal(t, tc.path, path.Load(), tc.args)
}
deps, out, _ := serverDeps(t, server.URL)
deps.Streams.OutTTY, deps.Streams.Width = true, 100
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "list"}))
require.Equal(t, `Showing 2 of 2 unread or pinned notifications in your inbox
ID TYPE REPOSITORY SUBJECT STATE UPDATED
12 pull alice/project #17 Improve greeting open about 2 hours ago
3 issue alice/project #8 Improve error messages closed about 3 hours ago
`, stripLinks(withoutANSI(out.String())))
deps, out, _ = serverDeps(t, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "list"}))
require.Equal(t, "12\tpull\talice/project\t#17 Improve greeting\topen\tunread\t2026-10-11T18:00:00Z\thttps://gitea.test/alice/project/pulls/17\n"+
"3\tissue\talice/project\t#8 Improve error messages\tclosed\tpinned\t2026-10-11T17:00:00Z\thttps://gitea.test/alice/project/issues/8\n", out.String())
}
func TestNotificationWatchStreamsOneJSONObjectPerLine(t *testing.T) {
var polls atomic.Int32
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
if polls.Add(1) == 1 {
_, _ = w.Write([]byte(`[{"id":1,"subject":{"title":"First"}}]`))
return
}
_, _ = w.Write([]byte(`[{"id":1,"subject":{"title":"First"}},{"id":2,"subject":{"title":"Second"}}]`))
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "watch", "--json", "id,title", "--interval", "20ms", "--timeout", "500ms"}))
require.Equal(t, "{\"id\":1,\"title\":\"First\"}\n{\"id\":2,\"title\":\"Second\"}\n", out.String())
}
func TestNotificationMutationsReportWhatChanged(t *testing.T) {
var marks []string
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodPatch && r.URL.Path == "/api/v1/notifications/threads/12":
marks = append(marks, r.URL.Query().Get("to-status"))
_, _ = w.Write([]byte(`{"id":12,"repository":{"full_name":"alice/project"},"subject":{"type":"Pull","url":"https://gitea.test/api/v1/repos/alice/project/issues/17"}}`))
case r.Method == http.MethodGet && r.URL.Path == "/api/v1/notifications/threads/12":
_, _ = w.Write([]byte(`{"id":12,"unread":true,"repository":{"full_name":"alice/project"},"subject":{"title":"Improve greeting","type":"Pull","state":"open","html_url":"https://gitea.test/alice/project/pulls/17","url":"https://gitea.test/api/v1/repos/alice/project/issues/17","latest_comment_url":"https://gitea.test/api/v1/repos/alice/project/issues/comments/9"}}`))
case r.URL.Path == "/api/v1/repos/alice/project/issues/comments/9":
_, _ = w.Write([]byte(`{"id":9,"body":"Looks good to me","user":{"login":"bob"}}`))
case r.Method == http.MethodPut && r.URL.Path == "/api/v1/notifications":
marks = append(marks, "all "+r.URL.Query().Get("to-status"))
_, _ = w.Write([]byte(`[{"id":12},{"id":3}]`))
default:
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
}
}))
defer server.Close()
deps, out, diagnostics := serverDeps(t, server.URL)
deps.Streams.OutTTY = true
for _, tc := range []struct {
args []string
want string
}{
{[]string{"pin", "12"}, "✓ Pinned notification 12 (alice/project#17)\n"},
{[]string{"unpin", "#12"}, "✓ Unpinned notification 12 (alice/project#17)\n"},
{[]string{"mark-read", "12"}, "✓ Marked notification 12 (alice/project#17) as read\n"},
{[]string{"mark-read", "--all", "--yes"}, "✓ Marked 2 notifications as read\n"},
} {
diagnostics.Reset()
require.NoError(t, Execute(context.Background(), deps, append([]string{"notification"}, tc.args...)), tc.args)
require.Equal(t, tc.want, withoutANSI(diagnostics.String()), tc.args)
}
require.Equal(t, []string{"pinned", "read", "read", "all read"}, marks)
prompt := &scriptedPrompter{accept: true}
deps.Prompter, deps.Streams.InTTY, deps.Streams.ErrTTY = prompt, true, true
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "pin", "--all"}))
require.Equal(t, []string{"Pin all unread notifications in your inbox?"}, prompt.confirmations)
deps.Streams.InTTY, deps.Streams.ErrTTY = false, false
marks = nil
out.Reset()
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "view", "12"}))
require.Contains(t, withoutANSI(out.String()), "Latest comment: bob\n\n Looks good to me\n\nView this pull request on Gitea: https://gitea.test/alice/project/pulls/17\n")
require.Equal(t, []string{"read"}, marks, "viewing marks an unread notification as read")
}