Skip to content

Commit dd5cea6

Browse files
test: improve unit test coverage for pkg/cli/jq.go (#3681)
1 parent 5bb4d93 commit dd5cea6

2 files changed

Lines changed: 178 additions & 117 deletions

File tree

pkg/cli/jq_integration_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//go:build integration
2+
3+
package cli
4+
5+
import (
6+
"context"
7+
"os"
8+
"os/exec"
9+
"path/filepath"
10+
"testing"
11+
"time"
12+
13+
"github.com/modelcontextprotocol/go-sdk/mcp"
14+
)
15+
16+
// TestMCPServer_StatusToolWithJq tests the status tool with jq filter parameter
17+
func TestMCPServer_StatusToolWithJq(t *testing.T) {
18+
// Skip if jq is not available
19+
if _, err := exec.LookPath("jq"); err != nil {
20+
t.Skip("Skipping test: jq not found in PATH")
21+
}
22+
23+
// Skip if the binary doesn't exist
24+
binaryPath := "../../gh-aw"
25+
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
26+
t.Skip("Skipping test: gh-aw binary not found. Run 'make build' first.")
27+
}
28+
29+
// Create a temporary directory with a workflow file
30+
tmpDir := t.TempDir()
31+
workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
32+
if err := os.MkdirAll(workflowsDir, 0755); err != nil {
33+
t.Fatalf("Failed to create workflows directory: %v", err)
34+
}
35+
36+
// Create a test workflow file
37+
workflowContent := `---
38+
on: push
39+
engine: copilot
40+
---
41+
# Test Workflow
42+
`
43+
workflowFile := filepath.Join(workflowsDir, "test.md")
44+
if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
45+
t.Fatalf("Failed to write workflow file: %v", err)
46+
}
47+
48+
// Save current directory and change to temp directory
49+
originalDir, _ := os.Getwd()
50+
defer os.Chdir(originalDir)
51+
52+
// Initialize git repository in the temp directory
53+
initCmd := exec.Command("git", "init")
54+
initCmd.Dir = tmpDir
55+
if err := initCmd.Run(); err != nil {
56+
t.Fatalf("Failed to initialize git repository: %v", err)
57+
}
58+
59+
// Create MCP client
60+
client := mcp.NewClient(&mcp.Implementation{
61+
Name: "test-client",
62+
Version: "1.0.0",
63+
}, nil)
64+
65+
// Start the MCP server as a subprocess
66+
serverCmd := exec.Command(filepath.Join(originalDir, binaryPath), "mcp-server")
67+
serverCmd.Dir = tmpDir
68+
transport := &mcp.CommandTransport{Command: serverCmd}
69+
70+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
71+
defer cancel()
72+
73+
session, err := client.Connect(ctx, transport, nil)
74+
if err != nil {
75+
t.Fatalf("Failed to connect to MCP server: %v", err)
76+
}
77+
defer session.Close()
78+
79+
// Test 1: Call status tool with jq filter to get just workflow names
80+
params := &mcp.CallToolParams{
81+
Name: "status",
82+
Arguments: map[string]any{
83+
"jq": ".[].workflow",
84+
},
85+
}
86+
result, err := session.CallTool(ctx, params)
87+
if err != nil {
88+
t.Fatalf("Failed to call status tool with jq filter: %v", err)
89+
}
90+
91+
// Verify result contains the workflow name
92+
if textContent, ok := result.Content[0].(*mcp.TextContent); ok {
93+
if textContent.Text == "" {
94+
t.Error("Expected non-empty text content from status tool with jq filter")
95+
}
96+
// The output should contain "test" (the workflow name)
97+
t.Logf("Status tool output with jq filter: %s", textContent.Text)
98+
} else {
99+
t.Error("Expected text content from status tool with jq filter")
100+
}
101+
102+
// Test 2: Call status tool with jq filter to count workflows
103+
params = &mcp.CallToolParams{
104+
Name: "status",
105+
Arguments: map[string]any{
106+
"jq": "length",
107+
},
108+
}
109+
result, err = session.CallTool(ctx, params)
110+
if err != nil {
111+
t.Fatalf("Failed to call status tool with jq count filter: %v", err)
112+
}
113+
114+
// Verify result contains a number
115+
if textContent, ok := result.Content[0].(*mcp.TextContent); ok {
116+
if textContent.Text == "" {
117+
t.Error("Expected non-empty text content from status tool with jq count filter")
118+
}
119+
t.Logf("Status tool count output: %s", textContent.Text)
120+
} else {
121+
t.Error("Expected text content from status tool with jq count filter")
122+
}
123+
}

pkg/cli/jq_test.go

Lines changed: 55 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
//go:build integration
2-
31
package cli
42

53
import (
6-
"context"
7-
"os"
84
"os/exec"
9-
"path/filepath"
5+
"strings"
106
"testing"
11-
"time"
12-
13-
"github.com/modelcontextprotocol/go-sdk/mcp"
147
)
158

169
func TestApplyJqFilter(t *testing.T) {
@@ -26,6 +19,18 @@ func TestApplyJqFilter(t *testing.T) {
2619
wantErr bool
2720
validate func(t *testing.T, output string)
2821
}{
22+
{
23+
name: "simple filter - identity",
24+
jsonInput: `{"name":"test"}`,
25+
jqFilter: ".",
26+
wantErr: false,
27+
validate: func(t *testing.T, output string) {
28+
output = strings.TrimSpace(output)
29+
if !strings.Contains(output, "test") {
30+
t.Errorf("Expected output to contain 'test', got %q", output)
31+
}
32+
},
33+
},
2934
{
3035
name: "simple filter - get first element",
3136
jsonInput: `[{"name":"a"},{"name":"b"}]`,
@@ -59,6 +64,41 @@ func TestApplyJqFilter(t *testing.T) {
5964
}
6065
},
6166
},
67+
{
68+
name: "filter - extract specific field",
69+
jsonInput: `{"name":"value","id":123}`,
70+
jqFilter: ".name",
71+
wantErr: false,
72+
validate: func(t *testing.T, output string) {
73+
output = strings.TrimSpace(output)
74+
if !strings.Contains(output, "value") {
75+
t.Errorf("Expected output to contain 'value', got %q", output)
76+
}
77+
},
78+
},
79+
{
80+
name: "filter - empty input",
81+
jsonInput: `{}`,
82+
jqFilter: ".",
83+
wantErr: false,
84+
validate: func(t *testing.T, output string) {
85+
output = strings.TrimSpace(output)
86+
if output != "{}" {
87+
t.Errorf("Expected '{}', got %q", output)
88+
}
89+
},
90+
},
91+
{
92+
name: "filter - array transformation",
93+
jsonInput: `[1,2,3]`,
94+
jqFilter: "map(. * 2)",
95+
wantErr: false,
96+
validate: func(t *testing.T, output string) {
97+
if !strings.Contains(output, "2") && !strings.Contains(output, "4") && !strings.Contains(output, "6") {
98+
t.Error("Expected transformed array output")
99+
}
100+
},
101+
},
62102
{
63103
name: "invalid filter - syntax error",
64104
jsonInput: `[{"name":"a"}]`,
@@ -73,6 +113,13 @@ func TestApplyJqFilter(t *testing.T) {
73113
wantErr: true,
74114
validate: nil,
75115
},
116+
{
117+
name: "empty filter",
118+
jsonInput: `{"data":"test"}`,
119+
jqFilter: "",
120+
wantErr: true,
121+
validate: nil,
122+
},
76123
}
77124

78125
for _, tt := range tests {
@@ -106,112 +153,3 @@ func TestApplyJqFilter_JqNotAvailable(t *testing.T) {
106153
t.Errorf("Expected 'jq not found in PATH' error, got: %v", err)
107154
}
108155
}
109-
110-
// TestMCPServer_StatusToolWithJq tests the status tool with jq filter parameter
111-
func TestMCPServer_StatusToolWithJq(t *testing.T) {
112-
// Skip if jq is not available
113-
if _, err := exec.LookPath("jq"); err != nil {
114-
t.Skip("Skipping test: jq not found in PATH")
115-
}
116-
117-
// Skip if the binary doesn't exist
118-
binaryPath := "../../gh-aw"
119-
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
120-
t.Skip("Skipping test: gh-aw binary not found. Run 'make build' first.")
121-
}
122-
123-
// Create a temporary directory with a workflow file
124-
tmpDir := t.TempDir()
125-
workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
126-
if err := os.MkdirAll(workflowsDir, 0755); err != nil {
127-
t.Fatalf("Failed to create workflows directory: %v", err)
128-
}
129-
130-
// Create a test workflow file
131-
workflowContent := `---
132-
on: push
133-
engine: copilot
134-
---
135-
# Test Workflow
136-
`
137-
workflowFile := filepath.Join(workflowsDir, "test.md")
138-
if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
139-
t.Fatalf("Failed to write workflow file: %v", err)
140-
}
141-
142-
// Save current directory and change to temp directory
143-
originalDir, _ := os.Getwd()
144-
defer os.Chdir(originalDir)
145-
146-
// Initialize git repository in the temp directory
147-
initCmd := exec.Command("git", "init")
148-
initCmd.Dir = tmpDir
149-
if err := initCmd.Run(); err != nil {
150-
t.Fatalf("Failed to initialize git repository: %v", err)
151-
}
152-
153-
// Create MCP client
154-
client := mcp.NewClient(&mcp.Implementation{
155-
Name: "test-client",
156-
Version: "1.0.0",
157-
}, nil)
158-
159-
// Start the MCP server as a subprocess
160-
serverCmd := exec.Command(filepath.Join(originalDir, binaryPath), "mcp-server")
161-
serverCmd.Dir = tmpDir
162-
transport := &mcp.CommandTransport{Command: serverCmd}
163-
164-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
165-
defer cancel()
166-
167-
session, err := client.Connect(ctx, transport, nil)
168-
if err != nil {
169-
t.Fatalf("Failed to connect to MCP server: %v", err)
170-
}
171-
defer session.Close()
172-
173-
// Test 1: Call status tool with jq filter to get just workflow names
174-
params := &mcp.CallToolParams{
175-
Name: "status",
176-
Arguments: map[string]any{
177-
"jq": ".[].workflow",
178-
},
179-
}
180-
result, err := session.CallTool(ctx, params)
181-
if err != nil {
182-
t.Fatalf("Failed to call status tool with jq filter: %v", err)
183-
}
184-
185-
// Verify result contains the workflow name
186-
if textContent, ok := result.Content[0].(*mcp.TextContent); ok {
187-
if textContent.Text == "" {
188-
t.Error("Expected non-empty text content from status tool with jq filter")
189-
}
190-
// The output should contain "test" (the workflow name)
191-
t.Logf("Status tool output with jq filter: %s", textContent.Text)
192-
} else {
193-
t.Error("Expected text content from status tool with jq filter")
194-
}
195-
196-
// Test 2: Call status tool with jq filter to count workflows
197-
params = &mcp.CallToolParams{
198-
Name: "status",
199-
Arguments: map[string]any{
200-
"jq": "length",
201-
},
202-
}
203-
result, err = session.CallTool(ctx, params)
204-
if err != nil {
205-
t.Fatalf("Failed to call status tool with jq count filter: %v", err)
206-
}
207-
208-
// Verify result contains a number
209-
if textContent, ok := result.Content[0].(*mcp.TextContent); ok {
210-
if textContent.Text == "" {
211-
t.Error("Expected non-empty text content from status tool with jq count filter")
212-
}
213-
t.Logf("Status tool count output: %s", textContent.Text)
214-
} else {
215-
t.Error("Expected text content from status tool with jq count filter")
216-
}
217-
}

0 commit comments

Comments
 (0)