|
1 | 1 | package cmd |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "bytes" |
4 | 5 | "context" |
| 6 | + "os" |
5 | 7 | "testing" |
6 | 8 |
|
| 9 | + "github.com/spf13/cobra" |
7 | 10 | "github.com/stretchr/testify/require" |
8 | 11 | "google.golang.org/grpc" |
| 12 | + "google.golang.org/grpc/codes" |
9 | 13 | "google.golang.org/grpc/metadata" |
| 14 | + "google.golang.org/grpc/status" |
10 | 15 |
|
11 | 16 | "github.com/authzed/authzed-go/pkg/responsemeta" |
12 | 17 | v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" |
13 | 18 |
|
| 19 | + "github.com/authzed/zed/internal/client" |
14 | 20 | zedtesting "github.com/authzed/zed/internal/testing" |
15 | 21 | ) |
16 | 22 |
|
@@ -138,3 +144,80 @@ func (m *mockSchemaServiceClient) ReadSchema(_ context.Context, _ *v1.ReadSchema |
138 | 144 | func (m *mockSchemaServiceClient) WriteSchema(_ context.Context, _ *v1.WriteSchemaRequest, _ ...grpc.CallOption) (*v1.WriteSchemaResponse, error) { |
139 | 145 | return nil, nil |
140 | 146 | } |
| 147 | + |
| 148 | +func TestVersionCommandWithUnavailableServer(t *testing.T) { |
| 149 | + // Note: Not running in parallel because we modify globals (client.NewClient, os.Stdout, os.Stderr) |
| 150 | + |
| 151 | + // This test verifies issue #554 is fixed: when SpiceDB is unavailable, |
| 152 | + // zed version should not produce noisy retry error logs |
| 153 | + |
| 154 | + // Save and restore original client.NewClient |
| 155 | + originalNewClient := client.NewClient |
| 156 | + t.Cleanup(func() { |
| 157 | + client.NewClient = originalNewClient |
| 158 | + }) |
| 159 | + |
| 160 | + // Mock client.NewClient to return Unavailable error |
| 161 | + client.NewClient = func(_ *cobra.Command) (client.Client, error) { |
| 162 | + return nil, status.Error(codes.Unavailable, "connection refused") |
| 163 | + } |
| 164 | + |
| 165 | + // Capture stdout to check output |
| 166 | + oldStdout := os.Stdout |
| 167 | + rOut, wOut, _ := os.Pipe() |
| 168 | + os.Stdout = wOut |
| 169 | + t.Cleanup(func() { |
| 170 | + os.Stdout = oldStdout |
| 171 | + }) |
| 172 | + |
| 173 | + // Capture stderr to verify no retry errors are logged |
| 174 | + oldStderr := os.Stderr |
| 175 | + rErr, wErr, _ := os.Pipe() |
| 176 | + os.Stderr = wErr |
| 177 | + t.Cleanup(func() { |
| 178 | + os.Stderr = oldStderr |
| 179 | + }) |
| 180 | + |
| 181 | + // Create command with all required flags |
| 182 | + cmd := zedtesting.CreateTestCobraCommandWithFlagValue(t, |
| 183 | + zedtesting.BoolFlag{FlagName: "include-remote-version", FlagValue: true}, |
| 184 | + zedtesting.BoolFlag{FlagName: "include-deps", FlagValue: false}, |
| 185 | + zedtesting.UintFlag{FlagName: "max-retries", FlagValue: 10}, |
| 186 | + zedtesting.StringFlag{FlagName: "endpoint", FlagValue: ""}, |
| 187 | + zedtesting.StringFlag{FlagName: "token", FlagValue: ""}, |
| 188 | + zedtesting.StringFlag{FlagName: "certificate-path", FlagValue: ""}, |
| 189 | + zedtesting.StringFlag{FlagName: "hostname-override", FlagValue: ""}, |
| 190 | + zedtesting.StringFlag{FlagName: "permissions-system", FlagValue: ""}, |
| 191 | + zedtesting.StringFlag{FlagName: "proxy", FlagValue: ""}, |
| 192 | + zedtesting.StringFlag{FlagName: "request-id", FlagValue: ""}, |
| 193 | + zedtesting.BoolFlag{FlagName: "insecure", FlagValue: false}, |
| 194 | + zedtesting.BoolFlag{FlagName: "no-verify-ca", FlagValue: false}, |
| 195 | + zedtesting.BoolFlag{FlagName: "skip-version-check", FlagValue: false}, |
| 196 | + zedtesting.IntFlag{FlagName: "max-message-size", FlagValue: 0}, |
| 197 | + ) |
| 198 | + |
| 199 | + // Run the version command |
| 200 | + err := versionCmdFunc(cmd, nil) |
| 201 | + |
| 202 | + // Close writers and read captured outputs |
| 203 | + wOut.Close() |
| 204 | + wErr.Close() |
| 205 | + var stdout bytes.Buffer |
| 206 | + _, _ = stdout.ReadFrom(rOut) |
| 207 | + var stderr bytes.Buffer |
| 208 | + _, _ = stderr.ReadFrom(rErr) |
| 209 | + |
| 210 | + // Command should succeed despite unavailable server |
| 211 | + require.NoError(t, err) |
| 212 | + |
| 213 | + // Stdout should contain client version and service: (unknown) |
| 214 | + output := stdout.String() |
| 215 | + require.Contains(t, output, "zed") |
| 216 | + require.Contains(t, output, "service:") |
| 217 | + require.Contains(t, output, "(unknown)") |
| 218 | + |
| 219 | + // Stderr should be empty (no retry error logs) |
| 220 | + stderrOutput := stderr.String() |
| 221 | + require.NotContains(t, stderrOutput, "retrying gRPC call") |
| 222 | + require.NotContains(t, stderrOutput, "Unavailable") |
| 223 | +} |
0 commit comments