mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
feat!: rebuild tea on Cobra with explicit noun/verb commands (#1126)
- **switch tea to the Cobra command tree:** `internal/app` wires the packages from parts 1–4 into explicit noun/verb commands with `--repo`, `--json` / `--jq`, `--sudo` and `--debug`; `main.go` points at it. Build, release and docs tooling (`tools/cli-docs`, Makefile, goreleaser, snap, CI) and README / CONTRIBUTING are updated. - **remove the urfave/cli command tree:** deletes `cmd/`, `modules/`, the old integration tests, contrib completion scripts and the generated `docs/CLI.md`, and drops their dependencies. This commit only deletes. ## Breaking changes This is a breaking release. It requires Gitea 28 or newer; configuration moves to separate `config.yml` and `hosts.yml` files and requires fresh authentication, and command names, aliases, flags and output formats change. `docs/migration-1.0.md` maps every old command, flag and environment variable to its replacement. ## Issues fixes #1118 fixes #1116 fixes #1115 fixes #1094 fixes #1091 fixes #1073 fixes #1052 fixes #1032 fixes #1023 fixes #966 fixes #908 fixes #833 fixes #831 fixes #817 fixes #804 fixes #779 fixes #770 fixes #737 fixes #697 fixes #726 fixes #677 fixes #672 fixes #633 fixes #632 fixes #560 fixes #556 fixes #523 fixes #516 fixes #510 fixes #504 fixes #500 fixes #482 fixes #406 fixes #403 fixes #373 fixes #351 fixes #344 fixes #326 fixes #325 fixes #323 fixes #246 fixes #240 fixes #232 fixes #197 fixes #172 fixes #141 fixes #69 fixes #1110 fixes #569 fixes #480 fixes #666 fixes #670 fixes #457 fixes #643 fixes #498 fixes #490 fixes #1069 fixes #834 fixes #602 fixes #328 fixes #417 fixes #571 fixes #1029 fixes #489 fixes #1050 fixes #162 fixes #430 fixes #1087 fixes #683 Reviewed-on: https://gitea.com/gitea/tea/pulls/1126 Reviewed-by: TheFox0x7 <95654+thefox0x7@noreply.gitea.com>
This commit is contained in:
1 parent
12e415a7a7
commit
f02245449b
456 files changed
+18892
-34015
No files matched your search
@@ -1,503 +0,0 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package webhooks
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dev/sdk"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func TestUpdateCommandMetadata(t *testing.T) {
|
||||
cmd := &CmdWebhooksUpdate
|
||||
|
||||
assert.Equal(t, "update", cmd.Name)
|
||||
assert.Contains(t, cmd.Aliases, "edit")
|
||||
assert.Contains(t, cmd.Aliases, "u")
|
||||
assert.Equal(t, "Update a webhook", cmd.Usage)
|
||||
assert.Equal(t, "Update webhook configuration in repository, organization, or globally", cmd.Description)
|
||||
assert.Equal(t, "<webhook-id>", cmd.ArgsUsage)
|
||||
assert.NotNil(t, cmd.Action)
|
||||
}
|
||||
|
||||
func TestUpdateCommandFlags(t *testing.T) {
|
||||
cmd := &CmdWebhooksUpdate
|
||||
|
||||
expectedFlags := []string{
|
||||
"url",
|
||||
"secret",
|
||||
"events",
|
||||
"active",
|
||||
"inactive",
|
||||
"branch-filter",
|
||||
"authorization-header",
|
||||
}
|
||||
|
||||
for _, flagName := range expectedFlags {
|
||||
found := false
|
||||
for _, flag := range cmd.Flags {
|
||||
if flag.Names()[0] == flagName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Expected flag %s not found", flagName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateActiveInactiveFlags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
activeSet bool
|
||||
activeValue bool
|
||||
inactiveSet bool
|
||||
inactiveValue bool
|
||||
originalActive bool
|
||||
expectedActive bool
|
||||
}{
|
||||
{
|
||||
name: "Set active to true",
|
||||
activeSet: true,
|
||||
activeValue: true,
|
||||
inactiveSet: false,
|
||||
originalActive: false,
|
||||
expectedActive: true,
|
||||
},
|
||||
{
|
||||
name: "Set active to false",
|
||||
activeSet: true,
|
||||
activeValue: false,
|
||||
inactiveSet: false,
|
||||
originalActive: true,
|
||||
expectedActive: false,
|
||||
},
|
||||
{
|
||||
name: "Set inactive to true",
|
||||
activeSet: false,
|
||||
inactiveSet: true,
|
||||
inactiveValue: true,
|
||||
originalActive: true,
|
||||
expectedActive: false,
|
||||
},
|
||||
{
|
||||
name: "Set inactive to false",
|
||||
activeSet: false,
|
||||
inactiveSet: true,
|
||||
inactiveValue: false,
|
||||
originalActive: false,
|
||||
expectedActive: true,
|
||||
},
|
||||
{
|
||||
name: "No flags set",
|
||||
activeSet: false,
|
||||
inactiveSet: false,
|
||||
originalActive: true,
|
||||
expectedActive: true,
|
||||
},
|
||||
{
|
||||
name: "Active flag takes precedence",
|
||||
activeSet: true,
|
||||
activeValue: true,
|
||||
inactiveSet: true,
|
||||
inactiveValue: true,
|
||||
originalActive: false,
|
||||
expectedActive: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Simulate the logic from runWebhooksUpdate
|
||||
active := tt.originalActive
|
||||
|
||||
if tt.activeSet {
|
||||
active = tt.activeValue
|
||||
} else if tt.inactiveSet {
|
||||
active = !tt.inactiveValue
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.expectedActive, active)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateConfigPreservation(t *testing.T) {
|
||||
// Test that existing configuration is preserved when not updated
|
||||
originalConfig := map[string]string{
|
||||
"url": "https://old.example.com/webhook",
|
||||
"secret": "old-secret",
|
||||
"http_method": "post",
|
||||
"content_type": "json",
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
updates map[string]string
|
||||
expectedConfig map[string]string
|
||||
}{
|
||||
{
|
||||
name: "Update only URL",
|
||||
updates: map[string]string{
|
||||
"url": "https://new.example.com/webhook",
|
||||
},
|
||||
expectedConfig: map[string]string{
|
||||
"url": "https://new.example.com/webhook",
|
||||
"secret": "old-secret",
|
||||
"http_method": "post",
|
||||
"content_type": "json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Update secret",
|
||||
updates: map[string]string{
|
||||
"secret": "new-secret",
|
||||
},
|
||||
expectedConfig: map[string]string{
|
||||
"url": "https://old.example.com/webhook",
|
||||
"secret": "new-secret",
|
||||
"http_method": "post",
|
||||
"content_type": "json",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "No updates",
|
||||
updates: map[string]string{},
|
||||
expectedConfig: map[string]string{
|
||||
"url": "https://old.example.com/webhook",
|
||||
"secret": "old-secret",
|
||||
"http_method": "post",
|
||||
"content_type": "json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Copy original config
|
||||
config := make(map[string]string)
|
||||
for k, v := range originalConfig {
|
||||
config[k] = v
|
||||
}
|
||||
|
||||
// Apply updates
|
||||
for k, v := range tt.updates {
|
||||
config[k] = v
|
||||
}
|
||||
|
||||
// Verify expected config
|
||||
assert.Equal(t, tt.expectedConfig, config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateBranchFilterAndAuthHeaderHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
originalBranchFilter string
|
||||
originalAuthHeader string
|
||||
setBranchFilter bool
|
||||
newBranchFilter string
|
||||
setAuthorizationHeader bool
|
||||
newAuthHeader string
|
||||
expectedBranchFilter string
|
||||
expectedAuthHeader string
|
||||
}{
|
||||
{
|
||||
name: "Preserve values",
|
||||
originalBranchFilter: "main",
|
||||
originalAuthHeader: "Bearer old-token",
|
||||
expectedBranchFilter: "main",
|
||||
expectedAuthHeader: "Bearer old-token",
|
||||
},
|
||||
{
|
||||
name: "Update branch filter",
|
||||
originalBranchFilter: "main",
|
||||
setBranchFilter: true,
|
||||
newBranchFilter: "develop",
|
||||
expectedBranchFilter: "develop",
|
||||
expectedAuthHeader: "",
|
||||
},
|
||||
{
|
||||
name: "Update authorization header",
|
||||
originalAuthHeader: "Bearer old-token",
|
||||
setAuthorizationHeader: true,
|
||||
newAuthHeader: "X-Token: new-token",
|
||||
expectedBranchFilter: "",
|
||||
expectedAuthHeader: "X-Token: new-token",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
branchFilter := tt.originalBranchFilter
|
||||
if tt.setBranchFilter {
|
||||
branchFilter = tt.newBranchFilter
|
||||
}
|
||||
|
||||
authHeader := tt.originalAuthHeader
|
||||
if tt.setAuthorizationHeader {
|
||||
authHeader = tt.newAuthHeader
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.expectedBranchFilter, branchFilter)
|
||||
assert.Equal(t, tt.expectedAuthHeader, authHeader)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateEventsHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
originalEvents []string
|
||||
newEvents string
|
||||
setEvents bool
|
||||
expectedEvents []string
|
||||
}{
|
||||
{
|
||||
name: "Update events",
|
||||
originalEvents: []string{"push"},
|
||||
newEvents: "push,pull_request,issues",
|
||||
setEvents: true,
|
||||
expectedEvents: []string{"push", "pull_request", "issues"},
|
||||
},
|
||||
{
|
||||
name: "Clear events",
|
||||
originalEvents: []string{"push", "pull_request"},
|
||||
newEvents: "",
|
||||
setEvents: true,
|
||||
expectedEvents: []string{""},
|
||||
},
|
||||
{
|
||||
name: "No event update",
|
||||
originalEvents: []string{"push", "pull_request"},
|
||||
newEvents: "",
|
||||
setEvents: false,
|
||||
expectedEvents: []string{"push", "pull_request"},
|
||||
},
|
||||
{
|
||||
name: "Single event",
|
||||
originalEvents: []string{"push", "issues"},
|
||||
newEvents: "pull_request",
|
||||
setEvents: true,
|
||||
expectedEvents: []string{"pull_request"},
|
||||
},
|
||||
{
|
||||
name: "Events with spaces",
|
||||
originalEvents: []string{"push"},
|
||||
newEvents: "push, pull_request , issues",
|
||||
setEvents: true,
|
||||
expectedEvents: []string{"push", "pull_request", "issues"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
events := tt.originalEvents
|
||||
|
||||
if tt.setEvents {
|
||||
eventsList := []string{}
|
||||
if tt.newEvents != "" {
|
||||
parts := strings.Split(tt.newEvents, ",")
|
||||
for _, part := range parts {
|
||||
eventsList = append(eventsList, strings.TrimSpace(part))
|
||||
}
|
||||
} else {
|
||||
eventsList = []string{""}
|
||||
}
|
||||
events = eventsList
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.expectedEvents, events)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateEditHookOption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config map[string]string
|
||||
events []string
|
||||
active bool
|
||||
expected gitea.EditHookOption
|
||||
}{
|
||||
{
|
||||
name: "Complete update",
|
||||
config: map[string]string{
|
||||
"url": "https://example.com/webhook",
|
||||
"secret": "new-secret",
|
||||
},
|
||||
events: []string{"push", "pull_request"},
|
||||
active: true,
|
||||
expected: gitea.EditHookOption{
|
||||
Config: map[string]string{
|
||||
"url": "https://example.com/webhook",
|
||||
"secret": "new-secret",
|
||||
},
|
||||
Events: []string{"push", "pull_request"},
|
||||
Active: &[]bool{true}[0],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Config only update",
|
||||
config: map[string]string{
|
||||
"url": "https://new.example.com/webhook",
|
||||
},
|
||||
events: []string{"push"},
|
||||
active: false,
|
||||
expected: gitea.EditHookOption{
|
||||
Config: map[string]string{
|
||||
"url": "https://new.example.com/webhook",
|
||||
},
|
||||
Events: []string{"push"},
|
||||
Active: &[]bool{false}[0],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Minimal update",
|
||||
config: map[string]string{},
|
||||
events: []string{},
|
||||
active: true,
|
||||
expected: gitea.EditHookOption{
|
||||
Config: map[string]string{},
|
||||
Events: []string{},
|
||||
Active: &[]bool{true}[0],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
option := gitea.EditHookOption{
|
||||
Config: tt.config,
|
||||
Events: tt.events,
|
||||
Active: &tt.active,
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.expected.Config, option.Config)
|
||||
assert.Equal(t, tt.expected.Events, option.Events)
|
||||
assert.Equal(t, *tt.expected.Active, *option.Active)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateWebhookIDValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
webhookID string
|
||||
expectedID int64
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid webhook ID",
|
||||
webhookID: "123",
|
||||
expectedID: 123,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Single digit ID",
|
||||
webhookID: "1",
|
||||
expectedID: 1,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Large webhook ID",
|
||||
webhookID: "999999",
|
||||
expectedID: 999999,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Zero webhook ID",
|
||||
webhookID: "0",
|
||||
expectedID: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Negative webhook ID",
|
||||
webhookID: "-1",
|
||||
expectedID: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Non-numeric webhook ID",
|
||||
webhookID: "abc",
|
||||
expectedID: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Empty webhook ID",
|
||||
webhookID: "",
|
||||
expectedID: 0,
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Float webhook ID",
|
||||
webhookID: "12.34",
|
||||
expectedID: 0,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// This simulates the utils.ArgToIndex function behavior
|
||||
if tt.webhookID == "" {
|
||||
assert.True(t, tt.expectError)
|
||||
return
|
||||
}
|
||||
|
||||
// Basic validation - check if it's numeric
|
||||
isNumeric := true
|
||||
for _, char := range tt.webhookID {
|
||||
if char < '0' || char > '9' {
|
||||
if !(char == '-' && tt.webhookID[0] == '-') {
|
||||
isNumeric = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !isNumeric || tt.webhookID == "0" || (len(tt.webhookID) > 0 && tt.webhookID[0] == '-') {
|
||||
assert.True(t, tt.expectError, "Should expect error for invalid ID: %s", tt.webhookID)
|
||||
} else {
|
||||
assert.False(t, tt.expectError, "Should not expect error for valid ID: %s", tt.webhookID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFlagTypes(t *testing.T) {
|
||||
cmd := &CmdWebhooksUpdate
|
||||
|
||||
flagTypes := map[string]string{
|
||||
"url": "string",
|
||||
"secret": "string",
|
||||
"events": "string",
|
||||
"active": "bool",
|
||||
"inactive": "bool",
|
||||
"branch-filter": "string",
|
||||
"authorization-header": "string",
|
||||
}
|
||||
|
||||
for flagName, expectedType := range flagTypes {
|
||||
found := false
|
||||
for _, flag := range cmd.Flags {
|
||||
if flag.Names()[0] == flagName {
|
||||
found = true
|
||||
switch expectedType {
|
||||
case "string":
|
||||
_, ok := flag.(*cli.StringFlag)
|
||||
assert.True(t, ok, "Flag %s should be a StringFlag", flagName)
|
||||
case "bool":
|
||||
_, ok := flag.(*cli.BoolFlag)
|
||||
assert.True(t, ok, "Flag %s should be a BoolFlag", flagName)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.True(t, found, "Flag %s not found", flagName)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user