// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package app import ( "testing" "time" "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) func TestParseRunTime(t *testing.T) { now := time.Date(2028, 2, 1, 12, 0, 0, 0, time.UTC) plus2 := time.FixedZone("UTC+2", 2*60*60) for _, tc := range []struct { input string expected time.Time }{ {"24h", now.Add(-24 * time.Hour)}, {"0s", now}, {"2028-01-01", time.Date(2028, 1, 1, 0, 0, 0, 0, plus2)}, {"2028-01-01T00:30:00+02:00", time.Date(2027, 12, 31, 22, 30, 0, 0, time.UTC)}, {"2028-01-01 08:00", time.Date(2028, 1, 1, 8, 0, 0, 0, plus2)}, } { result, err := parseRunTime(tc.input, now.In(plus2)) require.NoError(t, err) require.True(t, result.Equal(tc.expected), tc.input) } for _, input := range []string{"", "-24h", "not-a-date", "2028-02-30"} { _, err := parseRunTime(input, now) require.Error(t, err, input) } } func TestParseTimeRangeRejectsOnlyInvertedBounds(t *testing.T) { for _, tc := range []struct { since, until string wantErr bool }{ {"2026-10-01", "2026-10-02", false}, {"2026-10-01", "2026-10-01", false}, {"2026-10-02", "2026-10-01", true}, } { cmd := &cobra.Command{} var bounds timeRange bounds.register(cmd, "since", "", "until", "") require.NoError(t, cmd.Flags().Parse([]string{"--since=" + tc.since, "--until=" + tc.until})) _, _, err := bounds.parse(time.Now()) require.Equal(t, tc.wantErr, err != nil, tc.since+".."+tc.until) } } func TestNumberAcceptsHashPrefix(t *testing.T) { for raw, want := range map[string]int64{"12": 12, "#12": 12} { n, err := number(raw) require.NoError(t, err, raw) require.Equal(t, want, n, raw) } for _, raw := range []string{"#", "##1", "0", "#-1", "1#", "abc"} { _, err := number(raw) require.ErrorContains(t, err, "invalid resource number", raw) } } func TestUsageArgumentsSplitTopLevelGroups(t *testing.T) { for use, want := range map[string][]string{ "list": nil, "list [flags]": nil, "view ": {""}, "view [number]": {"[number]"}, "close ...": {"..."}, "clone [directory]": {"", "[directory]"}, "diff [ | | ]": {"[ | | ]"}, "download [flags]": {"", ""}, "upload ... [flags]": {"", "..."}, } { require.Equal(t, want, usageArguments(use), use) } } func TestArgumentErrorsShowTheUsageLine(t *testing.T) { for _, tc := range []struct { args []string want string }{ {[]string{"issue", "view"}, "missing issue number\nUsage: tea issue view { | } [flags]\nRun 'tea issue view --help' for examples."}, {[]string{"issue", "view", "1", "2"}, "too many arguments\nUsage: tea issue view { | } [flags]"}, {[]string{"issue", "list", "extra"}, "unexpected argument \"extra\"\nUsage: tea issue list [flags]"}, {[]string{"pr", "checkout"}, "missing pull request number\nUsage: tea pr checkout { | | } [flags]"}, {[]string{"run", "view"}, "missing run ID\nUsage: tea run view [] [flags]"}, {[]string{"release", "asset", "edit", "v1"}, "missing asset\nUsage: tea release asset edit [flags]"}, {[]string{"repo", "clone"}, "missing repository\nUsage: tea repo clone [] [-- ...] [flags]"}, } { requireUsageBeforeRequests(t, tc.want, tc.args...) } } func TestLimitAcceptsShorthandUpToOneThousand(t *testing.T) { var limit, page int cmd := &cobra.Command{Use: "list"} pageFlags(cmd, &limit, &page) require.Equal(t, 30, limit) require.NoError(t, cmd.Flags().Parse([]string{"-L", "1000"})) require.Equal(t, 1000, limit) require.ErrorContains(t, cmd.Flags().Parse([]string{"-L", "1001"}), "must be 1..1000") }