mirror of
https://gitea.com/gitea/tea.git
synced 2025-10-20 14:34:05 +02:00

## Summary This PR adds support for organization-level and global webhooks in the tea CLI tool. ## Changes Made ### Organization Webhooks - Added `--org` flag to webhook commands to operate on organization-level webhooks - Implemented full CRUD operations for org webhooks (create, list, update, delete) - Extended TeaContext to support organization scope ### Global Webhooks - Added `--global` flag with placeholder implementation - Ready for when Gitea SDK adds global webhook API methods ### Technical Details - Updated context handling to support org/global scopes - Modified all webhook subcommands (create, list, update, delete) - Maintained backward compatibility for repository webhooks - Updated tests and documentation ## Usage Examples ```bash # Repository webhooks (existing) tea webhooks list tea webhooks create https://example.com/hook --events push # Organization webhooks (new) tea webhooks list --org myorg tea webhooks create https://example.com/hook --org myorg --events push,pull_request # Global webhooks (future) tea webhooks list --global ``` ## Testing - All existing tests pass - Updated test expectations for new descriptions - Manual testing of org webhook operations completed Closes: webhook management feature request Reviewed-on: https://gitea.com/gitea/tea/pulls/798 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Ross Golder <ross@golder.org> Co-committed-by: Ross Golder <ross@golder.org>
331 lines
7.2 KiB
Go
331 lines
7.2 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package webhooks
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestListCommandMetadata(t *testing.T) {
|
|
cmd := &CmdWebhooksList
|
|
|
|
assert.Equal(t, "list", cmd.Name)
|
|
assert.Contains(t, cmd.Aliases, "ls")
|
|
assert.Equal(t, "List webhooks", cmd.Usage)
|
|
assert.Equal(t, "List webhooks in repository, organization, or globally", cmd.Description)
|
|
assert.NotNil(t, cmd.Action)
|
|
}
|
|
|
|
func TestListCommandFlags(t *testing.T) {
|
|
cmd := &CmdWebhooksList
|
|
|
|
// Should inherit from AllDefaultFlags which includes output, login, remote, repo flags
|
|
assert.NotNil(t, cmd.Flags)
|
|
assert.Greater(t, len(cmd.Flags), 0, "List command should have flags from AllDefaultFlags")
|
|
}
|
|
|
|
func TestListOutputFormats(t *testing.T) {
|
|
// Test that various output formats are supported through the output flag
|
|
supportedFormats := []string{
|
|
"table",
|
|
"csv",
|
|
"simple",
|
|
"tsv",
|
|
"yaml",
|
|
"json",
|
|
}
|
|
|
|
for _, format := range supportedFormats {
|
|
t.Run("Format_"+format, func(t *testing.T) {
|
|
// Verify format string is valid (non-empty, no spaces)
|
|
assert.NotEmpty(t, format)
|
|
assert.NotContains(t, format, " ")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListPagination(t *testing.T) {
|
|
// Test pagination parameters that would be used with ListHooksOptions
|
|
tests := []struct {
|
|
name string
|
|
page int
|
|
pageSize int
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "Default pagination",
|
|
page: 1,
|
|
pageSize: 10,
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Large page size",
|
|
page: 1,
|
|
pageSize: 100,
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "High page number",
|
|
page: 50,
|
|
pageSize: 10,
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Zero page",
|
|
page: 0,
|
|
pageSize: 10,
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "Negative page",
|
|
page: -1,
|
|
pageSize: 10,
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "Zero page size",
|
|
page: 1,
|
|
pageSize: 0,
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "Negative page size",
|
|
page: 1,
|
|
pageSize: -10,
|
|
valid: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.valid {
|
|
assert.Greater(t, tt.page, 0, "Valid page should be positive")
|
|
assert.Greater(t, tt.pageSize, 0, "Valid page size should be positive")
|
|
} else {
|
|
assert.True(t, tt.page <= 0 || tt.pageSize <= 0, "Invalid pagination should have non-positive values")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListSorting(t *testing.T) {
|
|
// Test potential sorting options for webhook lists
|
|
sortFields := []string{
|
|
"id",
|
|
"type",
|
|
"url",
|
|
"active",
|
|
"created",
|
|
"updated",
|
|
}
|
|
|
|
for _, field := range sortFields {
|
|
t.Run("SortField_"+field, func(t *testing.T) {
|
|
assert.NotEmpty(t, field)
|
|
assert.NotContains(t, field, " ")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListFiltering(t *testing.T) {
|
|
// Test filtering criteria that might be applied to webhook lists
|
|
tests := []struct {
|
|
name string
|
|
filterType string
|
|
filterValue string
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "Filter by type - gitea",
|
|
filterType: "type",
|
|
filterValue: "gitea",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Filter by type - slack",
|
|
filterType: "type",
|
|
filterValue: "slack",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Filter by active status",
|
|
filterType: "active",
|
|
filterValue: "true",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Filter by inactive status",
|
|
filterType: "active",
|
|
filterValue: "false",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Filter by event",
|
|
filterType: "event",
|
|
filterValue: "push",
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Invalid filter type",
|
|
filterType: "invalid",
|
|
filterValue: "value",
|
|
valid: false,
|
|
},
|
|
{
|
|
name: "Empty filter value",
|
|
filterType: "type",
|
|
filterValue: "",
|
|
valid: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.valid {
|
|
assert.NotEmpty(t, tt.filterType)
|
|
assert.NotEmpty(t, tt.filterValue)
|
|
} else {
|
|
assert.True(t, tt.filterType == "invalid" || tt.filterValue == "")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListCommandStructure(t *testing.T) {
|
|
cmd := &CmdWebhooksList
|
|
|
|
// Verify command structure
|
|
assert.NotEmpty(t, cmd.Name)
|
|
assert.NotEmpty(t, cmd.Usage)
|
|
assert.NotEmpty(t, cmd.Description)
|
|
assert.NotNil(t, cmd.Action)
|
|
|
|
// Verify aliases
|
|
assert.Greater(t, len(cmd.Aliases), 0, "List command should have aliases")
|
|
for _, alias := range cmd.Aliases {
|
|
assert.NotEmpty(t, alias)
|
|
assert.NotContains(t, alias, " ")
|
|
}
|
|
}
|
|
|
|
func TestListErrorHandling(t *testing.T) {
|
|
// Test various error conditions that the list command should handle
|
|
errorCases := []struct {
|
|
name string
|
|
description string
|
|
}{
|
|
{
|
|
name: "Network error",
|
|
description: "Should handle network connectivity issues",
|
|
},
|
|
{
|
|
name: "Authentication error",
|
|
description: "Should handle authentication failures",
|
|
},
|
|
{
|
|
name: "Permission error",
|
|
description: "Should handle insufficient permissions",
|
|
},
|
|
{
|
|
name: "Repository not found",
|
|
description: "Should handle missing repository",
|
|
},
|
|
{
|
|
name: "Invalid output format",
|
|
description: "Should handle unsupported output formats",
|
|
},
|
|
}
|
|
|
|
for _, errorCase := range errorCases {
|
|
t.Run(errorCase.name, func(t *testing.T) {
|
|
// Verify error case is documented
|
|
assert.NotEmpty(t, errorCase.description)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListTableHeaders(t *testing.T) {
|
|
// Test expected table headers for webhook list output
|
|
expectedHeaders := []string{
|
|
"ID",
|
|
"Type",
|
|
"URL",
|
|
"Events",
|
|
"Active",
|
|
"Updated",
|
|
}
|
|
|
|
for _, header := range expectedHeaders {
|
|
t.Run("Header_"+header, func(t *testing.T) {
|
|
assert.NotEmpty(t, header)
|
|
assert.NotContains(t, header, "\n")
|
|
})
|
|
}
|
|
|
|
// Verify all headers are unique
|
|
headerSet := make(map[string]bool)
|
|
for _, header := range expectedHeaders {
|
|
assert.False(t, headerSet[header], "Header %s appears multiple times", header)
|
|
headerSet[header] = true
|
|
}
|
|
}
|
|
|
|
func TestListEventFormatting(t *testing.T) {
|
|
// Test event list formatting for display
|
|
tests := []struct {
|
|
name string
|
|
events []string
|
|
maxLength int
|
|
expectedFormat string
|
|
}{
|
|
{
|
|
name: "Short event list",
|
|
events: []string{"push"},
|
|
maxLength: 40,
|
|
expectedFormat: "push",
|
|
},
|
|
{
|
|
name: "Multiple events",
|
|
events: []string{"push", "pull_request"},
|
|
maxLength: 40,
|
|
expectedFormat: "push,pull_request",
|
|
},
|
|
{
|
|
name: "Long event list - should truncate",
|
|
events: []string{"push", "pull_request", "pull_request_review_approved", "pull_request_sync"},
|
|
maxLength: 40,
|
|
expectedFormat: "truncated",
|
|
},
|
|
{
|
|
name: "Empty events",
|
|
events: []string{},
|
|
maxLength: 40,
|
|
expectedFormat: "",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
eventStr := ""
|
|
if len(tt.events) > 0 {
|
|
eventStr = tt.events[0]
|
|
for i := 1; i < len(tt.events); i++ {
|
|
eventStr += "," + tt.events[i]
|
|
}
|
|
}
|
|
|
|
if len(eventStr) > tt.maxLength && tt.maxLength > 3 {
|
|
eventStr = eventStr[:tt.maxLength-3] + "..."
|
|
}
|
|
|
|
if tt.expectedFormat == "truncated" {
|
|
assert.Contains(t, eventStr, "...")
|
|
} else if tt.expectedFormat != "" {
|
|
assert.Equal(t, tt.expectedFormat, eventStr)
|
|
}
|
|
})
|
|
}
|
|
}
|