1package cloudflare 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/stretchr/testify/assert" 14) 15 16const ( 17 deleteWorkerResponseData = `{ 18 "result": null, 19 "success": true, 20 "errors": [], 21 "messages": [] 22}` 23 uploadWorkerResponseData = `{ 24 "result": { 25 "script": "addEventListener('fetch', event => {\n event.passThroughOnException()\nevent.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n return fetch(request)\n}", 26 "etag": "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 27 "size": 191, 28 "modified_on": "2018-06-09T15:17:01.989141Z" 29 }, 30 "success": true, 31 "errors": [], 32 "messages": [] 33}` 34 updateWorkerRouteResponse = `{ 35 "result": { 36 "id": "e7a57d8746e74ae49c25994dadb421b1", 37 "pattern": "app3.example.com/*", 38 "enabled": true 39 }, 40 "success": true, 41 "errors": [], 42 "messages": [] 43}` 44 updateWorkerRouteEntResponse = `{ 45 "result": { 46 "id": "e7a57d8746e74ae49c25994dadb421b1", 47 "pattern": "app3.example.com/*", 48 "script": "test_script_1" 49 }, 50 "success": true, 51 "errors": [], 52 "messages": [] 53}` 54 createWorkerRouteResponse = `{ 55 "result": { 56 "id": "e7a57d8746e74ae49c25994dadb421b1" 57 }, 58 "success": true, 59 "errors": [], 60 "messages": [] 61}` 62 listRouteResponseData = `{ 63 "result": [ 64 { 65 "id": "e7a57d8746e74ae49c25994dadb421b1", 66 "pattern": "app1.example.com/*", 67 "enabled": true 68 }, 69 { 70 "id": "f8b68e9857f85bf59c25994dadb421b1", 71 "pattern": "app2.example.com/*", 72 "enabled": false 73 } 74 ], 75 "success": true, 76 "errors": [], 77 "messages": [] 78}` 79 listRouteEntResponseData = `{ 80 "result": [ 81 { 82 "id": "e7a57d8746e74ae49c25994dadb421b1", 83 "pattern": "app1.example.com/*", 84 "script": "test_script_1" 85 }, 86 { 87 "id": "f8b68e9857f85bf59c25994dadb421b1", 88 "pattern": "app2.example.com/*", 89 "script": "test_script_2" 90 }, 91 { 92 "id": "2b5bf4240cd34c77852fac70b1bf745a", 93 "pattern": "app3.example.com/*" 94 } 95 ], 96 "success": true, 97 "errors": [], 98 "messages": [] 99}` 100 getRouteResponseData = `{ 101 "result": { 102 "id": "e7a57d8746e74ae49c25994dadb421b1", 103 "pattern": "app1.example.com/*", 104 "script": "script-name" 105 }, 106 "success": true, 107 "errors": [], 108 "messages": [] 109}` 110 listBindingsResponseData = `{ 111 "result": [ 112 { 113 "name": "MY_KV", 114 "namespace_id": "89f5f8fd93f94cb98473f6f421aa3b65", 115 "type": "kv_namespace" 116 }, 117 { 118 "name": "MY_WASM", 119 "type": "wasm_module" 120 }, 121 { 122 "name": "MY_PLAIN_TEXT", 123 "type": "plain_text", 124 "text": "text" 125 }, 126 { 127 "name": "MY_SECRET_TEXT", 128 "type": "secret_text" 129 }, 130 { 131 "name": "MY_NEW_BINDING", 132 "type": "some_imaginary_new_binding_type" 133 } 134 ], 135 "success": true, 136 "errors": [], 137 "messages": [] 138 }` 139 listWorkersResponseData = `{ 140 "result": [ 141 { 142 "id": "bar", 143 "created_on": "2018-04-22T17:10:48.938097Z", 144 "modified_on": "2018-04-22T17:10:48.938097Z", 145 "etag": "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a" 146 }, 147 { 148 "id": "baz", 149 "created_on": "2018-04-22T17:10:48.938097Z", 150 "modified_on": "2018-04-22T17:10:48.938097Z", 151 "etag": "380dg51e97e80b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43088b" 152 } 153 ], 154 "success": true, 155 "errors": [], 156 "messages": [] 157}` 158) 159 160var ( 161 successResponse = Response{Success: true, Errors: []ResponseInfo{}, Messages: []ResponseInfo{}} 162 workerScript = "addEventListener('fetch', event => {\n event.passThroughOnException()\nevent.respondWith(handleRequest(event.request))\n})\n\nasync function handleRequest(request) {\n return fetch(request)\n}" 163 deleteWorkerRouteResponseData = createWorkerRouteResponse 164) 165 166func getFormValue(r *http.Request, key string) ([]byte, error) { 167 err := r.ParseMultipartForm(1024 * 1024) 168 if err != nil { 169 return nil, err 170 } 171 172 // In Go 1.10 there was a bug where field values with a content-type 173 // but without a filename would end up in Form.File but in versions 174 // before and after 1.10 they would be in form.Value. Here we check 175 // both in order to handle both scenarios 176 // https://golang.org/doc/go1.11#mime/multipart 177 178 // pre/post v1.10 179 if values, ok := r.MultipartForm.Value[key]; ok { 180 return []byte(values[0]), nil 181 } 182 183 // v1.10 184 if fileHeaders, ok := r.MultipartForm.File[key]; ok { 185 file, err := fileHeaders[0].Open() 186 if err != nil { 187 return nil, err 188 } 189 return ioutil.ReadAll(file) 190 } 191 192 return nil, fmt.Errorf("no value found for key %v", key) 193} 194 195type multipartUpload = struct { 196 Script string 197 BindingMeta map[string]workerBindingMeta 198} 199 200func parseMultipartUpload(r *http.Request) (multipartUpload, error) { 201 // Parse the metadata 202 mdBytes, err := getFormValue(r, "metadata") 203 if err != nil { 204 return multipartUpload{}, err 205 } 206 207 var metadata struct { 208 BodyPart string `json:"body_part"` 209 Bindings []workerBindingMeta `json:"bindings"` 210 } 211 err = json.Unmarshal(mdBytes, &metadata) 212 if err != nil { 213 return multipartUpload{}, err 214 } 215 216 // Get the script 217 script, err := getFormValue(r, metadata.BodyPart) 218 if err != nil { 219 return multipartUpload{}, err 220 } 221 222 // Since bindings are specified in the Go API as a map but are uploaded as a 223 // JSON array, the ordering of uploaded bindings is non-deterministic. To make 224 // it easier to compare for equality without running into ordering issues, we 225 // convert it back to a map 226 bindingMeta := make(map[string]workerBindingMeta) 227 for _, binding := range metadata.Bindings { 228 bindingMeta[binding["name"].(string)] = binding 229 } 230 231 return multipartUpload{ 232 Script: string(script), 233 BindingMeta: bindingMeta, 234 }, nil 235} 236 237func TestWorkers_DeleteWorker(t *testing.T) { 238 setup() 239 defer teardown() 240 241 mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) { 242 assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) 243 w.Header().Set("content-type", "application/javascript") 244 fmt.Fprintf(w, deleteWorkerResponseData) //nolint 245 }) 246 res, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}) 247 want := WorkerScriptResponse{ 248 successResponse, 249 WorkerScript{}} 250 if assert.NoError(t, err) { 251 assert.Equal(t, want.Response, res.Response) 252 } 253} 254 255func TestWorkers_DeleteWorkerWithName(t *testing.T) { 256 setup(UsingAccount("foo")) 257 defer teardown() 258 259 mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) { 260 assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) 261 w.Header().Set("content-type", "application/javascript") 262 fmt.Fprintf(w, deleteWorkerResponseData) //nolint 263 }) 264 res, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}) 265 want := WorkerScriptResponse{ 266 successResponse, 267 WorkerScript{}} 268 if assert.NoError(t, err) { 269 assert.Equal(t, want.Response, res.Response) 270 } 271} 272 273func TestWorkers_DeleteWorkerWithNameErrorsWithoutAccountId(t *testing.T) { 274 setup() 275 defer teardown() 276 277 _, err := client.DeleteWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}) 278 assert.Error(t, err) 279} 280 281func TestWorkers_DownloadWorker(t *testing.T) { 282 setup() 283 defer teardown() 284 285 mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) { 286 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 287 w.Header().Set("content-type", "application/javascript") 288 fmt.Fprintf(w, workerScript) //nolint 289 }) 290 res, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}) 291 want := WorkerScriptResponse{ 292 successResponse, 293 WorkerScript{ 294 Script: workerScript, 295 }} 296 if assert.NoError(t, err) { 297 assert.Equal(t, want.Script, res.Script) 298 } 299} 300 301func TestWorkers_DownloadWorkerWithName(t *testing.T) { 302 setup(UsingAccount("foo")) 303 defer teardown() 304 305 mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) { 306 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 307 w.Header().Set("content-type", "application/javascript") 308 fmt.Fprintf(w, workerScript) //nolint 309 }) 310 res, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}) 311 want := WorkerScriptResponse{ 312 successResponse, 313 WorkerScript{ 314 Script: workerScript, 315 }} 316 if assert.NoError(t, err) { 317 assert.Equal(t, want.Script, res.Script) 318 } 319} 320 321func TestWorkers_DownloadWorkerWithNameErrorsWithoutAccountId(t *testing.T) { 322 setup() 323 defer teardown() 324 325 _, err := client.DownloadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}) 326 assert.Error(t, err) 327} 328 329func TestWorkers_ListWorkerScripts(t *testing.T) { 330 setup(UsingAccount("foo")) 331 defer teardown() 332 333 mux.HandleFunc("/accounts/foo/workers/scripts", func(w http.ResponseWriter, r *http.Request) { 334 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 335 w.Header().Set("content-type", "application-json") 336 fmt.Fprintf(w, listWorkersResponseData) //nolint 337 }) 338 339 res, err := client.ListWorkerScripts(context.Background()) 340 sampleDate, _ := time.Parse(time.RFC3339Nano, "2018-04-22T17:10:48.938097Z") 341 want := []WorkerMetaData{ 342 { 343 ID: "bar", 344 ETAG: "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 345 CreatedOn: sampleDate, 346 ModifiedOn: sampleDate, 347 }, 348 { 349 ID: "baz", 350 ETAG: "380dg51e97e80b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43088b", 351 CreatedOn: sampleDate, 352 ModifiedOn: sampleDate, 353 }, 354 } 355 if assert.NoError(t, err) { 356 assert.Equal(t, want, res.WorkerList) 357 } 358} 359 360func TestWorkers_UploadWorker(t *testing.T) { 361 setup() 362 defer teardown() 363 364 mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) { 365 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 366 contentTypeHeader := r.Header.Get("content-type") 367 assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader) 368 w.Header().Set("content-type", "application/json") 369 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 370 }) 371 res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, workerScript) 372 formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") 373 want := WorkerScriptResponse{ 374 successResponse, 375 WorkerScript{ 376 Script: workerScript, 377 WorkerMetaData: WorkerMetaData{ 378 ETAG: "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 379 Size: 191, 380 ModifiedOn: formattedTime, 381 }, 382 }} 383 if assert.NoError(t, err) { 384 assert.Equal(t, want, res) 385 } 386} 387 388func TestWorkers_UploadWorkerWithName(t *testing.T) { 389 setup(UsingAccount("foo")) 390 defer teardown() 391 392 mux.HandleFunc("/accounts/foo/workers/scripts/bar", func(w http.ResponseWriter, r *http.Request) { 393 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 394 contentTypeHeader := r.Header.Get("content-type") 395 assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader) 396 w.Header().Set("content-type", "application/json") 397 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 398 }) 399 res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, workerScript) 400 formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") 401 want := WorkerScriptResponse{ 402 successResponse, 403 WorkerScript{ 404 Script: workerScript, 405 WorkerMetaData: WorkerMetaData{ 406 ETAG: "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 407 Size: 191, 408 ModifiedOn: formattedTime, 409 }, 410 }} 411 if assert.NoError(t, err) { 412 assert.Equal(t, want, res) 413 } 414} 415 416func TestWorkers_UploadWorkerSingleScriptWithAccount(t *testing.T) { 417 setup(UsingAccount("foo")) 418 defer teardown() 419 420 mux.HandleFunc("/zones/foo/workers/script", func(w http.ResponseWriter, r *http.Request) { 421 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 422 contentTypeHeader := r.Header.Get("content-type") 423 assert.Equal(t, "application/javascript", contentTypeHeader, "Expected content-type request header to be 'application/javascript', got %s", contentTypeHeader) 424 w.Header().Set("content-type", "application/json") 425 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 426 }) 427 res, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, workerScript) 428 formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") 429 want := WorkerScriptResponse{ 430 successResponse, 431 WorkerScript{ 432 Script: workerScript, 433 WorkerMetaData: WorkerMetaData{ 434 ETAG: "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 435 Size: 191, 436 ModifiedOn: formattedTime, 437 }, 438 }} 439 if assert.NoError(t, err) { 440 assert.Equal(t, want, res) 441 } 442} 443 444func TestWorkers_UploadWorkerWithNameErrorsWithoutAccountId(t *testing.T) { 445 setup() 446 defer teardown() 447 448 _, err := client.UploadWorker(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, workerScript) 449 assert.Error(t, err) 450} 451 452func TestWorkers_UploadWorkerWithInheritBinding(t *testing.T) { 453 setup(UsingAccount("foo")) 454 defer teardown() 455 456 // Setup route handler for both single-script and multi-script 457 handler := func(w http.ResponseWriter, r *http.Request) { 458 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 459 460 mpUpload, err := parseMultipartUpload(r) 461 assert.NoError(t, err) 462 463 expectedBindings := map[string]workerBindingMeta{ 464 "b1": { 465 "name": "b1", 466 "type": "inherit", 467 }, 468 "b2": { 469 "name": "b2", 470 "type": "inherit", 471 "old_name": "old_binding_name", 472 }, 473 } 474 assert.Equal(t, workerScript, mpUpload.Script) 475 assert.Equal(t, expectedBindings, mpUpload.BindingMeta) 476 477 w.Header().Set("content-type", "application/json") 478 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 479 } 480 mux.HandleFunc("/zones/foo/workers/script", handler) 481 mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler) 482 483 scriptParams := WorkerScriptParams{ 484 Script: workerScript, 485 Bindings: map[string]WorkerBinding{ 486 "b1": WorkerInheritBinding{}, 487 "b2": WorkerInheritBinding{ 488 OldName: "old_binding_name", 489 }, 490 }, 491 } 492 493 // Expected response 494 formattedTime, _ := time.Parse(time.RFC3339Nano, "2018-06-09T15:17:01.989141Z") 495 want := WorkerScriptResponse{ 496 successResponse, 497 WorkerScript{ 498 Script: workerScript, 499 WorkerMetaData: WorkerMetaData{ 500 ETAG: "279cf40d86d70b82f6cd3ba90a646b3ad995912da446836d7371c21c6a43977a", 501 Size: 191, 502 ModifiedOn: formattedTime, 503 }, 504 }} 505 506 // Test single-script 507 res, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ZoneID: "foo"}, &scriptParams) 508 if assert.NoError(t, err) { 509 assert.Equal(t, want, res) 510 } 511 512 // Test multi-script 513 res, err = client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams) 514 if assert.NoError(t, err) { 515 assert.Equal(t, want, res) 516 } 517} 518 519func TestWorkers_UploadWorkerWithKVBinding(t *testing.T) { 520 setup(UsingAccount("foo")) 521 defer teardown() 522 523 handler := func(w http.ResponseWriter, r *http.Request) { 524 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 525 526 mpUpload, err := parseMultipartUpload(r) 527 assert.NoError(t, err) 528 529 expectedBindings := map[string]workerBindingMeta{ 530 "b1": { 531 "name": "b1", 532 "type": "kv_namespace", 533 "namespace_id": "test-namespace", 534 }, 535 } 536 assert.Equal(t, workerScript, mpUpload.Script) 537 assert.Equal(t, expectedBindings, mpUpload.BindingMeta) 538 539 w.Header().Set("content-type", "application/json") 540 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 541 } 542 mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler) 543 544 scriptParams := WorkerScriptParams{ 545 Script: workerScript, 546 Bindings: map[string]WorkerBinding{ 547 "b1": WorkerKvNamespaceBinding{ 548 NamespaceID: "test-namespace", 549 }, 550 }, 551 } 552 _, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams) 553 assert.NoError(t, err) 554} 555 556func TestWorkers_UploadWorkerWithWasmBinding(t *testing.T) { 557 setup(UsingAccount("foo")) 558 defer teardown() 559 560 handler := func(w http.ResponseWriter, r *http.Request) { 561 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 562 563 mpUpload, err := parseMultipartUpload(r) 564 assert.NoError(t, err) 565 566 partName := mpUpload.BindingMeta["b1"]["part"].(string) 567 expectedBindings := map[string]workerBindingMeta{ 568 "b1": { 569 "name": "b1", 570 "type": "wasm_module", 571 "part": partName, 572 }, 573 } 574 assert.Equal(t, workerScript, mpUpload.Script) 575 assert.Equal(t, expectedBindings, mpUpload.BindingMeta) 576 577 wasmContent, err := getFormValue(r, partName) 578 assert.NoError(t, err) 579 assert.Equal(t, []byte("fake-wasm"), wasmContent) 580 581 w.Header().Set("content-type", "application/json") 582 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 583 } 584 mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler) 585 586 scriptParams := WorkerScriptParams{ 587 Script: workerScript, 588 Bindings: map[string]WorkerBinding{ 589 "b1": WorkerWebAssemblyBinding{ 590 Module: strings.NewReader("fake-wasm"), 591 }, 592 }, 593 } 594 _, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams) 595 assert.NoError(t, err) 596} 597 598func TestWorkers_UploadWorkerWithPlainTextBinding(t *testing.T) { 599 setup(UsingAccount("foo")) 600 defer teardown() 601 602 handler := func(w http.ResponseWriter, r *http.Request) { 603 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 604 605 mpUpload, err := parseMultipartUpload(r) 606 assert.NoError(t, err) 607 608 expectedBindings := map[string]workerBindingMeta{ 609 "b1": { 610 "name": "b1", 611 "type": "plain_text", 612 "text": "plain text value", 613 }, 614 } 615 assert.Equal(t, workerScript, mpUpload.Script) 616 assert.Equal(t, expectedBindings, mpUpload.BindingMeta) 617 618 w.Header().Set("content-type", "application/json") 619 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 620 } 621 mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler) 622 623 scriptParams := WorkerScriptParams{ 624 Script: workerScript, 625 Bindings: map[string]WorkerBinding{ 626 "b1": WorkerPlainTextBinding{ 627 Text: "plain text value", 628 }, 629 }, 630 } 631 _, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams) 632 assert.NoError(t, err) 633} 634 635func TestWorkers_UploadWorkerWithSecretTextBinding(t *testing.T) { 636 setup(UsingAccount("foo")) 637 defer teardown() 638 639 handler := func(w http.ResponseWriter, r *http.Request) { 640 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 641 642 mpUpload, err := parseMultipartUpload(r) 643 assert.NoError(t, err) 644 645 expectedBindings := map[string]workerBindingMeta{ 646 "b1": { 647 "name": "b1", 648 "type": "secret_text", 649 "text": "secret text value", 650 }, 651 } 652 assert.Equal(t, workerScript, mpUpload.Script) 653 assert.Equal(t, expectedBindings, mpUpload.BindingMeta) 654 655 w.Header().Set("content-type", "application/json") 656 fmt.Fprintf(w, uploadWorkerResponseData) //nolint 657 } 658 mux.HandleFunc("/accounts/foo/workers/scripts/bar", handler) 659 660 scriptParams := WorkerScriptParams{ 661 Script: workerScript, 662 Bindings: map[string]WorkerBinding{ 663 "b1": WorkerSecretTextBinding{ 664 Text: "secret text value", 665 }, 666 }, 667 } 668 _, err := client.UploadWorkerWithBindings(context.Background(), &WorkerRequestParams{ScriptName: "bar"}, &scriptParams) 669 assert.NoError(t, err) 670} 671 672func TestWorkers_CreateWorkerRoute(t *testing.T) { 673 setup() 674 defer teardown() 675 676 mux.HandleFunc("/zones/foo/workers/filters", func(w http.ResponseWriter, r *http.Request) { 677 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 678 w.Header().Set("content-type", "application-json") 679 fmt.Fprintf(w, createWorkerRouteResponse) //nolint 680 }) 681 route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true} 682 res, err := client.CreateWorkerRoute(context.Background(), "foo", route) 683 want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} 684 if assert.NoError(t, err) { 685 assert.Equal(t, want, res) 686 } 687} 688 689func TestWorkers_CreateWorkerRouteEnt(t *testing.T) { 690 setup(UsingAccount("foo")) 691 defer teardown() 692 693 mux.HandleFunc("/zones/foo/workers/routes", func(w http.ResponseWriter, r *http.Request) { 694 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 695 w.Header().Set("content-type", "application-json") 696 fmt.Fprintf(w, createWorkerRouteResponse) //nolint 697 }) 698 route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script"} 699 res, err := client.CreateWorkerRoute(context.Background(), "foo", route) 700 want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} 701 if assert.NoError(t, err) { 702 assert.Equal(t, want, res) 703 } 704} 705 706func TestWorkers_CreateWorkerRouteSingleScriptWithAccount(t *testing.T) { 707 setup(UsingAccount("foo")) 708 defer teardown() 709 710 mux.HandleFunc("/zones/foo/workers/filters", func(w http.ResponseWriter, r *http.Request) { 711 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 712 w.Header().Set("content-type", "application-json") 713 fmt.Fprintf(w, createWorkerRouteResponse) //nolint 714 }) 715 route := WorkerRoute{Pattern: "app1.example.com/*", Enabled: true} 716 res, err := client.CreateWorkerRoute(context.Background(), "foo", route) 717 want := WorkerRouteResponse{successResponse, WorkerRoute{ID: "e7a57d8746e74ae49c25994dadb421b1"}} 718 if assert.NoError(t, err) { 719 assert.Equal(t, want, res) 720 } 721} 722 723func TestWorkers_CreateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) { 724 setup(UsingAccount("foo")) 725 defer teardown() 726 727 route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true} 728 _, err := client.CreateWorkerRoute(context.Background(), "foo", route) 729 assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both") 730} 731 732func TestWorkers_CreateWorkerRouteWithNoScript(t *testing.T) { 733 setup(UsingAccount("foo")) 734 735 mux.HandleFunc("/zones/foo/workers/routes", func(w http.ResponseWriter, r *http.Request) { 736 assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) 737 w.Header().Set("content-type", "application-json") 738 fmt.Fprintf(w, createWorkerRouteResponse) //nolint 739 }) 740 741 route := WorkerRoute{Pattern: "app1.example.com/*"} 742 _, err := client.CreateWorkerRoute(context.Background(), "foo", route) 743 assert.NoError(t, err) 744} 745 746func TestWorkers_DeleteWorkerRoute(t *testing.T) { 747 setup() 748 defer teardown() 749 750 mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 751 assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) 752 w.Header().Set("content-type", "application-json") 753 fmt.Fprintf(w, deleteWorkerRouteResponseData) //nolint 754 }) 755 res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1") 756 want := WorkerRouteResponse{successResponse, 757 WorkerRoute{ 758 ID: "e7a57d8746e74ae49c25994dadb421b1", 759 }} 760 if assert.NoError(t, err) { 761 assert.Equal(t, want, res) 762 } 763} 764 765func TestWorkers_DeleteWorkerRouteEnt(t *testing.T) { 766 setup(UsingAccount("foo")) 767 defer teardown() 768 769 mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 770 assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) 771 w.Header().Set("content-type", "application-json") 772 fmt.Fprintf(w, deleteWorkerRouteResponseData) //nolint 773 }) 774 res, err := client.DeleteWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1") 775 want := WorkerRouteResponse{successResponse, 776 WorkerRoute{ 777 ID: "e7a57d8746e74ae49c25994dadb421b1", 778 }} 779 if assert.NoError(t, err) { 780 assert.Equal(t, want, res) 781 } 782} 783 784func TestWorkers_ListWorkerRoutes(t *testing.T) { 785 setup() 786 defer teardown() 787 788 mux.HandleFunc("/zones/foo/workers/filters", func(w http.ResponseWriter, r *http.Request) { 789 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 790 w.Header().Set("content-type", "application-json") 791 fmt.Fprintf(w, listRouteResponseData) //nolint 792 }) 793 794 res, err := client.ListWorkerRoutes(context.Background(), "foo") 795 want := WorkerRoutesResponse{successResponse, 796 []WorkerRoute{ 797 {ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Enabled: true}, 798 {ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Enabled: false}, 799 }, 800 } 801 if assert.NoError(t, err) { 802 assert.Equal(t, want, res) 803 } 804} 805 806func TestWorkers_ListWorkerRoutesEnt(t *testing.T) { 807 setup(UsingAccount("foo")) 808 defer teardown() 809 810 mux.HandleFunc("/zones/foo/workers/routes", func(w http.ResponseWriter, r *http.Request) { 811 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 812 w.Header().Set("content-type", "application-json") 813 fmt.Fprintf(w, listRouteEntResponseData) //nolint 814 }) 815 816 res, err := client.ListWorkerRoutes(context.Background(), "foo") 817 want := WorkerRoutesResponse{successResponse, 818 []WorkerRoute{ 819 {ID: "e7a57d8746e74ae49c25994dadb421b1", Pattern: "app1.example.com/*", Script: "test_script_1", Enabled: true}, 820 {ID: "f8b68e9857f85bf59c25994dadb421b1", Pattern: "app2.example.com/*", Script: "test_script_2", Enabled: true}, 821 {ID: "2b5bf4240cd34c77852fac70b1bf745a", Pattern: "app3.example.com/*", Script: "", Enabled: false}, 822 }, 823 } 824 if assert.NoError(t, err) { 825 assert.Equal(t, want, res) 826 } 827} 828 829func TestWorkers_GetWorkerRoute(t *testing.T) { 830 setup() 831 defer teardown() 832 833 mux.HandleFunc("/zones/foo/workers/routes/1234", func(w http.ResponseWriter, r *http.Request) { 834 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 835 w.Header().Set("content-type", "application-json") 836 fmt.Fprintf(w, getRouteResponseData) //nolint 837 }) 838 839 res, err := client.GetWorkerRoute(context.Background(), "foo", "1234") 840 want := WorkerRouteResponse{successResponse, 841 WorkerRoute{ 842 ID: "e7a57d8746e74ae49c25994dadb421b1", 843 Pattern: "app1.example.com/*", 844 Script: "script-name"}, 845 } 846 if assert.NoError(t, err) { 847 assert.Equal(t, want, res) 848 } 849} 850 851func TestWorkers_UpdateWorkerRoute(t *testing.T) { 852 setup() 853 defer teardown() 854 855 mux.HandleFunc("/zones/foo/workers/filters/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 856 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 857 w.Header().Set("content-type", "application-json") 858 fmt.Fprintf(w, updateWorkerRouteResponse) //nolint 859 }) 860 route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true} 861 res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) 862 want := WorkerRouteResponse{successResponse, 863 WorkerRoute{ 864 ID: "e7a57d8746e74ae49c25994dadb421b1", 865 Pattern: "app3.example.com/*", 866 Enabled: true, 867 }} 868 if assert.NoError(t, err) { 869 assert.Equal(t, want, res) 870 } 871} 872 873func TestWorkers_UpdateWorkerRouteEnt(t *testing.T) { 874 setup(UsingAccount("foo")) 875 defer teardown() 876 877 mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 878 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 879 w.Header().Set("content-type", "application-json") 880 fmt.Fprintf(w, updateWorkerRouteEntResponse) //nolint 881 }) 882 route := WorkerRoute{Pattern: "app3.example.com/*", Script: "test_script_1"} 883 res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) 884 want := WorkerRouteResponse{successResponse, 885 WorkerRoute{ 886 ID: "e7a57d8746e74ae49c25994dadb421b1", 887 Pattern: "app3.example.com/*", 888 Script: "test_script_1", 889 }} 890 if assert.NoError(t, err) { 891 assert.Equal(t, want, res) 892 } 893} 894 895func TestWorkers_UpdateWorkerRouteSingleScriptWithAccount(t *testing.T) { 896 setup(UsingAccount("foo")) 897 defer teardown() 898 899 mux.HandleFunc("/zones/foo/workers/filters/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 900 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 901 w.Header().Set("content-type", "application-json") 902 fmt.Fprintf(w, updateWorkerRouteEntResponse) //nolint 903 }) 904 route := WorkerRoute{Pattern: "app3.example.com/*", Enabled: true} 905 res, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) 906 want := WorkerRouteResponse{successResponse, 907 WorkerRoute{ 908 ID: "e7a57d8746e74ae49c25994dadb421b1", 909 Pattern: "app3.example.com/*", 910 Script: "test_script_1", 911 }} 912 if assert.NoError(t, err) { 913 assert.Equal(t, want, res) 914 } 915} 916 917func TestWorkers_ListWorkerBindingsMultiScript(t *testing.T) { 918 setup(UsingAccount("foo")) 919 defer teardown() 920 921 mux.HandleFunc("/accounts/foo/workers/scripts/my-script/bindings", func(w http.ResponseWriter, r *http.Request) { 922 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 923 w.Header().Set("content-type", "application-json") 924 fmt.Fprintf(w, listBindingsResponseData) //nolint 925 }) 926 927 mux.HandleFunc("/accounts/foo/workers/scripts/my-script/bindings/MY_WASM/content", func(w http.ResponseWriter, r *http.Request) { 928 assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) 929 w.Header().Set("content-type", "application/wasm") 930 _, _ = w.Write([]byte("mock multi-script wasm")) 931 }) 932 933 res, err := client.ListWorkerBindings(context.Background(), &WorkerRequestParams{ 934 ScriptName: "my-script", 935 }) 936 assert.NoError(t, err) 937 938 assert.Equal(t, successResponse, res.Response) 939 assert.Equal(t, 5, len(res.BindingList)) 940 941 assert.Equal(t, res.BindingList[0], WorkerBindingListItem{ 942 Name: "MY_KV", 943 Binding: WorkerKvNamespaceBinding{ 944 NamespaceID: "89f5f8fd93f94cb98473f6f421aa3b65", 945 }, 946 }) 947 assert.Equal(t, WorkerKvNamespaceBindingType, res.BindingList[0].Binding.Type()) 948 949 assert.Equal(t, "MY_WASM", res.BindingList[1].Name) 950 wasmBinding := res.BindingList[1].Binding.(WorkerWebAssemblyBinding) 951 wasmModuleContent, err := ioutil.ReadAll(wasmBinding.Module) 952 assert.NoError(t, err) 953 assert.Equal(t, []byte("mock multi-script wasm"), wasmModuleContent) 954 assert.Equal(t, WorkerWebAssemblyBindingType, res.BindingList[1].Binding.Type()) 955 956 assert.Equal(t, res.BindingList[2], WorkerBindingListItem{ 957 Name: "MY_PLAIN_TEXT", 958 Binding: WorkerPlainTextBinding{ 959 Text: "text", 960 }, 961 }) 962 assert.Equal(t, WorkerPlainTextBindingType, res.BindingList[2].Binding.Type()) 963 964 assert.Equal(t, res.BindingList[3], WorkerBindingListItem{ 965 Name: "MY_SECRET_TEXT", 966 Binding: WorkerSecretTextBinding{}, 967 }) 968 assert.Equal(t, WorkerSecretTextBindingType, res.BindingList[3].Binding.Type()) 969 970 assert.Equal(t, res.BindingList[4], WorkerBindingListItem{ 971 Name: "MY_NEW_BINDING", 972 Binding: WorkerInheritBinding{}, 973 }) 974 assert.Equal(t, WorkerInheritBindingType, res.BindingList[4].Binding.Type()) 975} 976 977func TestWorkers_UpdateWorkerRouteErrorsWhenMixingSingleAndMultiScriptProperties(t *testing.T) { 978 setup(UsingAccount("foo")) 979 defer teardown() 980 981 route := WorkerRoute{Pattern: "app1.example.com/*", Script: "test_script", Enabled: true} 982 _, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) 983 assert.EqualError(t, err, "Only `Script` or `Enabled` may be specified for a WorkerRoute, not both") 984} 985 986func TestWorkers_UpdateWorkerRouteWithNoScript(t *testing.T) { 987 setup(UsingAccount("foo")) 988 989 mux.HandleFunc("/zones/foo/workers/routes/e7a57d8746e74ae49c25994dadb421b1", func(w http.ResponseWriter, r *http.Request) { 990 assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) 991 w.Header().Set("content-type", "application-json") 992 fmt.Fprintf(w, updateWorkerRouteEntResponse) //nolint 993 }) 994 995 route := WorkerRoute{Pattern: "app1.example.com/*"} 996 _, err := client.UpdateWorkerRoute(context.Background(), "foo", "e7a57d8746e74ae49c25994dadb421b1", route) 997 assert.NoError(t, err) 998} 999