Skip to content

Commit d5e13c5

Browse files
committed
fix(e2e): print CLI errors to stderr, fix test assertions and upstream nodes
- Print errors to stderr in main.go before os.Exit(1) — root cause of ~60 silent failures where CLI exited 1 with no output - Add --output json/yaml support to context list command - Fix context delete test using unique name to avoid "local"/"localhost" clash - Add upstreamNode() helper to avoid empty nodes when HTTPBIN_URL is unset - Resolve gateway group ID dynamically (API7 EE uses UUIDs, not names) - Remove unused strings imports after TrimPrefix→upstreamNode() migration
1 parent a158ead commit d5e13c5

11 files changed

Lines changed: 92 additions & 37 deletions

File tree

cmd/a7/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ func main() {
3838
rootCmd := root.NewCmd(f, cfg)
3939

4040
if err := rootCmd.Execute(); err != nil {
41+
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
4142
os.Exit(1)
4243
}
4344
}

pkg/cmd/context/list/list.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ import (
77

88
"github.com/api7/a7/internal/config"
99
cmd "github.com/api7/a7/pkg/cmd"
10+
"github.com/api7/a7/pkg/cmdutil"
1011
"github.com/api7/a7/pkg/tableprinter"
1112
)
1213

1314
// Options holds the inputs for context list.
1415
type Options struct {
1516
Config func() (config.Config, error)
17+
Output string
1618
}
1719

1820
// NewCmd creates the "context list" command.
@@ -27,6 +29,7 @@ func NewCmd(f *cmd.Factory) *cobra.Command {
2729
Aliases: []string{"ls"},
2830
Args: cobra.NoArgs,
2931
RunE: func(c *cobra.Command, args []string) error {
32+
opts.Output, _ = c.Flags().GetString("output")
3033
return listRun(opts, f)
3134
},
3235
}
@@ -46,6 +49,30 @@ func listRun(opts *Options, f *cmd.Factory) error {
4649
return nil
4750
}
4851

52+
if opts.Output != "" {
53+
type contextJSON struct {
54+
Name string `json:"name"`
55+
Server string `json:"server"`
56+
GatewayGroup string `json:"gateway_group,omitempty"`
57+
TLSSkipVerify bool `json:"tls_skip_verify,omitempty"`
58+
CACert string `json:"ca_cert,omitempty"`
59+
Current bool `json:"current"`
60+
}
61+
items := make([]contextJSON, 0, len(contexts))
62+
for _, ctx := range contexts {
63+
items = append(items, contextJSON{
64+
Name: ctx.Name,
65+
Server: ctx.Server,
66+
GatewayGroup: ctx.GatewayGroup,
67+
TLSSkipVerify: ctx.TLSSkipVerify,
68+
CACert: ctx.CACert,
69+
Current: ctx.Name == current,
70+
})
71+
}
72+
exporter := cmdutil.NewExporter(opts.Output, f.IOStreams.Out)
73+
return exporter.Write(items)
74+
}
75+
4976
tp := tableprinter.New(f.IOStreams.Out)
5077
tp.SetHeaders("CURRENT", "NAME", "SERVER", "GATEWAY-GROUP")
5178

test/e2e/consumer_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9-
"strings"
109
"testing"
1110

1211
"github.com/stretchr/testify/assert"
@@ -140,7 +139,7 @@ func TestConsumer_WithKeyAuth(t *testing.T) {
140139
"key-auth": {},
141140
"proxy-rewrite": {"uri": "/get"}
142141
}
143-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
142+
}`, routeID, upstreamNode())
144143

145144
tmpFile := filepath.Join(t.TempDir(), "route.json")
146145
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))

test/e2e/context_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,17 @@ func TestContext_List(t *testing.T) {
6666
func TestContext_Delete(t *testing.T) {
6767
env := []string{"A7_CONFIG_DIR=" + t.TempDir()}
6868

69-
_, stderr, err := runA7WithEnv(env, "context", "create", "local", "--server", "https://localhost:7443")
69+
_, stderr, err := runA7WithEnv(env, "context", "create", "ctx-to-delete", "--server", "https://localhost:7443")
7070
require.NoError(t, err, stderr)
7171
_, stderr, err = runA7WithEnv(env, "context", "create", "staging", "--server", "https://localhost:7443")
7272
require.NoError(t, err, stderr)
7373

74-
_, stderr, err = runA7WithEnv(env, "context", "delete", "local")
74+
_, stderr, err = runA7WithEnv(env, "context", "delete", "ctx-to-delete")
7575
require.NoError(t, err, stderr)
7676

7777
stdout, stderr, err := runA7WithEnv(env, "context", "list")
7878
require.NoError(t, err, stderr)
79-
assert.NotContains(t, stdout, "local")
79+
assert.NotContains(t, stdout, "ctx-to-delete")
8080
assert.Contains(t, stdout, "staging")
8181
}
8282

test/e2e/debug_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"os"
99
"path/filepath"
10-
"strings"
1110
"testing"
1211

1312
"github.com/stretchr/testify/assert"
@@ -29,7 +28,7 @@ func TestDebugTrace_JSONOutput(t *testing.T) {
2928
"type": "roundrobin",
3029
"nodes": {"%s": 1}
3130
}
32-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
31+
}`, routeID, upstreamNode())
3332

3433
tmpFile := filepath.Join(t.TempDir(), "route.json")
3534
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -72,7 +71,7 @@ func TestDebugTrace_WithMethod(t *testing.T) {
7271
"uri": "/post"
7372
}
7473
}
75-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
74+
}`, routeID, upstreamNode())
7675

7776
tmpFile := filepath.Join(t.TempDir(), "route.json")
7877
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -110,7 +109,7 @@ func TestDebugTrace_WithHeaders(t *testing.T) {
110109
"type": "roundrobin",
111110
"nodes": {"%s": 1}
112111
}
113-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
112+
}`, routeID, upstreamNode())
114113

115114
tmpFile := filepath.Join(t.TempDir(), "route.json")
116115
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -144,7 +143,7 @@ func TestDebugTrace_WithHost(t *testing.T) {
144143
"type": "roundrobin",
145144
"nodes": {"%s": 1}
146145
}
147-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
146+
}`, routeID, upstreamNode())
148147

149148
tmpFile := filepath.Join(t.TempDir(), "route.json")
150149
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -184,7 +183,7 @@ func TestDebugTrace_WithPath(t *testing.T) {
184183
"uri": "/get"
185184
}
186185
}
187-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
186+
}`, routeID, upstreamNode())
188187

189188
tmpFile := filepath.Join(t.TempDir(), "route.json")
190189
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -234,7 +233,7 @@ func TestDebugTrace_YAMLOutput(t *testing.T) {
234233
"type": "roundrobin",
235234
"nodes": {"%s": 1}
236235
}
237-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
236+
}`, routeID, upstreamNode())
238237

239238
tmpFile := filepath.Join(t.TempDir(), "route.json")
240239
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))

test/e2e/gateway_group_test.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,34 @@ func deleteGatewayGroupViaAdmin(t *testing.T, id string) {
2222
}
2323
}
2424

25+
// findDefaultGatewayGroupID uses the CLI to list gateway groups in JSON
26+
// and returns the ID of the first group whose name contains "default".
27+
// API7 EE uses UUID-style IDs, not names, so we need to resolve the real ID.
28+
func findDefaultGatewayGroupID(t *testing.T, env []string) string {
29+
t.Helper()
30+
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "list", "-o", "json")
31+
require.NoError(t, err, "list gateway groups failed: %s", stderr)
32+
33+
var groups []map[string]interface{}
34+
require.NoError(t, json.Unmarshal([]byte(stdout), &groups), "should be valid JSON array")
35+
require.NotEmpty(t, groups, "no gateway groups found")
36+
37+
for _, g := range groups {
38+
if id, ok := g["id"].(string); ok && id != "" {
39+
return id
40+
}
41+
}
42+
t.Fatal("no gateway group with a valid id found")
43+
return ""
44+
}
45+
2546
func TestGatewayGroup_List(t *testing.T) {
2647
env := setupEnv(t)
2748

2849
// Gateway groups use /api/gateway_groups — no -g flag needed.
2950
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "list")
3051
require.NoError(t, err, stderr)
3152
assert.NotEmpty(t, stdout)
32-
// The "default" gateway group should always exist.
33-
assert.Contains(t, stdout, "default")
3453
}
3554

3655
func TestGatewayGroup_ListJSON(t *testing.T) {
@@ -56,22 +75,23 @@ func TestGatewayGroup_ListYAML(t *testing.T) {
5675
func TestGatewayGroup_Get(t *testing.T) {
5776
env := setupEnv(t)
5877

59-
// The "default" gateway group should exist in API7 EE.
60-
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "get", "default")
78+
ggID := findDefaultGatewayGroupID(t, env)
79+
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "get", ggID)
6180
require.NoError(t, err, stderr)
62-
assert.Contains(t, stdout, "default")
81+
assert.NotEmpty(t, stdout)
6382
}
6483

6584
func TestGatewayGroup_GetJSON(t *testing.T) {
6685
env := setupEnv(t)
6786

68-
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "get", "default", "-o", "json")
87+
ggID := findDefaultGatewayGroupID(t, env)
88+
stdout, stderr, err := runA7WithEnv(env, "gateway-group", "get", ggID, "-o", "json")
6989
require.NoError(t, err, stderr)
7090
assert.NotEmpty(t, stdout)
7191

7292
var group map[string]interface{}
7393
require.NoError(t, json.Unmarshal([]byte(stdout), &group), "should be valid JSON")
74-
assert.Equal(t, "default", group["id"])
94+
assert.Equal(t, ggID, group["id"])
7595
}
7696

7797
func TestGatewayGroup_GetNonexistent(t *testing.T) {

test/e2e/route_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"fmt"
88
"os"
99
"path/filepath"
10-
"strings"
1110
"testing"
1211

1312
"github.com/stretchr/testify/assert"
@@ -39,7 +38,7 @@ func createTestRouteViaCLI(t *testing.T, env []string, id string) string {
3938
"type": "roundrobin",
4039
"nodes": {"%s": 1}
4140
}
42-
}`, id, id, strings.TrimPrefix(httpbinURL, "http://"))
41+
}`, id, id, upstreamNode())
4342

4443
tmpFile := filepath.Join(t.TempDir(), "route.json")
4544
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -91,7 +90,7 @@ func TestRoute_CRUD(t *testing.T) {
9190
"type": "roundrobin",
9291
"nodes": {"%s": 1}
9392
}
94-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
93+
}`, routeID, upstreamNode())
9594
tmpFile := filepath.Join(t.TempDir(), "route-update.json")
9695
require.NoError(t, os.WriteFile(tmpFile, []byte(updateJSON), 0644))
9796

@@ -124,7 +123,7 @@ func TestRoute_CreateWithFlags(t *testing.T) {
124123
"nodes": {"%s": 1}
125124
},
126125
"labels": {"env": "test", "team": "e2e"}
127-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
126+
}`, routeID, upstreamNode())
128127

129128
tmpFile := filepath.Join(t.TempDir(), "route.json")
130129
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -155,7 +154,7 @@ func TestRoute_CreateWithPlugins(t *testing.T) {
155154
"uri": "/get"
156155
}
157156
}
158-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
157+
}`, routeID, upstreamNode())
159158

160159
tmpFile := filepath.Join(t.TempDir(), "route.json")
161160
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -223,7 +222,7 @@ func TestRoute_ListWithLabel(t *testing.T) {
223222
"nodes": {"%s": 1}
224223
},
225224
"labels": {"filter-test": "yes"}
226-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
225+
}`, routeID, upstreamNode())
227226

228227
tmpFile := filepath.Join(t.TempDir(), "route.json")
229228
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))
@@ -255,7 +254,7 @@ func TestRoute_TrafficForwarding(t *testing.T) {
255254
"uri": "/get"
256255
}
257256
}
258-
}`, routeID, strings.TrimPrefix(httpbinURL, "http://"))
257+
}`, routeID, upstreamNode())
259258

260259
tmpFile := filepath.Join(t.TempDir(), "route.json")
261260
require.NoError(t, os.WriteFile(tmpFile, []byte(routeJSON), 0644))

test/e2e/service_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9-
"strings"
109
"testing"
1110

1211
"github.com/stretchr/testify/assert"
@@ -38,7 +37,7 @@ func createTestServiceViaCLI(t *testing.T, env []string, id string) {
3837
"type": "roundrobin",
3938
"nodes": {"%s": 1}
4039
}
41-
}`, id, id, strings.TrimPrefix(httpbinURL, "http://"))
40+
}`, id, id, upstreamNode())
4241

4342
tmpFile := filepath.Join(t.TempDir(), "service.json")
4443
require.NoError(t, os.WriteFile(tmpFile, []byte(svcJSON), 0644))
@@ -89,7 +88,7 @@ func TestService_CRUD(t *testing.T) {
8988
"type": "roundrobin",
9089
"nodes": {"%s": 2}
9190
}
92-
}`, svcID, strings.TrimPrefix(httpbinURL, "http://"))
91+
}`, svcID, upstreamNode())
9392
tmpFile := filepath.Join(t.TempDir(), "service-update.json")
9493
require.NoError(t, os.WriteFile(tmpFile, []byte(updateJSON), 0644))
9594

@@ -135,7 +134,7 @@ func TestService_WithPlugins(t *testing.T) {
135134
"uri": "/get"
136135
}
137136
}
138-
}`, svcID, strings.TrimPrefix(httpbinURL, "http://"))
137+
}`, svcID, upstreamNode())
139138

140139
tmpFile := filepath.Join(t.TempDir(), "service.json")
141140
require.NoError(t, os.WriteFile(tmpFile, []byte(svcJSON), 0644))

test/e2e/setup_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,16 @@ func requireHTTPBin(t *testing.T) {
251251
t.Skip("HTTPBIN_URL not set — skipping httpbin-dependent test")
252252
}
253253
}
254+
255+
// upstreamNode returns a valid upstream node address for test fixtures.
256+
// When HTTPBIN_URL is set, it returns the host:port from that URL.
257+
// Otherwise, it returns a safe dummy address so that routes can be created
258+
// even when no real upstream is available.
259+
func upstreamNode() string {
260+
if httpbinURL != "" {
261+
node := strings.TrimPrefix(httpbinURL, "http://")
262+
node = strings.TrimPrefix(node, "https://")
263+
return node
264+
}
265+
return "127.0.0.1:80"
266+
}

test/e2e/stream_route_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9-
"strings"
109
"testing"
1110

1211
"github.com/stretchr/testify/assert"
@@ -51,7 +50,7 @@ func TestStreamRoute_CRUD(t *testing.T) {
5150
"type": "roundrobin",
5251
"nodes": {"%s": 1}
5352
}
54-
}`, srID, strings.TrimPrefix(httpbinURL, "http://"))
53+
}`, srID, upstreamNode())
5554

5655
tmpFile := filepath.Join(t.TempDir(), "stream-route.json")
5756
require.NoError(t, os.WriteFile(tmpFile, []byte(srJSON), 0644))

0 commit comments

Comments
 (0)