Skip to content

Commit 4c8ff36

Browse files
committed
Add integration and unit tests for OADP CLI commands
- Introduced a comprehensive suite of integration tests in `integration_test.go` to validate binary build, Makefile installation, client configuration, and command architecture. - Created unit tests for root, non-admin, and NABSL commands to ensure help text accuracy and command functionality. - Enhanced the Makefile to include dedicated targets for running unit and integration tests. - Added a `TESTING.md` document to outline the testing architecture and best practices for future contributions. - Removed outdated test files to streamline the testing structure.
1 parent 14d9dc3 commit 4c8ff36

14 files changed

Lines changed: 956 additions & 614 deletions

File tree

Makefile

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ help: ## Show this help message
5555
@echo " make build PLATFORM=windows/amd64"
5656
@echo " make build PLATFORM=windows/arm64"
5757
@echo ""
58+
@echo "Testing commands:"
59+
@echo " make test # Run all tests (unit + integration)"
60+
@echo " make test-unit # Run unit tests only"
61+
@echo " make test-integration # Run integration tests only"
62+
@echo ""
5863
@echo "Release commands:"
5964
@echo " make release-build # Build binaries for all platforms"
6065
@echo " make release-archives # Create tar.gz archives for all platforms"
@@ -212,9 +217,24 @@ uninstall-all: ## Uninstall the kubectl plugin from all locations (user + system
212217
.PHONY: test
213218
test: ## Run all tests
214219
@echo "Running tests..."
215-
go test ./...
220+
@echo "🧪 Running unit tests..."
221+
go test ./cmd/... ./internal/...
222+
@echo "🔗 Running integration tests..."
223+
go test . -v
216224
@echo "✅ Tests completed!"
217225

226+
.PHONY: test-unit
227+
test-unit: ## Run unit tests only
228+
@echo "Running unit tests..."
229+
go test ./cmd/... ./internal/...
230+
@echo "✅ Unit tests completed!"
231+
232+
.PHONY: test-integration
233+
test-integration: ## Run integration tests only
234+
@echo "Running integration tests..."
235+
go test . -v
236+
@echo "✅ Integration tests completed!"
237+
218238
# Cleanup targets
219239
.PHONY: clean
220240
clean: ## Remove built binaries

TESTING.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# OADP CLI Testing Guide
2+
3+
This document describes the decentralized testing architecture for the OADP CLI.
4+
5+
## Architecture Overview
6+
7+
Tests are organized following Go best practices - they live next to the code they test:
8+
9+
```
10+
├── cmd/
11+
│ ├── root_test.go # Root command tests
12+
│ ├── nabsl/
13+
│ │ ├── nabsl.go
14+
│ │ └── nabsl_test.go # NABSL command tests
15+
│ └── non-admin/
16+
│ ├── nonadmin_test.go # Non-admin command tests
17+
│ └── bsl/
18+
│ ├── bsl.go
19+
│ └── bsl_test.go # BSL command tests
20+
├── internal/
21+
│ └── testutil/
22+
│ └── testutil.go # Shared test utilities
23+
└── integration_test.go # Integration tests
24+
```
25+
26+
## Test Types
27+
28+
### 1. Unit Tests
29+
Located next to the source code they test:
30+
31+
- **`cmd/root_test.go`**: Tests root command functionality, help text, basic structure
32+
- **`cmd/nabsl/nabsl_test.go`**: Tests NABSL commands (approve, reject, get, describe)
33+
- **`cmd/non-admin/nonadmin_test.go`**: Tests non-admin command structure
34+
- **`cmd/non-admin/bsl/bsl_test.go`**: Tests BSL creation, credential handling
35+
36+
### 2. Integration Tests
37+
Located at the project root in `integration_test.go`:
38+
39+
- **Binary Build**: Tests that the CLI binary builds successfully
40+
- **Makefile Integration**: Tests installation options and build system
41+
- **Client Config**: Tests end-to-end client configuration workflow
42+
- **Command Architecture**: Tests overall command structure and relationships
43+
44+
### 3. Shared Utilities
45+
Located in `internal/testutil/`:
46+
47+
- **`BuildCLIBinary()`**: Builds test binary with proper cleanup
48+
- **`RunCommand()`**: Executes CLI commands with timeout and logging
49+
- **`TestHelpCommand()`**: Validates help text contains expected content
50+
- **`SetupTempHome()`**: Creates isolated test environment for client config
51+
52+
## Running Tests
53+
54+
### All Tests
55+
```bash
56+
make test
57+
```
58+
59+
### Unit Tests Only
60+
```bash
61+
make test-unit
62+
```
63+
64+
### Integration Tests Only
65+
```bash
66+
make test-integration
67+
```
68+
69+
### Specific Package
70+
```bash
71+
# Test specific command
72+
go test ./cmd/nabsl -v
73+
74+
# Test with coverage
75+
go test ./cmd/... -cover
76+
```
77+
78+
## Test Coverage
79+
80+
### Unit Tests Verify:
81+
- ✅ Command help text and structure
82+
- ✅ Flag definitions and validation
83+
- ✅ Subcommand availability
84+
- ✅ Help flag consistency (`--help` and `-h`)
85+
- ✅ Command architecture changes
86+
87+
### Integration Tests Verify:
88+
- ✅ Binary builds successfully
89+
- ✅ Makefile installation options work
90+
- ✅ Client configuration end-to-end
91+
- ✅ Cross-command functionality
92+
- ✅ Overall system behavior
93+
94+
## Benefits of Decentralized Testing
95+
96+
### 1. **Locality**
97+
- Tests live next to the code they test
98+
- Easy to find and maintain
99+
- Clear ownership and responsibility
100+
101+
### 2. **Focused Scope**
102+
- Each test file has a narrow, clear scope
103+
- Faster test execution for specific areas
104+
- Better isolation of test failures
105+
106+
### 3. **Parallel Execution**
107+
- Tests can run in parallel across packages
108+
- Better CI/CD performance
109+
- Independent test environments
110+
111+
### 4. **Maintainability**
112+
- When code changes, related tests are immediately visible
113+
- Easier to keep tests in sync with code
114+
- Reduced cognitive overhead
115+
116+
## Adding New Tests
117+
118+
### For New Commands
119+
1. Create `*_test.go` file in the same package as your command
120+
2. Import `"github.com/migtools/oadp-cli/internal/testutil"`
121+
3. Follow existing patterns for help text validation
122+
123+
### For Integration Scenarios
124+
1. Add tests to `integration_test.go`
125+
2. Use `testutil.BuildCLIBinary()` for binary-based tests
126+
3. Focus on cross-package functionality
127+
128+
### Example Test Structure
129+
```go
130+
func TestNewCommand(t *testing.T) {
131+
binaryPath := testutil.BuildCLIBinary(t)
132+
133+
tests := []struct {
134+
name string
135+
args []string
136+
expectContains []string
137+
}{
138+
{
139+
name: "command help",
140+
args: []string{"newcommand", "--help"},
141+
expectContains: []string{"expected text"},
142+
},
143+
}
144+
145+
for _, tt := range tests {
146+
t.Run(tt.name, func(t *testing.T) {
147+
testutil.TestHelpCommand(t, binaryPath, tt.args, tt.expectContains)
148+
})
149+
}
150+
}
151+
```
152+
153+
## Best Practices
154+
155+
1. **Use testutil helpers** for common operations
156+
2. **Test help text** to verify command structure
157+
3. **Use table-driven tests** for multiple scenarios
158+
4. **Keep tests focused** on single responsibilities
159+
5. **Mock external dependencies** when possible
160+
6. **Use descriptive test names** that explain what's being tested
161+
162+
This testing architecture ensures comprehensive coverage while maintaining clarity and ease of maintenance.

cmd/nabsl/nabsl_test.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
Copyright 2025 The OADP CLI Contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package nabsl
18+
19+
import (
20+
"testing"
21+
22+
"github.com/migtools/oadp-cli/internal/testutil"
23+
)
24+
25+
// TestNABSLCommands tests the NABSL command functionality
26+
func TestNABSLCommands(t *testing.T) {
27+
binaryPath := testutil.BuildCLIBinary(t)
28+
29+
tests := []struct {
30+
name string
31+
args []string
32+
expectContains []string
33+
}{
34+
{
35+
name: "nabsl help",
36+
args: []string{"nabsl", "--help"},
37+
expectContains: []string{
38+
"Manage approval requests for non-admin backup storage locations",
39+
"approve",
40+
"reject",
41+
"describe",
42+
"get",
43+
},
44+
},
45+
{
46+
name: "nabsl approve help",
47+
args: []string{"nabsl", "approve", "--help"},
48+
expectContains: []string{
49+
"Approve a pending backup storage location request",
50+
"--reason",
51+
},
52+
},
53+
{
54+
name: "nabsl reject help",
55+
args: []string{"nabsl", "reject", "--help"},
56+
expectContains: []string{
57+
"Reject a pending backup storage location request",
58+
"--reason",
59+
},
60+
},
61+
{
62+
name: "nabsl get help",
63+
args: []string{"nabsl", "get", "--help"},
64+
expectContains: []string{
65+
"Get non-admin backup storage location requests",
66+
},
67+
},
68+
{
69+
name: "nabsl describe help",
70+
args: []string{"nabsl", "describe", "--help"},
71+
expectContains: []string{
72+
"Describe a non-admin backup storage location request",
73+
},
74+
},
75+
}
76+
77+
for _, tt := range tests {
78+
t.Run(tt.name, func(t *testing.T) {
79+
testutil.TestHelpCommand(t, binaryPath, tt.args, tt.expectContains)
80+
})
81+
}
82+
}
83+
84+
// TestNABSLHelpFlags tests that both --help and -h work for nabsl commands
85+
func TestNABSLHelpFlags(t *testing.T) {
86+
binaryPath := testutil.BuildCLIBinary(t)
87+
88+
commands := [][]string{
89+
{"nabsl", "--help"},
90+
{"nabsl", "-h"},
91+
{"nabsl", "approve", "--help"},
92+
{"nabsl", "approve", "-h"},
93+
{"nabsl", "reject", "--help"},
94+
{"nabsl", "reject", "-h"},
95+
{"nabsl", "get", "--help"},
96+
{"nabsl", "get", "-h"},
97+
{"nabsl", "describe", "--help"},
98+
{"nabsl", "describe", "-h"},
99+
}
100+
101+
for _, cmd := range commands {
102+
t.Run("help_flags_"+cmd[len(cmd)-1], func(t *testing.T) {
103+
testutil.TestHelpCommand(t, binaryPath, cmd, []string{"Usage:"})
104+
})
105+
}
106+
}
107+
108+
// TestNABSLClientConfigIntegration tests that NABSL commands respect client config
109+
func TestNABSLClientConfigIntegration(t *testing.T) {
110+
binaryPath := testutil.BuildCLIBinary(t)
111+
_, cleanup := testutil.SetupTempHome(t)
112+
defer cleanup()
113+
114+
t.Run("nabsl commands work with client config", func(t *testing.T) {
115+
// Set a known namespace
116+
_, err := testutil.RunCommand(t, binaryPath, "client", "config", "set", "namespace=admin-namespace")
117+
if err != nil {
118+
t.Fatalf("Failed to set client config: %v", err)
119+
}
120+
121+
// Test that nabsl commands can be invoked (they should respect the namespace)
122+
// We test help commands since they don't require actual K8s resources
123+
commands := [][]string{
124+
{"nabsl", "get", "--help"},
125+
{"nabsl", "approve", "--help"},
126+
{"nabsl", "reject", "--help"},
127+
{"nabsl", "describe", "--help"},
128+
}
129+
130+
for _, cmd := range commands {
131+
t.Run("config_test_"+cmd[1], func(t *testing.T) {
132+
output, err := testutil.RunCommand(t, binaryPath, cmd...)
133+
if err != nil {
134+
t.Fatalf("NABSL command should work with client config: %v", err)
135+
}
136+
if output == "" {
137+
t.Errorf("Expected help output for %v", cmd)
138+
}
139+
})
140+
}
141+
})
142+
}

cmd/non-admin/bsl/bsl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func NewBSLCommand(f client.Factory) *cobra.Command {
2626
c := &cobra.Command{
2727
Use: "bsl",
2828
Short: "Create and manage backup storage locations",
29-
Long: "Create and manage non-admin backup storage locations and their approval requests",
29+
Long: "Create and manage non-admin backup storage locations",
3030
}
3131

3232
c.AddCommand(

0 commit comments

Comments
 (0)