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