Files
gitea-tea/internal/app/discussion_view_test.go
T
bircni dc24231ebc test: share the first-page and clock helpers
writeFirstPage answers paged listings in app and services tests,
testNow replaces the copied clock, withoutANSI the second color
stripper, and the single-entry table test becomes a plain test.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-12 14:25:38 +02:00

192 lines
12 KiB
Go
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package app
import (
"context"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/require"
)
// plainTTY makes deps render for a terminal without color or markdown, so views compare as text.
func plainTTY(t *testing.T, deps *Dependencies, serverURL string) {
t.Helper()
deps.Streams.OutTTY = true
deps.Getenv = serverEnv(serverURL, map[string]string{"NO_COLOR": "1"})
rawMarkdown(t, *deps)
}
func TestDiscussionViews(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/repos/alice/project/issues/3":
_, _ = w.Write([]byte(`{"number":3,"title":"Crash\u001b[2J","body":"Steps:\r\n\u001b]52;c;aGk=\u0007run‮\r","html_url":"https://example.org/i/3","state":"open","user":{"login":"bob"},"created_at":"2026-01-01T00:00:00Z","labels":[{"name":"bug","color":"ff0000"}],"assignees":[{"login":"alice"}],"comments":1}`))
case "/api/v1/repos/alice/project/issues/3/dependencies", "/api/v1/repos/alice/project/issues/3/blocks", "/api/v1/repos/alice/project/pulls/4/reviews":
_, _ = w.Write([]byte(`[]`))
case "/api/v1/repos/alice/project/issues/3/comments":
_, _ = w.Write([]byte(`[{"id":79,"body":"Confirmed\u001b[31m\n\tok","user":{"login":"carol"},"created_at":"2026-01-02T00:00:00Z"}]`))
case "/api/v1/repos/alice/project/pulls/4":
_, _ = w.Write([]byte(`{"number":4,"title":"Fix","html_url":"https://example.org/p/4","state":"closed","merged":true,"merged_at":"2026-01-03T12:00:00Z","merged_by":{"login":"alice"},"user":{"login":"bob"},"created_at":"2026-01-03T00:00:00Z","base":{"ref":"main"},"head":{"ref":"fix"},"requested_reviewers":[{"login":"carol"}]}`))
default:
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
}
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
deps.Now = func() time.Time { return time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC) }
require.NoError(t, Execute(context.Background(), deps, []string{"issue", "view", "3", "-R", "alice/project", "--comments"}))
require.Equal(t, "title:\tCrash[2J\nstate:\topen\nauthor:\tbob\nlabels:\tbug\nassignees:\talice\nmilestone:\t\ndue:\t\nref:\t\nblocked by:\t\nblocks:\t\ncomments:\t1\nnumber:\t3\nurl:\thttps://example.org/i/3\n--\nSteps:\n]52;c;aGk=run\n"+
"--\nauthor:\tcarol\ncreated_at:\t2026-01-02T00:00:00Z\nid:\t79\n--\nConfirmed[31m\n\tok\n", out.String())
out.Reset()
plainTTY(t, &deps, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"issue", "view", "3", "-R", "alice/project"}))
require.Equal(t, "Crash[2J #3\nOpen • bob opened about 3 days ago • 1 comment\nLabels: bug\nAssignees: alice\n\nSteps:\n]52;c;aGk=run\n\n"+
"1 comment — use --comments to view them\n\nView this issue on Gitea: https://example.org/i/3\n", out.String())
out.Reset()
require.NoError(t, Execute(context.Background(), deps, []string{"issue", "view", "3", "-R", "alice/project", "--comments"}))
require.Contains(t, out.String(), "\n\ncarol • about 2 days ago • #79\nConfirmed[31m\n\tok\n\nView this issue on Gitea:")
out.Reset()
require.NoError(t, Execute(context.Background(), deps, []string{"pr", "view", "4", "-R", "alice/project"}))
require.Equal(t, "Fix #4\nMerged • alice merged into main from fix • about 12 hours ago • 0 comments\nReviewers: carol (Requested)\n\nNo description provided\n\nView this pull request on Gitea: https://example.org/p/4\n", out.String())
}
func TestIssueViewDependencies(t *testing.T) {
supported := true
var paths []string
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
paths = append(paths, r.URL.Path)
switch {
case r.URL.Path == "/api/v1/repos/alice/project/issues/3":
_, _ = w.Write([]byte(`{"number":3,"title":"Crash","html_url":"https://example.org/i/3","state":"open","user":{"login":"bob"},"created_at":"2026-01-03T00:00:00Z","due_date":"2026-12-24T23:59:59Z","ref":"main"}`))
case !supported:
w.WriteHeader(http.StatusNotFound)
case r.URL.Query().Get("page") != "1":
_, _ = w.Write([]byte(`[]`))
case r.URL.Path == "/api/v1/repos/alice/project/issues/3/dependencies":
_, _ = w.Write([]byte(`[{"number":1,"repository":{"full_name":"alice/project"}},{"number":5,"repository":{"full_name":"bob/lib"}},{"number":0,"title":"HIDDEN"}]`))
case r.URL.Path == "/api/v1/repos/alice/project/issues/3/blocks":
_, _ = w.Write([]byte(`[{"number":9,"repository":{"full_name":"alice/project"}}]`))
default:
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
}
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
deps.Now = func() time.Time { return time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC) }
plainTTY(t, &deps, server.URL)
args := []string{"issue", "view", "3", "-R", "alice/project"}
require.NoError(t, Execute(context.Background(), deps, args))
require.Equal(t, "Crash #3\nOpen • bob opened about 1 day ago • 0 comments\nDue: Dec 24, 2026 (in about 11 months)\nRef: main\nBlocked by: #1, bob/lib#5, hidden\nBlocks: #9\n\nNo description provided\n\nView this issue on Gitea: https://example.org/i/3\n", out.String())
out.Reset()
deps.Streams.OutTTY = false
require.NoError(t, Execute(context.Background(), deps, args))
require.Contains(t, out.String(), "\ndue:\t2026-12-24T23:59:59Z\nref:\tmain\nblocked by:\t#1, bob/lib#5, hidden\nblocks:\t#9\n")
out.Reset()
supported, paths = false, nil
require.NoError(t, Execute(context.Background(), deps, args))
require.Contains(t, out.String(), "\nblocked by:\t\nblocks:\t\n")
require.Equal(t, []string{"/api/v1/repos/alice/project/issues/3", "/api/v1/repos/alice/project/issues/3/dependencies"}, paths)
out.Reset()
paths = nil
require.NoError(t, Execute(context.Background(), deps, append(args, "--json=number")))
require.Equal(t, []string{"/api/v1/repos/alice/project/issues/3"}, paths)
}
func TestPullViewCommentsIncludeReviews(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/api/v1/repos/alice/project/pulls/4":
_, _ = w.Write([]byte(`{"number":4,"title":"Fix","html_url":"https://example.org/p/4","state":"open","mergeable":true,"user":{"login":"bob"},"created_at":"2026-01-01T00:00:00Z","base":{"ref":"main"},"head":{"ref":"fix"},"comments":1}`))
case "/api/v1/repos/alice/project/issues/4/comments":
_, _ = w.Write([]byte(`[{"body":"Looks close","user":{"login":"carol"},"created_at":"2026-01-02T00:00:00Z"}]`))
case "/api/v1/repos/alice/project/pulls/4/reviews":
writeFirstPage(w, r, `[
{"id":1,"body":"Please rename","state":"REQUEST_CHANGES","user":{"login":"alice"},"submitted_at":"2026-01-01T12:00:00Z","comments_count":1},
{"id":2,"body":"","state":"APPROVED","user":{"login":"dave"},"submitted_at":"2026-01-03T00:00:00Z"},
{"id":3,"body":"Draft","state":"PENDING","user":{"login":"erin"},"submitted_at":"2026-01-03T00:00:00Z"},
{"id":4,"body":"Great","state":"APPROVED","user":{"login":"alice"},"submitted_at":"2026-01-03T06:00:00Z"}
]`)
case "/api/v1/repos/alice/project/pulls/4/reviews/1/comments":
_, _ = w.Write([]byte(`[{"id":9,"body":"Typo here","path":"main.go","position":12,"user":{"login":"alice"},"created_at":"2026-01-01T12:00:00Z"}]`))
default:
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
w.WriteHeader(http.StatusNotFound)
}
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
deps.Now = func() time.Time { return time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC) }
require.NoError(t, Execute(context.Background(), deps, []string{"pr", "view", "4", "-R", "alice/project", "--comments"}))
require.Equal(t, "title:\tFix\nstate:\topen\nauthor:\tbob\nbase:\tmain\nhead:\tfix\nlabels:\t\nassignees:\t\nreviewers:\talice (Approved), dave (Approved)\nmilestone:\t\nchecks:\t\nmergeable:\tyes\ncomments:\t1\nnumber:\t4\nurl:\thttps://example.org/p/4\n--\n\n"+
"--\nauthor:\talice\ncreated_at:\t2026-01-01T12:00:00Z\nreview:\tREQUEST_CHANGES\n--\nPlease rename\n"+
"--\nauthor:\talice\ncreated_at:\t2026-01-01T12:00:00Z\nid:\t9\npath:\tmain.go:12\n--\nTypo here\n"+
"--\nauthor:\tcarol\ncreated_at:\t2026-01-02T00:00:00Z\n--\nLooks close\n"+
"--\nauthor:\tdave\ncreated_at:\t2026-01-03T00:00:00Z\nreview:\tAPPROVED\n--\n\n"+
"--\nauthor:\talice\ncreated_at:\t2026-01-03T06:00:00Z\nreview:\tAPPROVED\n--\nGreat\n", out.String())
out.Reset()
plainTTY(t, &deps, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"pr", "view", "4", "-R", "alice/project", "--comments"}))
require.Equal(t, "Fix #4\nOpen • bob wants to merge into main from fix • about 3 days ago • 1 comment\nReviewers: alice (Approved), dave (Approved)\nMergeable: yes\n\nNo description provided\n\n"+
"alice requested changes • about 2 days ago\nPlease rename\n\n"+
"alice commented on main.go:12 • about 2 days ago\nTypo here\n\n"+
"carol • about 2 days ago\nLooks close\n\n"+
"dave approved these changes • about 1 day ago\n\n"+
"alice approved these changes • about 18 hours ago\nGreat\n\n"+
"View this pull request on Gitea: https://example.org/p/4\n", out.String())
}
func TestPullViewStates(t *testing.T) {
for _, tc := range []struct {
name, pull, statuses, reviews, want, decision string
}{
{
name: "draft",
pull: `{"number":15,"title":"WIP: experiment","state":"open","draft":true,"mergeable":false,"user":{"login":"alice"},"base":{"ref":"main"},"head":{"ref":"experiment"}}`,
want: "WIP: experiment #15\nDraft • alice wants to merge into main from experiment • about 1 day ago • 0 comments\nMergeable: no — draft; mark it ready with: tea pr ready 15\n",
decision: "",
},
{
name: "fork with checks and conflicts",
pull: `{"number":17,"title":"Improve greeting","state":"open","mergeable":false,"user":{"login":"bob"},"base":{"ref":"main"},"head":{"ref":"greet","sha":"abc","repo":{"full_name":"bob/project"}},"requested_reviewers":[{"login":"carol"}]}`,
statuses: `[{"id":1,"context":"ci / test","status":"pending"},{"id":2,"context":"lint","status":"failure"}]`,
reviews: `[{"id":4,"state":"REQUEST_CHANGES","user":{"login":"alice"}}]`,
want: "Improve greeting #17\nOpen • bob wants to merge into main from bob:greet • about 1 day ago • 0 comments\nReviewers: alice (Changes requested), carol (Requested)\nChecks: 1 failing, 1 pending (tea pr checks 17)\nMergeable: no — conflicts with main\n",
decision: "changes_requested",
},
} {
t.Run(tc.name, func(t *testing.T) {
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.URL.Path == "/api/v1/repos/alice/project/pulls/15" || r.URL.Path == "/api/v1/repos/alice/project/pulls/17":
_, _ = w.Write([]byte(tc.pull[:len(tc.pull)-1] + `,"created_at":"2026-01-03T00:00:00Z","html_url":"https://example.org/p"}`))
case r.URL.Query().Get("page") != "1":
_, _ = w.Write([]byte(`[]`))
case r.URL.Path == "/api/v1/repos/alice/project/statuses/abc":
_, _ = w.Write([]byte(tc.statuses))
case tc.reviews == "":
_, _ = w.Write([]byte(`[]`))
default:
_, _ = w.Write([]byte(tc.reviews))
}
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
deps.Now = func() time.Time { return time.Date(2026, 1, 4, 0, 0, 0, 0, time.UTC) }
plainTTY(t, &deps, server.URL)
number := map[string]string{"draft": "15"}[tc.name]
if number == "" {
number = "17"
}
require.NoError(t, Execute(context.Background(), deps, []string{"pr", "view", number, "-R", "alice/project"}))
require.Equal(t, tc.want+"\nNo description provided\n\nView this pull request on Gitea: https://example.org/p\n", out.String())
out.Reset()
require.NoError(t, Execute(context.Background(), deps, []string{"pr", "view", number, "-R", "alice/project", "--json=draft,review_decision"}))
require.Contains(t, out.String(), `"review_decision": "`+tc.decision+`"`)
})
}
}