Files
gitea-tea/internal/app/milestone_create_form_test.go
T
bircni ae0ea27bb0 fix: reconcile the issues stream and remove the legacy output shims
Use one canPrompt and pullNumberArguments for the comment commands, set
display preferences instead of the retired environment variables in
tests, and delete writeList, writeItem, batch, table, writeRecord(s),
the legacy column map, confirm, require, validateMilestoneFlags and
listWindow now that every command uses their replacements.

Signed-off-by: bircni <bircni@icloud.com>
2026-10-12 00:19:35 +02:00

165 lines
8.1 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package app
import (
"context"
"encoding/json"
"fmt"
"net/http"
"testing"
"time"
"gitea.dev/tea/internal/clierr"
"gitea.dev/tea/internal/terminal"
"github.com/stretchr/testify/require"
)
type milestoneFormFunc func(context.Context) (terminal.MilestoneFormValues, error)
func (f milestoneFormFunc) EditMilestone(ctx context.Context, _ string, _ terminal.MilestoneFormValues) (terminal.MilestoneFormValues, error) {
return f(ctx)
}
func TestMilestoneCreateInteractiveForm(t *testing.T) {
var body map[string]any
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/api/v1/repos/alice/project/milestones", r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
_, _ = w.Write([]byte(`{"id":9,"title":"Release","description":"Ship it","state":"open","due_on":"2026-12-31T00:00:00Z"}`))
}))
defer server.Close()
deps, out, diagnostics := serverDeps(t, server.URL)
deps.Streams.InTTY, deps.Streams.ErrTTY = true, true
due := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)
deps.MilestoneForm = milestoneFormFunc(func(context.Context) (terminal.MilestoneFormValues, error) {
return terminal.MilestoneFormValues{Title: "Release", Description: "Ship it", Deadline: &due}, nil
})
require.NoError(t, Execute(context.Background(), deps, []string{"milestone", "create", "-R", "alice/project", "--json=id,title,description", "--interactive"}))
require.JSONEq(t, `{"id":9,"title":"Release","description":"Ship it"}`, out.String())
require.Empty(t, diagnostics.String())
require.Equal(t, "Release", body["title"])
require.Equal(t, "Ship it", body["description"])
require.Equal(t, "2026-12-31T00:00:00Z", body["due_on"])
out.Reset()
deps.MilestoneForm = milestoneFormFunc(func(context.Context) (terminal.MilestoneFormValues, error) {
t.Fatal("form should not run when create flags are set")
return terminal.MilestoneFormValues{}, nil
})
require.Equal(t, clierr.Usage, clierr.ExitCode(Execute(context.Background(), deps, []string{"milestone", "create", "-R", "alice/project", "--no-input"})))
require.Empty(t, out.String())
require.NoError(t, Execute(context.Background(), deps, []string{"milestone", "create", "-R", "alice/project", "--title", "Flagged", "--json=id", "--interactive"}))
}
func TestMilestoneCreateState(t *testing.T) {
var payload map[string]any
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
require.Equal(t, "/api/v1/repos/alice/project/milestones", r.URL.Path)
require.NoError(t, json.NewDecoder(r.Body).Decode(&payload))
_, _ = w.Write([]byte(`{"id":9,"title":"Done","state":"closed"}`))
}))
defer server.Close()
deps, out, _ := serverDeps(t, server.URL)
require.NoError(t, Execute(context.Background(), deps, []string{"milestone", "create", "-R", "alice/project", "--title", "Done", "--state", "closed", "--json=id,state", "--no-input"}))
require.Equal(t, "closed", payload["state"])
require.JSONEq(t, `{"id":9,"state":"closed"}`, out.String())
}
func TestMilestoneTablesViewsAndDoneLines(t *testing.T) {
var changed []string
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method + " " + r.URL.Path {
case "GET /api/v1/repos/alice/project/milestones":
writeMilestones(w, r, `[{"id":5,"title":"later","state":"open"},{"id":4,"title":"v0.9","state":"closed","closed_issues":3},`+
`{"id":3,"title":"v1.0","state":"open","description":"First release","due_on":"2026-12-31T00:00:00Z","open_issues":3,"closed_issues":1}]`)
case "GET /api/v1/repos/alice/project/issues/1", "GET /api/v1/repos/alice/project/issues/2":
_, _ = w.Write([]byte(`{"number":1,"milestone":{"id":4,"title":"v0.9"}}`))
case "PATCH /api/v1/repos/alice/project/issues/1", "PATCH /api/v1/repos/alice/project/issues/2":
changed = append(changed, r.Method+" "+r.URL.Path)
_, _ = fmt.Fprintf(w, `{"number":%s}`, r.URL.Path[len(r.URL.Path)-1:])
default:
changed = append(changed, r.Method+" "+r.URL.Path)
_, _ = w.Write([]byte(`{"id":3,"title":"v1.0","state":"closed"}`))
}
}))
defer server.Close()
deps, out, diagnostics := serverDeps(t, server.URL)
deps.Now = func() time.Time { return time.Date(2026, 10, 11, 20, 0, 0, 0, time.UTC) }
deps.Getenv = serverEnv(server.URL, map[string]string{"NO_COLOR": "1"})
plainOutput(t, deps)
run := func(tty bool, args ...string) {
t.Helper()
out.Reset()
diagnostics.Reset()
deps.Streams.OutTTY = tty
require.NoError(t, Execute(context.Background(), deps, append(args, "-R", "alice/project", "--no-input")))
}
run(true, "milestone", "list", "--state", "all")
require.Equal(t, "Showing 3 of 3 milestones in alice/project\n\n"+
"ID TITLE STATE DUE PROGRESS\n"+
"3 v1.0 open in about 2 months 1/4 (25%)\n"+
"5 later open 0/0\n"+
"4 v0.9 closed 3/3 (100%)\n", out.String())
run(false, "milestone", "list", "--state", "all")
require.Equal(t, "3\tv1.0\topen\t2026-12-31T00:00:00Z\t1/4 (25%)\n5\tlater\topen\t\t0/0\n4\tv0.9\tclosed\t\t3/3 (100%)\n", out.String())
run(true, "milestone", "view", "v1.0")
require.Equal(t, "v1.0 #3\nOpen • due Dec 31, 2026 (in about 2 months) • 1 of 4 closed (25%)\n\nFirst release\n\n"+
"View this milestone on Gitea: "+server.URL+"/alice/project/milestone/3\n", out.String())
run(true, "milestone", "close", "v1.0", "v0.9")
require.Equal(t, "✓ Closed milestone \"v1.0\" in alice/project\n! Milestone \"v0.9\" in alice/project is already closed\n", diagnostics.String())
run(true, "milestone", "issue", "add", "v1.0", "1", "#2")
require.Equal(t, "✓ Added #1, #2 to milestone \"v1.0\" in alice/project\n", diagnostics.String())
run(true, "milestone", "issue", "remove", "v0.9", "1")
require.Equal(t, "✓ Removed #1 from milestone \"v0.9\" in alice/project\n", diagnostics.String())
run(false, "milestone", "create", "v2.0")
require.Equal(t, server.URL+"/alice/project/milestone/3\n", out.String())
require.Equal(t, []string{
"GET /api/v1/repos/alice/project/milestones/3",
"PATCH /api/v1/repos/alice/project/milestones/3",
"PATCH /api/v1/repos/alice/project/issues/1",
"PATCH /api/v1/repos/alice/project/issues/2",
"PATCH /api/v1/repos/alice/project/issues/1",
"POST /api/v1/repos/alice/project/milestones",
}, changed)
}
func TestMilestoneEditFormStartsFromTheMilestone(t *testing.T) {
var edited map[string]any
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.Method + " " + r.URL.Path {
case "GET /api/v1/repos/alice/project/milestones":
writeMilestones(w, r, `[{"id":3,"title":"v1.0","description":"First","due_on":"2026-12-31T00:00:00Z"}]`)
case "PATCH /api/v1/repos/alice/project/milestones/3":
require.NoError(t, json.NewDecoder(r.Body).Decode(&edited))
_, _ = w.Write([]byte(`{"id":3,"title":"v1.0.0"}`))
default:
t.Errorf("unexpected %s %s", r.Method, r.URL.Path)
}
}))
defer server.Close()
deps, _, _ := serverDeps(t, server.URL)
deps.Streams.InTTY, deps.Streams.ErrTTY = true, true
var heading string
var current terminal.MilestoneFormValues
deps.MilestoneForm = milestoneFormEditor(func(h string, c terminal.MilestoneFormValues) terminal.MilestoneFormValues {
heading, current = h, c
return terminal.MilestoneFormValues{Title: "v1.0.0", Description: c.Description}
})
require.NoError(t, Execute(context.Background(), deps, []string{"milestone", "edit", "v1.0", "-R", "alice/project"}))
require.Equal(t, "Editing milestone \"v1.0\" in alice/project", heading)
require.Equal(t, "First", current.Description)
require.Equal(t, "2026-12-31", current.Deadline.Format(time.DateOnly))
require.Equal(t, "v1.0.0", edited["title"])
require.Equal(t, "First", edited["description"])
}
type milestoneFormEditor func(string, terminal.MilestoneFormValues) terminal.MilestoneFormValues
func (f milestoneFormEditor) EditMilestone(_ context.Context, heading string, current terminal.MilestoneFormValues) (terminal.MilestoneFormValues, error) {
return f(heading, current), nil
}