mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
fix(wiki,notification): guard wiki edit renames, show real revision totals and tidy notification view
Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
6 files changed
+145
-24
No files matched your search
@@ -193,7 +193,7 @@ func (a *application) notificationWatchCommand(scope func(*cobra.Command) (notif
|
|||||||
Long: `Print notifications as they arrive or change, until Ctrl-C or --timeout. On a terminal
|
Long: `Print notifications as they arrive or change, until Ctrl-C or --timeout. On a terminal
|
||||||
it runs until stopped; otherwise it stops after a minute unless --timeout says otherwise.
|
it runs until stopped; otherwise it stops after a minute unless --timeout says otherwise.
|
||||||
With --json it prints one JSON object per line.`,
|
With --json it prints one JSON object per line.`,
|
||||||
Example: "$ tea notification watch\n# Stream new pull request notifications to a script\n$ tea notification watch --type pull --json id,repository,title | ./notify.sh",
|
Example: "$ tea notification watch\n# Stream pull request notifications to a script, starting with the current ones\n$ tea notification watch --type pull --json id,repository,title --timeout 0 | ./notify.sh",
|
||||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||||
listed, err := a.notificationOptions(opts, updated)
|
listed, err := a.notificationOptions(opts, updated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -284,10 +284,14 @@ pinned.`,
|
|||||||
if err := writeView(a, cmd, item, func(item services.Notification) terminal.View { return notificationView(item, comment, a.display()) }); err != nil {
|
if err := writeView(a, cmd, item, func(item services.Notification) terminal.View { return notificationView(item, comment, a.display()) }); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// Marking is a side effect of viewing: a token without write:notification, or an
|
||||||
|
// administrator reading another user's thread, still gets the view.
|
||||||
if item.Unread && !item.Pinned {
|
if item.Unread && !item.Pinned {
|
||||||
_, err = s.Mark(cmd.Context(), id, "read")
|
if _, err := s.Mark(cmd.Context(), id, "read"); err != nil {
|
||||||
|
a.warn(cmd, "Could not mark notification %d as read: %v", id, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,17 +384,24 @@ func notificationView(item services.Notification, comment *services.Comment, d t
|
|||||||
Title: item.Title, Meta: meta, Kind: notificationKinds[item.Type], URL: item.URL,
|
Title: item.Title, Meta: meta, Kind: notificationKinds[item.Type], URL: item.URL,
|
||||||
Badges: []terminal.Cell{{Text: titleCase(item.State), Color: stateColors[item.State]}, {Text: notificationStatus(item), Muted: true}},
|
Badges: []terminal.Cell{{Text: titleCase(item.State), Color: stateColors[item.State]}, {Text: notificationStatus(item), Muted: true}},
|
||||||
Fields: []terminal.Field{{Label: "Notification", Value: strconv.FormatInt(item.ID, 10)}, {Label: "Type", Value: notificationKinds[item.Type]}, {Label: "Repository", Value: item.Repository}},
|
Fields: []terminal.Field{{Label: "Notification", Value: strconv.FormatInt(item.ID, 10)}, {Label: "Type", Value: notificationKinds[item.Type]}, {Label: "Repository", Value: item.Repository}},
|
||||||
Body: "_No comments yet._",
|
|
||||||
}
|
}
|
||||||
|
if d.TTY {
|
||||||
|
view.Body = "_No comments yet._"
|
||||||
|
}
|
||||||
|
view.Fields = append(view.Fields,
|
||||||
|
terminal.Field{Label: "State", Value: item.State, PipedOnly: true},
|
||||||
|
terminal.Field{Label: "Status", Value: notificationStatus(item), PipedOnly: true})
|
||||||
if item.Number != 0 {
|
if item.Number != 0 {
|
||||||
view.ID = "#" + strconv.FormatInt(item.Number, 10)
|
view.ID = "#" + strconv.FormatInt(item.Number, 10)
|
||||||
}
|
}
|
||||||
if comment != nil {
|
if comment != nil {
|
||||||
when := ""
|
when := ""
|
||||||
if created := terminal.ParseTime(comment.CreatedAt); !created.IsZero() {
|
if created := terminal.ParseTime(comment.CreatedAt); d.TTY && !created.IsZero() {
|
||||||
when = ", " + terminal.RelativeTime(d.Now, created)
|
when = ", " + terminal.RelativeTime(d.Now, created)
|
||||||
}
|
}
|
||||||
view.Fields = append(view.Fields, terminal.Field{Label: "Latest comment", Value: comment.Author + when})
|
commented := timeField("Commented", comment.CreatedAt, d)
|
||||||
|
commented.PipedOnly = true
|
||||||
|
view.Fields = append(view.Fields, terminal.Field{Label: "Latest comment", Value: comment.Author + when}, commented)
|
||||||
view.Body = comment.Body
|
view.Body = comment.Body
|
||||||
}
|
}
|
||||||
return view
|
return view
|
||||||
|
|||||||
@@ -155,3 +155,23 @@ func TestNotificationMarkAllNamesTheBeforeCutoff(t *testing.T) {
|
|||||||
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "mark-read", "--all", "--before=1h"}))
|
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "mark-read", "--all", "--before=1h"}))
|
||||||
require.Contains(t, withoutANSI(diagnostics.String()), "No unread notifications updated before 2028-02-01 11:00 in your inbox")
|
require.Contains(t, withoutANSI(diagnostics.String()), "No unread notifications updated before 2028-02-01 11:00 in your inbox")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNotificationViewSurvivesARefusedMark(t *testing.T) {
|
||||||
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodPatch:
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
_, _ = w.Write([]byte(`{"message":"token does not have write:notification scope"}`))
|
||||||
|
default:
|
||||||
|
_, _ = w.Write([]byte(`{"id":12,"unread":true,"repository":{"full_name":"alice/project"},"subject":{"title":"Improve greeting","type":"Pull","state":"open","url":"https://gitea.test/api/v1/repos/alice/project/issues/17"}}`))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
deps, out, diagnostics := serverDeps(t, server.URL)
|
||||||
|
require.NoError(t, Execute(context.Background(), deps, []string{"notification", "view", "12"}))
|
||||||
|
require.Contains(t, out.String(), "title:\tImprove greeting\n")
|
||||||
|
require.Contains(t, out.String(), "state:\topen\n")
|
||||||
|
require.Contains(t, out.String(), "status:\tunread\n")
|
||||||
|
require.NotContains(t, out.String(), "_No comments yet._")
|
||||||
|
require.Contains(t, diagnostics.String(), "Could not mark notification 12 as read")
|
||||||
|
}
|
||||||
@@ -120,7 +120,7 @@ func (a *application) wikiRevisionsCommand() *cobra.Command {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
items, err := (services.Wiki{Client: target.client, Repository: target.Repo}).Revisions(cmd.Context(), args[0], page)
|
revisions, err := (services.Wiki{Client: target.client, Repository: target.Repo}).Revisions(cmd.Context(), args[0], page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ func (a *application) wikiRevisionsCommand() *cobra.Command {
|
|||||||
},
|
},
|
||||||
Context: "revisions of wiki page " + strconv.Quote(args[0]), Empty: "no revisions of wiki page " + strconv.Quote(args[0]),
|
Context: "revisions of wiki page " + strconv.Quote(args[0]), Empty: "no revisions of wiki page " + strconv.Quote(args[0]),
|
||||||
}
|
}
|
||||||
return writeTable(a, cmd, items, spec, listPage{Number: page})
|
return writeTable(a, cmd, revisions.Items, spec, servicePage(revisions, page))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
positiveIntVar(command, &page, "page", 1, math.MaxInt, "Revision page (server controls page size)")
|
positiveIntVar(command, &page, "page", 1, math.MaxInt, "Revision page (server controls page size)")
|
||||||
@@ -280,11 +280,11 @@ $ tea wiki edit Home --editor`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item, err := service.Edit(cmd.Context(), args[0], services.WikiOptions{Title: content.title, Body: body, Message: content.message})
|
item, renamed, err := service.Edit(cmd.Context(), args[0], services.WikiOptions{Title: content.title, Body: body, Message: content.message})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if item.Title != args[0] {
|
if renamed {
|
||||||
a.done(cmd, "Renamed wiki page %q to %q in %s", args[0], item.Title, target.Repo)
|
a.done(cmd, "Renamed wiki page %q to %q in %s", args[0], item.Title, target.Repo)
|
||||||
} else {
|
} else {
|
||||||
a.done(cmd, "Edited wiki page %q in %s", item.Title, target.Repo)
|
a.done(cmd, "Edited wiki page %q in %s", item.Title, target.Repo)
|
||||||
|
|||||||
@@ -20,12 +20,19 @@ func TestWikiEditRenamesAndKeepsTheContent(t *testing.T) {
|
|||||||
_, _ = w.Write([]byte(`{"title":"New Title","content_base64":"S2VlcCBtZQ=="}`))
|
_, _ = w.Write([]byte(`{"title":"New Title","content_base64":"S2VlcCBtZQ=="}`))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if r.URL.Path == "/api/v1/repos/alice/x/wiki/page/New Title" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
_, _ = w.Write([]byte(`{"title":"Old Title","content_base64":"S2VlcCBtZQ=="}`))
|
_, _ = w.Write([]byte(`{"title":"Old Title","content_base64":"S2VlcCBtZQ=="}`))
|
||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
deps, out, diagnostics := repoTTYDeps(t, server.URL)
|
deps, out, diagnostics := repoTTYDeps(t, server.URL)
|
||||||
require.NoError(t, Execute(context.Background(), deps, []string{"wiki", "edit", "Old Title", "--title", "New Title", "-R", "alice/x", "--no-input"}))
|
require.NoError(t, Execute(context.Background(), deps, []string{"wiki", "edit", "Old Title", "--title", "New Title", "-R", "alice/x", "--no-input"}))
|
||||||
require.Equal(t, []string{"GET /api/v1/repos/alice/x/wiki/page/Old Title", "PATCH /api/v1/repos/alice/x/wiki/page/Old Title"}, requests)
|
require.Equal(t, []string{
|
||||||
|
"GET /api/v1/repos/alice/x/wiki/page/Old Title", "GET /api/v1/repos/alice/x/wiki/page/Old Title",
|
||||||
|
"GET /api/v1/repos/alice/x/wiki/page/New Title", "PATCH /api/v1/repos/alice/x/wiki/page/Old Title",
|
||||||
|
}, requests)
|
||||||
require.Empty(t, out.String())
|
require.Empty(t, out.String())
|
||||||
require.Equal(t, "✓ Renamed wiki page \"Old Title\" to \"New Title\" in alice/x\n", diagnostics.String())
|
require.Equal(t, "✓ Renamed wiki page \"Old Title\" to \"New Title\" in alice/x\n", diagnostics.String())
|
||||||
err := Execute(context.Background(), deps, []string{"wiki", "edit", "Home", "-R", "alice/x"})
|
err := Execute(context.Background(), deps, []string{"wiki", "edit", "Home", "-R", "alice/x"})
|
||||||
@@ -65,3 +72,43 @@ func TestWikiViewAndCreate(t *testing.T) {
|
|||||||
require.Equal(t, "https://gitea.test/alice/x/wiki/Page-Two\n", out.String())
|
require.Equal(t, "https://gitea.test/alice/x/wiki/Page-Two\n", out.String())
|
||||||
require.Equal(t, "✓ Created wiki page \"Page Two\" in alice/x\n", diagnostics.String())
|
require.Equal(t, "✓ Created wiki page \"Page Two\" in alice/x\n", diagnostics.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWikiEditRefusesToOverwriteOrCreate(t *testing.T) {
|
||||||
|
patched := 0
|
||||||
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch {
|
||||||
|
case r.Method == http.MethodPatch:
|
||||||
|
patched++
|
||||||
|
_, _ = w.Write([]byte(`{"title":"Getting Started","content_base64":"eA=="}`))
|
||||||
|
case r.URL.Path == "/api/v1/repos/alice/x/wiki/page/Draft":
|
||||||
|
_, _ = w.Write([]byte(`{"title":"Draft","content_base64":"ZHJhZnQ="}`))
|
||||||
|
case r.URL.Path == "/api/v1/repos/alice/x/wiki/page/Page B":
|
||||||
|
_, _ = w.Write([]byte(`{"title":"Page B","content_base64":"Yg=="}`))
|
||||||
|
case r.URL.Path == "/api/v1/repos/alice/x/wiki/page/Getting-Started":
|
||||||
|
_, _ = w.Write([]byte(`{"title":"Getting Started","content_base64":"Zw=="}`))
|
||||||
|
default:
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
deps, _, diagnostics := repoTTYDeps(t, server.URL)
|
||||||
|
err := Execute(context.Background(), deps, []string{"wiki", "edit", "Draft", "--title", "Page B", "-R", "alice/x", "--no-input"})
|
||||||
|
require.ErrorContains(t, err, `wiki page "Page B" already exists in alice/x`)
|
||||||
|
err = Execute(context.Background(), deps, []string{"wiki", "edit", "Drfat", "--body", "x", "-R", "alice/x", "--no-input"})
|
||||||
|
require.ErrorContains(t, err, `wiki page "Drfat" not found`)
|
||||||
|
require.Zero(t, patched, "neither refused edit may reach the PATCH")
|
||||||
|
// Editing by the URL slug is not a rename.
|
||||||
|
require.NoError(t, Execute(context.Background(), deps, []string{"wiki", "edit", "Getting-Started", "--body", "x", "-R", "alice/x", "--no-input"}))
|
||||||
|
require.Equal(t, "✓ Edited wiki page \"Getting Started\" in alice/x\n", diagnostics.String())
|
||||||
|
require.Equal(t, 1, patched)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWikiRevisionsCountsTheWholeHistory(t *testing.T) {
|
||||||
|
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_, _ = w.Write([]byte(`{"commits":[{"sha":"abc","message":"Edit","author":{"name":"alice","date":"2026-10-11T18:00:00Z"}}],"count":56}`))
|
||||||
|
}))
|
||||||
|
defer server.Close()
|
||||||
|
deps, out, _ := repoTTYDeps(t, server.URL)
|
||||||
|
require.NoError(t, Execute(context.Background(), deps, []string{"wiki", "revisions", "Home", "-R", "alice/x"}))
|
||||||
|
require.Contains(t, out.String(), "Showing 1 of 56 revisions of wiki page \"Home\" · next: --page 2")
|
||||||
|
}
|
||||||
+39
-11
@@ -150,22 +150,45 @@ func (s Wiki) Create(ctx context.Context, opts WikiOptions) (WikiPage, error) {
|
|||||||
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
|
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
|
||||||
return c.Wiki.CreatePage(ctx, owner, repo, wikiOptions(opts))
|
return c.Wiki.CreatePage(ctx, owner, repo, wikiOptions(opts))
|
||||||
})
|
})
|
||||||
|
if IsNotFound(err) {
|
||||||
|
// Gitea answers a bare 404 when the wiki is disabled or the repository is missing, which
|
||||||
|
// says nothing about the page being created.
|
||||||
|
repository, checkErr := (Repositories{Client: s.Client}).get(ctx, s.Repository)
|
||||||
|
if checkErr != nil {
|
||||||
|
return WikiPage{}, checkErr
|
||||||
|
}
|
||||||
|
if !repository.HasWiki {
|
||||||
|
return WikiPage{}, fmt.Errorf("the wiki of %s is disabled; turn it on with: tea repo edit --enable-wiki -R %s", s.Repository, s.Repository)
|
||||||
|
}
|
||||||
|
}
|
||||||
return item, Classify(err, "wiki page", strconv.Quote(opts.Title), s.Repository)
|
return item, Classify(err, "wiki page", strconv.Quote(opts.Title), s.Repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit replaces page content and optionally changes its title.
|
// Edit replaces page content and optionally changes its title. It reports whether the title
|
||||||
func (s Wiki) Edit(ctx context.Context, name string, opts WikiOptions) (WikiPage, error) {
|
// changed. Gitea's edit creates a page that does not exist and overwrites one the new title
|
||||||
|
// names, so both are checked first.
|
||||||
|
func (s Wiki) Edit(ctx context.Context, name string, opts WikiOptions) (WikiPage, bool, error) {
|
||||||
|
current, err := s.View(ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return WikiPage{}, false, err
|
||||||
|
}
|
||||||
|
renamed := opts.Title != "" && opts.Title != current.Title
|
||||||
if opts.Title == "" {
|
if opts.Title == "" {
|
||||||
current, err := s.View(ctx, name)
|
|
||||||
if err != nil {
|
|
||||||
return WikiPage{}, err
|
|
||||||
}
|
|
||||||
opts.Title = current.Title
|
opts.Title = current.Title
|
||||||
}
|
}
|
||||||
|
if renamed {
|
||||||
|
existing, err := s.View(ctx, opts.Title)
|
||||||
|
if err == nil && existing.Title != current.Title {
|
||||||
|
return WikiPage{}, false, &ConflictError{Reason: ReasonExists, Kind: "wiki page", Key: strconv.Quote(opts.Title), Scope: s.Repository}
|
||||||
|
}
|
||||||
|
if err != nil && !IsNotFound(err) {
|
||||||
|
return WikiPage{}, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
|
item, err := s.page(ctx, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiPage, *gitea.Response, error) {
|
||||||
return c.Wiki.EditPage(ctx, owner, repo, name, wikiOptions(opts))
|
return c.Wiki.EditPage(ctx, owner, repo, name, wikiOptions(opts))
|
||||||
})
|
})
|
||||||
return item, Classify(err, "wiki page", strconv.Quote(name), s.Repository)
|
return item, renamed, Classify(err, "wiki page", strconv.Quote(name), s.Repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes a named wiki page.
|
// Delete removes a named wiki page.
|
||||||
@@ -176,13 +199,18 @@ func (s Wiki) Delete(ctx context.Context, name string) error {
|
|||||||
return Classify(err, "wiki page", strconv.Quote(name), s.Repository)
|
return Classify(err, "wiki page", strconv.Quote(name), s.Repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revisions returns one server-paginated page of wiki commits.
|
// wikiRevisionPageSize is Gitea's default number of wiki commits per page ([git] COMMITS_RANGE_SIZE).
|
||||||
func (s Wiki) Revisions(ctx context.Context, name string, page int) ([]WikiRevision, error) {
|
const wikiRevisionPageSize = 50
|
||||||
|
|
||||||
|
// Revisions returns one server-paginated page of wiki commits and the total the server counted.
|
||||||
|
func (s Wiki) Revisions(ctx context.Context, name string, page int) (Page[WikiRevision], error) {
|
||||||
revisions, err := repoCall(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiCommitList, *gitea.Response, error) {
|
revisions, err := repoCall(ctx, s.Client, s.Repository, func(ctx context.Context, c *gitea.Client, owner, repo string) (*gitea.WikiCommitList, *gitea.Response, error) {
|
||||||
return c.Wiki.GetPageRevisions(ctx, owner, repo, name, gitea.ListWikiPageRevisionsOptions{Page: page})
|
return c.Wiki.GetPageRevisions(ctx, owner, repo, name, gitea.ListWikiPageRevisionsOptions{Page: page})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return Page[WikiRevision]{}, err
|
||||||
}
|
}
|
||||||
return convertAll(revisions.WikiCommits, wikiRevisionFrom), nil
|
items := convertAll(revisions.WikiCommits, wikiRevisionFrom)
|
||||||
|
total := int(revisions.Count)
|
||||||
|
return Page[WikiRevision]{Items: items, Total: total, Counted: true, More: (page-1)*wikiRevisionPageSize+len(items) < total}, nil
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ func TestWikiContentAndNamedPagePath(t *testing.T) {
|
|||||||
_, _ = w.Write([]byte(`{"title":"Name with spaces","content_base64":"w7xuaWNvZGUKdGV4dA==","last_commit":{"sha":"abc","author":{"name":"Alice","email":"author@example.test","date":"2026-10-04T00:00:00Z"}}}`))
|
_, _ = w.Write([]byte(`{"title":"Name with spaces","content_base64":"w7xuaWNvZGUKdGV4dA==","last_commit":{"sha":"abc","author":{"name":"Alice","email":"author@example.test","date":"2026-10-04T00:00:00Z"}}}`))
|
||||||
}))
|
}))
|
||||||
service := Wiki{Client: client, Repository: "alice/project"}
|
service := Wiki{Client: client, Repository: "alice/project"}
|
||||||
item, err := service.Edit(context.Background(), "Name with spaces", WikiOptions{Body: "ünicode\ntext"})
|
item, _, err := service.Edit(context.Background(), "Name with spaces", WikiOptions{Body: "ünicode\ntext"})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "ünicode\ntext", item.Body)
|
require.Equal(t, "ünicode\ntext", item.Body)
|
||||||
require.Equal(t, "Alice", item.Author)
|
require.Equal(t, "Alice", item.Author)
|
||||||
@@ -96,7 +96,9 @@ func TestWikiListAndRevisionsMapCommits(t *testing.T) {
|
|||||||
require.Equal(t, []WikiPage{{Title: "Draft"}}, last.Items)
|
require.Equal(t, []WikiPage{{Title: "Draft"}}, last.Items)
|
||||||
revisions, err := service.Revisions(context.Background(), "Home", 3)
|
revisions, err := service.Revisions(context.Background(), "Home", 3)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []WikiRevision{{SHA: "abc", Message: "Edit", Author: "Alice", CreatedAt: "2026-10-04T00:00:00Z"}, {SHA: "def"}}, revisions)
|
require.Equal(t, []WikiRevision{{SHA: "abc", Message: "Edit", Author: "Alice", CreatedAt: "2026-10-04T00:00:00Z"}, {SHA: "def"}}, revisions.Items)
|
||||||
|
require.True(t, revisions.Counted)
|
||||||
|
require.Equal(t, 2, revisions.Total)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWikiCreateAndDelete(t *testing.T) {
|
func TestWikiCreateAndDelete(t *testing.T) {
|
||||||
@@ -119,3 +121,16 @@ func TestWikiCreateAndDelete(t *testing.T) {
|
|||||||
require.Equal(t, WikiPage{Title: "New", Body: "body", CommitCount: 1, Sidebar: "side", Footer: "foot"}, page)
|
require.Equal(t, WikiPage{Title: "New", Body: "body", CommitCount: 1, Sidebar: "side", Footer: "foot"}, page)
|
||||||
require.NoError(t, service.Delete(context.Background(), "Old page"))
|
require.NoError(t, service.Delete(context.Background(), "Old page"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWikiCreateExplainsADisabledWiki(t *testing.T) {
|
||||||
|
client := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "/api/v1/repos/alice/project" {
|
||||||
|
_, _ = w.Write([]byte(`{"full_name":"alice/project","has_wiki":false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.NotFound(w, r)
|
||||||
|
}))
|
||||||
|
_, err := (Wiki{Client: client, Repository: "alice/project"}).Create(context.Background(), WikiOptions{Title: "New", Body: "body"})
|
||||||
|
require.ErrorContains(t, err, "the wiki of alice/project is disabled")
|
||||||
|
require.NotErrorAs(t, err, new(*NotFoundError))
|
||||||
|
}
|
||||||
Reference in new issue
Block a user