Skip to content
File

Blob: cmd/client/cli_test.go

go147 lines
1package client
2 
3import (
4 "bytes"
5 "context"
6 "os"
7 "path/filepath"
8 "strings"
9 "testing"
10 
11 tunclient "go.miragespace.co/specter/tun/client"
12 
13 "github.com/stretchr/testify/require"
14 "github.com/urfave/cli/v3"
15)
16 
17func TestClientCLIInheritsFlagsContextAndMetadata(t *testing.T) {
18 type contextKey struct{}
19 ctx := context.WithValue(t.Context(), contextKey{}, "request-context")
20 cmd := Generate()
21 called := false
22 for _, subcommand := range cmd.Commands {
23 if subcommand.Name == "connect" {
24 subcommand.Action = func(ctx context.Context, command *cli.Command) error {
25 called = true
26 require.True(t, command.Bool("insecure"))
27 require.Equal(t, "target.example.com", command.Args().First())
28 require.Equal(t, "request-context", ctx.Value(contextKey{}))
29 require.Equal(t, "root-metadata", command.Root().Metadata["test"])
30 return nil
31 }
32 }
33 }
34 app := &cli.Command{
35 Name: "specter",
36 Commands: []*cli.Command{cmd},
37 Metadata: map[string]any{"test": "root-metadata"},
38 }
39 require.NoError(t, app.Run(ctx, []string{"specter", "client", "--insecure", "connect", "target.example.com"}))
40 require.True(t, called)
41}
42 
43func TestConfigExampleUsesRootWriter(t *testing.T) {
44 var output bytes.Buffer
45 app := &cli.Command{Name: "specter", Writer: &output, Commands: []*cli.Command{Generate()}}
46 require.NoError(t, app.Run(t.Context(), []string{"specter", "client", "config-example"}))
47 require.Equal(t, tunclient.ExampleConfigYAML(), output.String())
48}
49 
50func TestLightweightFlags(t *testing.T) {
51 for _, name := range []string{"expose", "serve", "token"} {
52 t.Run(name, func(t *testing.T) {
53 cmd := Generate()
54 called := false
55 args := []string{"specter", "client", "--insecure", name}
56 for _, sub := range cmd.Commands {
57 if sub.Name != name {
58 continue
59 }
60 if name == "token" {
61 sub = sub.Commands[1]
62 args = append(args, "list", "--config", "owner.yaml")
63 } else {
64 args = append(args, "--apex", "example.com", "http://localhost:8080")
65 }
66 sub.Action = func(_ context.Context, cmd *cli.Command) error {
67 called = true
68 require.True(t, cmd.Bool("insecure"))
69 return nil
70 }
71 }
72 app := &cli.Command{
73 Name: "specter",
74 Commands: []*cli.Command{cmd},
75 }
76 require.NoError(t, app.Run(t.Context(), args))
77 require.True(t, called)
78 })
79 }
80 for _, name := range []string{"expose", "serve"} {
81 app := &cli.Command{
82 Name: "specter",
83 Commands: []*cli.Command{Generate()},
84 }
85 require.ErrorContains(t, app.Run(t.Context(), []string{"specter", "client", name, "--apex", "example.com", "--server", "a", "http://localhost"}), "flag provided but not defined: -server")
86 }
87 for _, tc := range []struct {
88 name string
89 file string
90 env string
91 fileSet bool
92 envSet bool
93 valid bool
94 }{
95 {
96 name: "file",
97 file: " secret\n",
98 fileSet: true,
99 valid: true,
100 },
101 {
102 name: "env",
103 env: " secret\n",
104 envSet: true,
105 valid: true,
106 },
107 {
108 name: "both",
109 file: "secret",
110 env: "secret",
111 fileSet: true,
112 envSet: true,
113 },
114 {name: "neither"},
115 {
116 name: "large file",
117 file: strings.Repeat("x", 4097),
118 fileSet: true,
119 },
120 {
121 name: "large env",
122 env: strings.Repeat("x", 4097),
123 envSet: true,
124 },
125 {
126 name: "empty",
127 env: " \n",
128 envSet: true,
129 },
130 } {
131 t.Run(tc.name, func(t *testing.T) {
132 path := ""
133 if tc.fileSet {
134 path = filepath.Join(t.TempDir(), "token")
135 require.NoError(t, os.WriteFile(path, []byte(tc.file), 0600))
136 }
137 token, err := loadTunnelToken(path, tc.env, tc.envSet)
138 if tc.valid {
139 require.NoError(t, err)
140 require.Equal(t, "secret", token)
141 } else {
142 require.Error(t, err)
143 }
144 })
145 }
146}