github.com/bazelbuild/remote-apis-sdks@v0.0.0-20240425170053-8a36686a6350/go/pkg/client/client_test.go (about) 1 package client 2 3 import ( 4 "context" 5 "errors" 6 "net" 7 "os" 8 "path" 9 "testing" 10 11 repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2" 12 svpb "github.com/bazelbuild/remote-apis/build/bazel/semver" 13 "google.golang.org/grpc" 14 ) 15 16 const ( 17 // This key pair for `foo.example.com` was generated using https://golang.org/src/crypto/tls/generate_cert.go 18 19 tlsCert = ` 20 -----BEGIN CERTIFICATE----- 21 MIIC/zCCAeegAwIBAgIQa6p4srafgw0S6iDvt7rlXzANBgkqhkiG9w0BAQsFADAS 22 MRAwDgYDVQQKEwdBY21lIENvMB4XDTIwMTAwODA5NTExM1oXDTIxMTAwODA5NTEx 23 M1owEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 24 AQoCggEBAM1Mo2UOcQxkPHs2+oaclJsPZo80Cqb0Oze0zxA9uFXg6SiklqPjWUPc 25 o2TTBmbpw6t2Cv6Ywr6cTB7PJXCruOFo5x+6rZlzjrfqArYn3Btv64/6SwaarIEW 26 UcisWDvcEW+QArAmsJo39RL4ptsb+0Eoqkm1WeQ31XWh9i7W0F3qy43lC4k5QpXb 27 imeevejwk3TMFAnAc8dBXxNDg3gr9isDq2yH1ZhJ/4BNcle6WYNny0Uh9kYCLCWe 28 PK1ZYOeT1rRCtBJTg7w1/e0Qpln6V/MsJNtgt14vYPsHAsW+AMLycN1f9/8T7joz 29 6/D8GVMC5Vaz2z5CxeNb5ifCQkmEN8kCAwEAAaNRME8wDgYDVR0PAQH/BAQDAgWg 30 MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwGgYDVR0RBBMwEYIP 31 Zm9vLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQB0d9y7XyLwWMBMxIRL 32 5rdgk9AvpVKX5FtYkJmxG7F9p67GlLhy314SyPDaUm/tl0yf/LM9m7BM7m9900mO 33 9qjfTi6wc0Co9dDMhi4rh0CPlEilWpiA7wAyM0eVQPJx+wv/u/8aDzVy/Cr8wLwi 34 UyXETaAVsbrHmoIMQgyy0boTDzBwwhx8e/heqaZfV6H6RsBpChYFIF0A7T92bu91 35 WmSA9kfUwzYeOvLyX/s+J77U/bnYo4Fl/LnBkBr1vkTPF9xGWYZFU0Ok/uk9TyrB 36 vI06wv3H6phi4fWDMbWuSiBHr3AefTqcxkBcaxYCzLgOdRJEnV1GWdw/Gt9PNyJ2 37 IJff 38 -----END CERTIFICATE----- 39 ` 40 41 tlsKey = ` 42 -----BEGIN PRIVATE KEY----- 43 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDNTKNlDnEMZDx7 44 NvqGnJSbD2aPNAqm9Ds3tM8QPbhV4OkopJaj41lD3KNk0wZm6cOrdgr+mMK+nEwe 45 zyVwq7jhaOcfuq2Zc4636gK2J9wbb+uP+ksGmqyBFlHIrFg73BFvkAKwJrCaN/US 46 +KbbG/tBKKpJtVnkN9V1ofYu1tBd6suN5QuJOUKV24pnnr3o8JN0zBQJwHPHQV8T 47 Q4N4K/YrA6tsh9WYSf+ATXJXulmDZ8tFIfZGAiwlnjytWWDnk9a0QrQSU4O8Nf3t 48 EKZZ+lfzLCTbYLdeL2D7BwLFvgDC8nDdX/f/E+46M+vw/BlTAuVWs9s+QsXjW+Yn 49 wkJJhDfJAgMBAAECggEAOdsALQl92FUjKkIbJfZYdr5HU9IBsn0fdxpwRA5wtVr1 50 uitv/G2kiVhTf6Vsp3H3X4mbnZYlQ5w+Y2xTC8aJCpUKlUmBhL4pGTFMZFMlV2Ia 51 C3Ao1oqeVJ6am6feEbh5/WujJksw97UPTrJeK19eVkWEf9MoqwUFFep3u0l0XRK9 52 zR5ZhhZe+3C1DS3r7P89/7ZlnqLuba94fIqwn11D7jzmiZQBuBLa6X16grGs03Gb 53 LoBoiTj7g0a6nSEcDen5jI8oPrZuMWAlTXLYb68eCgvU2FXe0A2PFZzh8XQ71Y4J 54 Y6L0oGGiTThgphsH/bqQi03mF/Hd0BcIlBe+8mPSFQKBgQDuUI5ENq650eOf6PPh 55 abLl+cmBIxNdfFYZHB5IapDfqKBK3imD3QYT4cBdbcQ0PWjbFj/aCgkPYCmMxBSX 56 WGVohhH4sXHNIS0BL6dGalQhE+C3tWkzg79AE84tTrvijVuOom3RiR0SGzBQ4YBl 57 boPPKinY6Hz6FEhlfWdlwR0GcwKBgQDciN2m1wNG6gkDg/2OEn+g79boKPdI0lC4 58 VTfQ6xDKwGs2N8HIOoeXHhTStgkPCeNmxuOtEtqfKKRpje2ZRFvjZWOh6t+CvX0w 59 ZE+GhJtBC0ODYQ9+Hai1HgBbLYrMR44cRWRx5uMD4/v4Vrg7jdtpUICeonPd4mMD 60 ppgV0Ze90wKBgBWu+LLWMuGvakGjhYUuA9wO9TmtwlZQONlCCLNYFuRyyFrniel4 61 k9P9R254zVBfggnlJVwma5qdikpHkQQQQ/HVuQ7ivlMGwLyJ5HNwu0TjNSAh/nd0 62 dlNTOgA/WLMNX5ZDyzA0hJOgK65ARll0S8Puty4IQU7Tx56MYXsNriFnAoGBAI1g 63 +Kfio2ks1AZ68WvJFCT6XM2Mpar7mV/y0cuPRr6USKcDa6mPTClm0Xab2IbZkfzr 64 eD4WHi12gqBgqnddSYMoFo1Q42UPUVhalljoXhd+vxNUf/gbz3H8/8F0Gs0G+mXJ 65 XrFekR6HY5s7DPSw5n5Ha41HG/ydC1jlzg3+xcadAoGBAIZ/ZJpnC2Tv8poGHbmD 66 B3hWlPIQSU2dEGQCEhiGhzoi7muXmYqx5t8YnMGLGmsltLxD95KhACrTZZmBGiUS 67 HavFSkKMI1EuncwH2ooshIF9ZQqYNpIH7YGxnzDgNXnmeb26FI+b0uuxW74PWZrL 68 h4A58eQ+JGSLao6JSmi2T0tZ 69 -----END PRIVATE KEY----- 70 ` 71 72 instance = "instance" 73 ) 74 75 func TestCreateTLSConfig(t *testing.T) { 76 t.Run("EmptyDialParams", func(t *testing.T) { 77 _, err := createTLSConfig(DialParams{}) 78 if err != nil { 79 t.Errorf("Could not create TLS config: %v", err) 80 } 81 }) 82 83 t.Run("OverrideServerName", func(t *testing.T) { 84 tlsConfig, err := createTLSConfig(DialParams{ 85 TLSServerName: "foo.example.com", 86 }) 87 if err != nil { 88 t.Errorf("Could not create TLS config: %v", err) 89 } 90 if tlsConfig.ServerName != "foo.example.com" { 91 t.Errorf("Expected ServerName to be 'foo.example.com', got '%v'", tlsConfig.ServerName) 92 } 93 }) 94 95 t.Run("UseClientCertificate", func(t *testing.T) { 96 t.Run("OnlyTLSClientAuthCert", func(t *testing.T) { 97 _, err := createTLSConfig(DialParams{ 98 TLSClientAuthCert: "/foo/bar", 99 }) 100 if err == nil { 101 t.Error("Expected error creating mTLS config without auth-key, got nil") 102 } 103 }) 104 105 t.Run("OnlyTLSClientAuthKey", func(t *testing.T) { 106 _, err := createTLSConfig(DialParams{ 107 TLSClientAuthKey: "/foo/bar", 108 }) 109 if err == nil { 110 t.Error("Expected error creating mTLS config without auth-cert, got nil") 111 } 112 }) 113 114 t.Run("OnlyTLSClientAuthCert", func(t *testing.T) { 115 certPath := path.Join(t.TempDir(), "cert.pem") 116 if err := os.WriteFile(certPath, []byte(tlsCert), 0644); err != nil { 117 t.Fatalf("Could not write '%v': %v", certPath, err) 118 } 119 keyPath := path.Join(t.TempDir(), "key.pem") 120 if err := os.WriteFile(keyPath, []byte(tlsKey), 0644); err != nil { 121 t.Fatalf("Could not write '%v': %v", keyPath, err) 122 } 123 124 tlsConfig, err := createTLSConfig(DialParams{ 125 TLSClientAuthCert: certPath, 126 TLSClientAuthKey: keyPath, 127 }) 128 if err != nil { 129 t.Errorf("Could not create TLS config: %v", err) 130 } 131 if len(tlsConfig.Certificates) != 1 { 132 t.Errorf("Expected exactly 1 certificate, got: %v", tlsConfig.Certificates) 133 } 134 }) 135 }) 136 } 137 138 func TestVersionComparison(t *testing.T) { 139 latestSupportedVersion := &svpb.SemVer{Major: 2, Minor: 1} 140 serverCapabilities := &repb.ServerCapabilities{ 141 HighApiVersion: latestSupportedVersion, 142 } 143 144 if !highAPIVersionNewerThanOrEqualTo(serverCapabilities, 1, 9) { 145 t.Errorf("Got: !(2.1 >= 1.9") 146 } 147 148 if !highAPIVersionNewerThanOrEqualTo(serverCapabilities, 2, 0) { 149 t.Errorf("Got: !(2.1 >= 2.0") 150 } 151 152 if highAPIVersionNewerThanOrEqualTo(serverCapabilities, 2, 2) { 153 t.Errorf("Got: 2.1 >= 2.2") 154 } 155 156 if highAPIVersionNewerThanOrEqualTo(&repb.ServerCapabilities{}, 0, 1) { 157 t.Errorf("Got: empty >= 0.1") 158 } 159 160 if highAPIVersionNewerThanOrEqualTo(nil, 0, 1) { 161 t.Errorf("Got: nil >= 0.1") 162 } 163 } 164 165 func TestActionSupportsPlatformProperties(t *testing.T) { 166 if supportsActionPlatformProperties(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 1}}) { 167 t.Errorf("Got platform properties field suppported for v2.1 (expected only for v >= 2.2)") 168 } 169 170 if !supportsActionPlatformProperties(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 2}}) { 171 t.Errorf("Got platform properties field not suppported for v2.2 (expected for v >= 2.2)") 172 } 173 174 if !supportsActionPlatformProperties(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 3}}) { 175 t.Errorf("Got platform properties field not suppported for v2.3 (expected for v >= 2.2)") 176 } 177 } 178 179 func TestCommandUsesOutputPaths(t *testing.T) { 180 if supportsCommandOutputPaths(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 0}}) { 181 t.Errorf("Got `output_paths` field suppported for v2.0 (expected only for v >= 2.1)") 182 } 183 184 if !supportsCommandOutputPaths(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 1}}) { 185 t.Errorf("Got `output_paths` field not suppported for v2.1 (expected for v >= 2.1)") 186 } 187 188 if !supportsCommandOutputPaths(&repb.ServerCapabilities{HighApiVersion: &svpb.SemVer{Major: 2, Minor: 2}}) { 189 t.Errorf("Got `output_paths` field not suppported for v2.2 (expected for v >= 2.1)") 190 } 191 } 192 193 func TestNewClient(t *testing.T) { 194 t.Parallel() 195 ctx := context.Background() 196 197 c, err := NewClient(ctx, instance, DialParams{ 198 Service: "server", 199 NoSecurity: true, 200 }, StartupCapabilities(false)) 201 if err != nil { 202 t.Fatalf("Error creating client: %v", err) 203 } 204 defer c.Close() 205 } 206 207 func TestNewClientFromConnection(t *testing.T) { 208 t.Parallel() 209 ctx := context.Background() 210 l, err := net.Listen("tcp", ":0") 211 if err != nil { 212 t.Fatalf("Cannot listen: %v", err) 213 } 214 conn, err := grpc.Dial(l.Addr().String(), grpc.WithInsecure()) 215 if err != nil { 216 t.Fatalf("Cannot establish gRPC connection: %v", err) 217 } 218 219 c, err := NewClientFromConnection(ctx, instance, conn, conn, StartupCapabilities(false)) 220 if err != nil { 221 t.Fatalf("Error creating client: %v", err) 222 } 223 defer c.Close() 224 225 _, err = NewClientFromConnection(ctx, instance, nil, conn, StartupCapabilities(false)) 226 if err == nil { 227 t.Fatalf("Expected error got nil") 228 } 229 230 _, err = NewClientFromConnection(ctx, instance, conn, nil, StartupCapabilities(false)) 231 if err == nil { 232 t.Fatalf("Expected error got nil") 233 } 234 } 235 236 func TestResourceName(t *testing.T) { 237 t.Parallel() 238 239 tests := []struct { 240 name string 241 instanceName string 242 segments []string 243 wantName string 244 wantErr error 245 }{ 246 { 247 name: "valid", 248 segments: []string{"uploads", "uuid", "blobs", "abc", "1", "meta"}, 249 instanceName: "the/instance", 250 wantName: "the/instance/uploads/uuid/blobs/abc/1/meta", 251 }, 252 { 253 name: "empty_instance_name", 254 segments: []string{"uploads", "uuid", "blobs", "abc", "1", "meta"}, 255 instanceName: "", 256 wantName: "uploads/uuid/blobs/abc/1/meta", 257 }, 258 { 259 name: "empty_instance_name_no_segments", 260 segments: []string{}, 261 instanceName: "", 262 wantName: "", 263 }, 264 { 265 name: "empty_segment", 266 segments: []string{"uploads", "", "uuid", "blobs", "abc", "1", "meta"}, 267 instanceName: "the/instance", 268 wantName: "", 269 wantErr: ErrEmptySegment, 270 }, 271 } 272 273 for _, test := range tests { 274 t.Run(test.name, func(t *testing.T) { 275 c := Client{InstanceName: test.instanceName} 276 name, err := c.ResourceName(test.segments...) 277 if !errors.Is(test.wantErr, err) { 278 t.Errorf("unexpected error; want %v, got %v", test.wantErr, err) 279 } 280 if name != test.wantName { 281 t.Errorf("name mismatch; want %q, got %q", test.wantName, name) 282 } 283 }) 284 } 285 }