1
0
Fork 0
mirror of https://gitea.com/gitea/tea.git synced 2025-10-20 14:34:05 +02:00
tea/cmd/webhooks/list_test.go
Ross Golder 3495ec5ed4 feat: add repository webhook management (#798)
## 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>
2025-10-19 03:40:23 +00:00

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)
}
})
}
}