1
0
Fork 0
mirror of https://gitea.com/gitea/tea.git synced 2025-10-20 14:34:05 +02:00
tea/modules/print/actions_test.go
Ross Golder 7a5c260268 feat: add actions management commands (#796)
## Summary

This PR adds comprehensive Actions secrets and variables management functionality to the tea CLI, enabling users to manage their repository's CI/CD configuration directly from the command line.

## Features Added

### Actions Secrets Management
- **List secrets**: `tea actions secrets list` - Display all repository action secrets
- **Create secrets**: `tea actions secrets create <name>` - Create new secrets with interactive prompts
- **Delete secrets**: `tea actions secrets delete <name>` - Remove existing secrets

### Actions Variables Management
- **List variables**: `tea actions variables list` - Display all repository action variables
- **Set variables**: `tea actions variables set <name> <value>` - Create or update variables
- **Delete variables**: `tea actions variables delete <name>` - Remove existing variables

## Implementation Details

- **Interactive prompts**: Secure input handling for sensitive secret values
- **Input validation**: Proper validation for secret/variable names and values
- **Table formatting**: Consistent output formatting with existing tea commands
- **Error handling**: Comprehensive error handling and user feedback
- **Test coverage**: Full test suite for all functionality

## Usage Examples

```bash
# Secrets management
tea actions secrets list
tea actions secrets create API_KEY    # Will prompt securely for value
tea actions secrets delete OLD_SECRET

# Variables management
tea actions variables list
tea actions variables set API_URL https://api.example.com
tea actions variables delete UNUSED_VAR
```

## Related Issue

Resolves #797

## Testing

- All new functionality includes comprehensive unit tests
- Integration with existing tea CLI patterns and conventions
- Validated against Gitea Actions API

Reviewed-on: https://gitea.com/gitea/tea/pulls/796
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 02:53:17 +00:00

214 lines
5 KiB
Go

// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package print
import (
"bytes"
"strconv"
"strings"
"testing"
"time"
"code.gitea.io/sdk/gitea"
)
func TestActionSecretsListEmpty(t *testing.T) {
// Test with empty secrets - should not panic
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionSecretsList panicked with empty list: %v", r)
}
}()
ActionSecretsList([]*gitea.Secret{}, "")
}
func TestActionSecretsListWithData(t *testing.T) {
secrets := []*gitea.Secret{
{
Name: "TEST_SECRET_1",
Created: time.Now().Add(-24 * time.Hour),
},
{
Name: "TEST_SECRET_2",
Created: time.Now().Add(-48 * time.Hour),
},
}
// Test that it doesn't panic with real data
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionSecretsList panicked with data: %v", r)
}
}()
ActionSecretsList(secrets, "")
// Test JSON output format to verify structure
var buf bytes.Buffer
testTable := table{
headers: []string{"Name", "Created"},
}
for _, secret := range secrets {
testTable.addRow(secret.Name, FormatTime(secret.Created, true))
}
testTable.fprint(&buf, "json")
output := buf.String()
if !strings.Contains(output, "TEST_SECRET_1") {
t.Error("Expected TEST_SECRET_1 in JSON output")
}
if !strings.Contains(output, "TEST_SECRET_2") {
t.Error("Expected TEST_SECRET_2 in JSON output")
}
}
func TestActionVariableDetails(t *testing.T) {
variable := &gitea.RepoActionVariable{
Name: "TEST_VARIABLE",
Value: "test_value",
RepoID: 123,
OwnerID: 456,
}
// Test that it doesn't panic
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionVariableDetails panicked: %v", r)
}
}()
ActionVariableDetails(variable)
}
func TestActionVariablesListEmpty(t *testing.T) {
// Test with empty variables - should not panic
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionVariablesList panicked with empty list: %v", r)
}
}()
ActionVariablesList([]*gitea.RepoActionVariable{}, "")
}
func TestActionVariablesListWithData(t *testing.T) {
variables := []*gitea.RepoActionVariable{
{
Name: "TEST_VARIABLE_1",
Value: "short_value",
RepoID: 123,
OwnerID: 456,
},
{
Name: "TEST_VARIABLE_2",
Value: strings.Repeat("a", 60), // Long value to test truncation
RepoID: 124,
OwnerID: 457,
},
}
// Test that it doesn't panic with real data
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionVariablesList panicked with data: %v", r)
}
}()
ActionVariablesList(variables, "")
// Test JSON output format to verify structure and truncation
var buf bytes.Buffer
testTable := table{
headers: []string{"Name", "Value", "Repository ID"},
}
for _, variable := range variables {
value := variable.Value
if len(value) > 50 {
value = value[:47] + "..."
}
testTable.addRow(variable.Name, value, strconv.Itoa(int(variable.RepoID)))
}
testTable.fprint(&buf, "json")
output := buf.String()
if !strings.Contains(output, "TEST_VARIABLE_1") {
t.Error("Expected TEST_VARIABLE_1 in JSON output")
}
if !strings.Contains(output, "TEST_VARIABLE_2") {
t.Error("Expected TEST_VARIABLE_2 in JSON output")
}
// Check that long value is truncated in our test table
if strings.Contains(output, strings.Repeat("a", 60)) {
t.Error("Long value should be truncated in table output")
}
}
func TestActionVariablesListValueTruncation(t *testing.T) {
variable := &gitea.RepoActionVariable{
Name: "LONG_VALUE_VARIABLE",
Value: strings.Repeat("abcdefghij", 10), // 100 characters
RepoID: 123,
OwnerID: 456,
}
// Test that it doesn't panic
defer func() {
if r := recover(); r != nil {
t.Errorf("ActionVariablesList panicked with long value: %v", r)
}
}()
ActionVariablesList([]*gitea.RepoActionVariable{variable}, "")
// Test the truncation logic directly
value := variable.Value
if len(value) > 50 {
value = value[:47] + "..."
}
if len(value) != 50 { // 47 chars + "..." = 50
t.Errorf("Truncated value should be 50 characters, got %d", len(value))
}
if !strings.HasSuffix(value, "...") {
t.Error("Truncated value should end with '...'")
}
}
func TestTableSorting(t *testing.T) {
// Test that the table sorting works correctly
secrets := []*gitea.Secret{
{Name: "Z_SECRET", Created: time.Now()},
{Name: "A_SECRET", Created: time.Now()},
{Name: "M_SECRET", Created: time.Now()},
}
// Test the table sorting logic
table := table{
headers: []string{"Name", "Created"},
}
for _, secret := range secrets {
table.addRow(secret.Name, FormatTime(secret.Created, true))
}
// Sort by first column (Name) in ascending order (false = ascending)
table.sort(0, false)
// Check that the first row is A_SECRET after ascending sorting
if table.values[0][0] != "A_SECRET" {
t.Errorf("Expected first sorted value to be 'A_SECRET', got '%s'", table.values[0][0])
}
// Check that the last row is Z_SECRET after ascending sorting
if table.values[2][0] != "Z_SECRET" {
t.Errorf("Expected last sorted value to be 'Z_SECRET', got '%s'", table.values[2][0])
}
}