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