@x47base/pocketbase-addon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +9 -0
- package/Dockerfile +26 -0
- package/FEATURES.md +32 -0
- package/LICENSE.md +17 -0
- package/MIGRATION.md +49 -0
- package/NOTICE.md +5 -0
- package/README.md +26 -0
- package/adapter.go +31 -0
- package/admin/register.go +60 -0
- package/admin/register_test.go +37 -0
- package/backups/backup_encryption_test.go +82 -0
- package/backups/encryption.go +138 -0
- package/backups/integration_test.go +51 -0
- package/backups/register.go +120 -0
- package/backups/restore.go +119 -0
- package/backups/s3_test.go +52 -0
- package/backups/swap.go +53 -0
- package/backups/swap_test.go +75 -0
- package/backups/upload.go +52 -0
- package/bin/pocketbase-extension.mjs +25 -0
- package/cmd/edge/main.go +123 -0
- package/cmd/import-fork/main.go +37 -0
- package/cmd/loadtest/main.go +61 -0
- package/cmd/loadtest/sandbox.go +73 -0
- package/cmd/loadtest/sandbox_test.go +20 -0
- package/cmd/pocketbase/main.go +78 -0
- package/deploy/README.md +148 -0
- package/deploy/app/hooks/README.md +2 -0
- package/deploy/app/migrations/1789000000_notes.js +16 -0
- package/deploy/app/public/README.md +2 -0
- package/deploy/compose.secrets.yaml +8 -0
- package/deploy/compose.yaml +68 -0
- package/deploy/edge.json +13 -0
- package/edge/gateway.go +295 -0
- package/edge/gateway_test.go +296 -0
- package/edge/openapi.json +1 -0
- package/edge/policy.go +150 -0
- package/features/collection_singleton.go +13 -0
- package/features/collection_singleton_test.go +46 -0
- package/features/dimensions_test.go +65 -0
- package/features/duplicate.go +178 -0
- package/features/duplicate_test.go +128 -0
- package/features/field_color.go +46 -0
- package/features/field_date_only.go +39 -0
- package/features/field_json_schema.go +92 -0
- package/features/field_scalar_extensions_test.go +66 -0
- package/features/files.go +36 -0
- package/features/filter_has_any_test.go +81 -0
- package/features/generate_test.go +72 -0
- package/features/has_any_visibility_test.go +62 -0
- package/features/json.go +50 -0
- package/features/membership.go +64 -0
- package/features/register.go +45 -0
- package/features/schema_test.go +58 -0
- package/features/ui/main.js +133 -0
- package/features/ui/settings.js +31 -0
- package/go.mod +54 -0
- package/go.sum +159 -0
- package/internal/archive/create.go +91 -0
- package/internal/archive/create_test.go +125 -0
- package/internal/archive/extract.go +99 -0
- package/internal/archive/extract_test.go +88 -0
- package/jsvm/binds.go +1273 -0
- package/jsvm/binds_app_reset_test.go +314 -0
- package/jsvm/binds_test.go +1870 -0
- package/jsvm/form_data.go +149 -0
- package/jsvm/form_data_test.go +225 -0
- package/jsvm/internal/types/generated/embed.go +6 -0
- package/jsvm/internal/types/generated/types.d.ts +24820 -0
- package/jsvm/internal/types/types.go +1408 -0
- package/jsvm/jsvm.go +587 -0
- package/jsvm/mapper.go +67 -0
- package/jsvm/mapper_test.go +42 -0
- package/jsvm/pool.go +73 -0
- package/jsvm/program_source_test.go +24 -0
- package/loadtest/loadtest.go +202 -0
- package/loadtest/loadtest_test.go +84 -0
- package/localization/README.md +23 -0
- package/localization/catalogue.json +483 -0
- package/localization/localization.go +94 -0
- package/localization/localization_test.go +21 -0
- package/mail/register.go +80 -0
- package/mail/register_test.go +49 -0
- package/mail/resolve.go +91 -0
- package/migration/import.go +96 -0
- package/migration/import_test.go +58 -0
- package/otp/otp.go +56 -0
- package/otp/otp_test.go +56 -0
- package/package.json +51 -0
- package/scripts/check-edge.py +42 -0
- package/scripts/check.sh +11 -0
- package/scripts/sync-jsvm-types.sh +10 -0
- package/security/README.md +94 -0
- package/security/assurance_test.go +149 -0
- package/security/compatibility_test.go +128 -0
- package/security/config.go +78 -0
- package/security/dashboard_test.go +103 -0
- package/security/management.go +169 -0
- package/security/openapi.json +508 -0
- package/security/review.go +34 -0
- package/security/security.go +503 -0
- package/security/security_test.go +146 -0
- package/security/state.go +116 -0
- package/security/ui/dashboard.css +4 -0
- package/security/ui/dashboard.js +83 -0
- package/security/ui/main.js +15 -0
- package/security/ui/model.js +32 -0
- package/security/ui/model.test.mjs +25 -0
- package/security/ui/registration.test.mjs +10 -0
- package/settings/env_test.go +41 -0
- package/settings/openapi.json +193 -0
- package/settings/settings.go +155 -0
- package/settings/settings_test.go +31 -0
- package/watcher/watcher.go +192 -0
- package/watcher/watcher_test.go +200 -0
- package/web/static.go +99 -0
- package/web/static_test.go +48 -0
|
@@ -0,0 +1,1870 @@
|
|
|
1
|
+
package jsvm
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json/v2"
|
|
6
|
+
"errors"
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"mime/multipart"
|
|
10
|
+
"net/http"
|
|
11
|
+
"net/http/httptest"
|
|
12
|
+
"os"
|
|
13
|
+
"path/filepath"
|
|
14
|
+
"strconv"
|
|
15
|
+
"strings"
|
|
16
|
+
"testing"
|
|
17
|
+
"time"
|
|
18
|
+
|
|
19
|
+
"github.com/dop251/goja"
|
|
20
|
+
validation "github.com/pocketbase/ozzo-validation/v4"
|
|
21
|
+
"github.com/pocketbase/pocketbase/apis"
|
|
22
|
+
"github.com/pocketbase/pocketbase/core"
|
|
23
|
+
"github.com/pocketbase/pocketbase/tests"
|
|
24
|
+
"github.com/pocketbase/pocketbase/tools/filesystem"
|
|
25
|
+
"github.com/pocketbase/pocketbase/tools/mailer"
|
|
26
|
+
"github.com/pocketbase/pocketbase/tools/router"
|
|
27
|
+
"github.com/pocketbase/pocketbase/tools/types"
|
|
28
|
+
"github.com/spf13/cast"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
func testBindsCount(vm *goja.Runtime, namespace string, count int, t *testing.T) {
|
|
32
|
+
v, err := vm.RunString(`Object.keys(` + namespace + `).length`)
|
|
33
|
+
if err != nil {
|
|
34
|
+
t.Fatal(err)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
total, _ := v.Export().(int64)
|
|
38
|
+
|
|
39
|
+
if int(total) != count {
|
|
40
|
+
t.Fatalf("Expected %d %s binds, got %d", count, namespace, total)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// note: this test is useful as a reminder to update the tests in case
|
|
45
|
+
// a new base binding is added.
|
|
46
|
+
func TestBindCoreCount(t *testing.T) {
|
|
47
|
+
vm := goja.New()
|
|
48
|
+
BindCore(vm)
|
|
49
|
+
|
|
50
|
+
testBindsCount(vm, "this", 41, t)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func TestBindCoreSleep(t *testing.T) {
|
|
54
|
+
vm := goja.New()
|
|
55
|
+
BindCore(vm)
|
|
56
|
+
vm.Set("reader", strings.NewReader("test"))
|
|
57
|
+
|
|
58
|
+
start := time.Now()
|
|
59
|
+
_, err := vm.RunString(`
|
|
60
|
+
sleep(100);
|
|
61
|
+
`)
|
|
62
|
+
if err != nil {
|
|
63
|
+
t.Fatal(err)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
lasted := time.Since(start).Milliseconds()
|
|
67
|
+
if lasted < 100 || lasted > 150 {
|
|
68
|
+
t.Fatalf("Expected to sleep for ~100ms, got %d", lasted)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func TestBindCoreReaderToString(t *testing.T) {
|
|
73
|
+
vm := goja.New()
|
|
74
|
+
BindCore(vm)
|
|
75
|
+
vm.Set("reader", strings.NewReader("test"))
|
|
76
|
+
|
|
77
|
+
_, err := vm.RunString(`
|
|
78
|
+
let result = readerToString(reader)
|
|
79
|
+
|
|
80
|
+
if (result != "test") {
|
|
81
|
+
throw new Error('Expected "test", got ' + result);
|
|
82
|
+
}
|
|
83
|
+
`)
|
|
84
|
+
if err != nil {
|
|
85
|
+
t.Fatal(err)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func TestBindCoreToString(t *testing.T) {
|
|
90
|
+
vm := goja.New()
|
|
91
|
+
BindCore(vm)
|
|
92
|
+
vm.Set("scenarios", []struct {
|
|
93
|
+
Name string
|
|
94
|
+
Value any
|
|
95
|
+
Expected string
|
|
96
|
+
}{
|
|
97
|
+
{"null", nil, ""},
|
|
98
|
+
{"string", "test", "test"},
|
|
99
|
+
{"number", -12.4, "-12.4"},
|
|
100
|
+
{"bool", true, "true"},
|
|
101
|
+
{"arr", []int{1, 2, 3}, `[1,2,3]`},
|
|
102
|
+
{"obj", map[string]any{"test": 123}, `{"test":123}`},
|
|
103
|
+
{"reader", strings.NewReader("test"), "test"},
|
|
104
|
+
{"struct", struct {
|
|
105
|
+
Name string
|
|
106
|
+
private string
|
|
107
|
+
}{Name: "123", private: "456"}, `{"Name":"123"}`},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
_, err := vm.RunString(`
|
|
111
|
+
for (let s of scenarios) {
|
|
112
|
+
let str = toString(s.value)
|
|
113
|
+
if (str != s.expected) {
|
|
114
|
+
throw new Error('[' + s.name + '] Expected string ' + s.expected + ', got ' + str);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
`)
|
|
118
|
+
if err != nil {
|
|
119
|
+
t.Fatal(err)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func TestBindCoreToBytes(t *testing.T) {
|
|
124
|
+
vm := goja.New()
|
|
125
|
+
BindCore(vm)
|
|
126
|
+
vm.Set("bytesEqual", bytes.Equal)
|
|
127
|
+
vm.Set("scenarios", []struct {
|
|
128
|
+
Name string
|
|
129
|
+
Value any
|
|
130
|
+
Expected []byte
|
|
131
|
+
}{
|
|
132
|
+
{"null", nil, []byte{}},
|
|
133
|
+
{"string", "test", []byte("test")},
|
|
134
|
+
{"number", -12.4, []byte("-12.4")},
|
|
135
|
+
{"bool", true, []byte("true")},
|
|
136
|
+
{"arr", []int{1, 2, 3}, []byte{1, 2, 3}},
|
|
137
|
+
{"jsonraw", types.JSONRaw{1, 2, 3}, []byte{1, 2, 3}},
|
|
138
|
+
{"reader", strings.NewReader("test"), []byte("test")},
|
|
139
|
+
{"obj", map[string]any{"test": 123}, []byte(`{"test":123}`)},
|
|
140
|
+
{"struct", struct {
|
|
141
|
+
Name string
|
|
142
|
+
private string
|
|
143
|
+
}{Name: "123", private: "456"}, []byte(`{"Name":"123"}`)},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
_, err := vm.RunString(`
|
|
147
|
+
for (let s of scenarios) {
|
|
148
|
+
let b = toBytes(s.value)
|
|
149
|
+
if (!Array.isArray(b)) {
|
|
150
|
+
throw new Error('[' + s.name + '] Expected toBytes to return an array');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!bytesEqual(b, s.expected)) {
|
|
154
|
+
throw new Error('[' + s.name + '] Expected bytes ' + s.expected + ', got ' + b);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`)
|
|
158
|
+
if err != nil {
|
|
159
|
+
t.Fatal(err)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func TestBindCoreUnmarshal(t *testing.T) {
|
|
164
|
+
vm := goja.New()
|
|
165
|
+
BindCore(vm)
|
|
166
|
+
vm.Set("data", &map[string]any{"a": 123})
|
|
167
|
+
|
|
168
|
+
_, err := vm.RunString(`
|
|
169
|
+
unmarshal({"b": 456}, data)
|
|
170
|
+
|
|
171
|
+
if (data.a != 123) {
|
|
172
|
+
throw new Error('Expected data.a 123, got ' + data.a);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (data.b != 456) {
|
|
176
|
+
throw new Error('Expected data.b 456, got ' + data.b);
|
|
177
|
+
}
|
|
178
|
+
`)
|
|
179
|
+
if err != nil {
|
|
180
|
+
t.Fatal(err)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
func TestBindCoreContext(t *testing.T) {
|
|
185
|
+
vm := goja.New()
|
|
186
|
+
BindCore(vm)
|
|
187
|
+
|
|
188
|
+
_, err := vm.RunString(`
|
|
189
|
+
const base = new Context(null, "a", 123);
|
|
190
|
+
const sub = new Context(base, "b", 456);
|
|
191
|
+
|
|
192
|
+
const scenarios = [
|
|
193
|
+
{key: "a", expected: 123},
|
|
194
|
+
{key: "b", expected: 456},
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
for (let s of scenarios) {
|
|
198
|
+
if (sub.value(s.key) != s.expected) {
|
|
199
|
+
throw new("Expected " +s.key + " value " + s.expected + ", got " + sub.value(s.key));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`)
|
|
203
|
+
if err != nil {
|
|
204
|
+
t.Fatal(err)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
func TestBindCoreCookie(t *testing.T) {
|
|
209
|
+
vm := goja.New()
|
|
210
|
+
BindCore(vm)
|
|
211
|
+
|
|
212
|
+
_, err := vm.RunString(`
|
|
213
|
+
const cookie = new Cookie({
|
|
214
|
+
name: "example_name",
|
|
215
|
+
value: "example_value",
|
|
216
|
+
path: "/example_path",
|
|
217
|
+
domain: "example.com",
|
|
218
|
+
maxAge: 10,
|
|
219
|
+
secure: true,
|
|
220
|
+
httpOnly: true,
|
|
221
|
+
sameSite: 3,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const result = cookie.string();
|
|
225
|
+
|
|
226
|
+
const expected = "example_name=example_value; Path=/example_path; Domain=example.com; Max-Age=10; HttpOnly; Secure; SameSite=Strict";
|
|
227
|
+
|
|
228
|
+
if (expected != result) {
|
|
229
|
+
throw new("Expected \n" + expected + "\ngot\n" + result);
|
|
230
|
+
}
|
|
231
|
+
`)
|
|
232
|
+
if err != nil {
|
|
233
|
+
t.Fatal(err)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
func TestBindCoreSubscriptionMessage(t *testing.T) {
|
|
238
|
+
vm := goja.New()
|
|
239
|
+
BindCore(vm)
|
|
240
|
+
vm.Set("bytesToString", func(b []byte) string {
|
|
241
|
+
return string(b)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
_, err := vm.RunString(`
|
|
245
|
+
const payload = {
|
|
246
|
+
name: "test",
|
|
247
|
+
data: '{"test":123}'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const result = new SubscriptionMessage(payload);
|
|
251
|
+
|
|
252
|
+
if (result.name != payload.name) {
|
|
253
|
+
throw new("Expected name " + payload.name + ", got " + result.name);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (bytesToString(result.data) != payload.data) {
|
|
257
|
+
throw new("Expected data '" + payload.data + "', got '" + bytesToString(result.data) + "'");
|
|
258
|
+
}
|
|
259
|
+
`)
|
|
260
|
+
if err != nil {
|
|
261
|
+
t.Fatal(err)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
func TestBindCoreRecord(t *testing.T) {
|
|
266
|
+
app, _ := tests.NewTestApp()
|
|
267
|
+
defer app.Cleanup()
|
|
268
|
+
|
|
269
|
+
collection, err := app.FindCachedCollectionByNameOrId("users")
|
|
270
|
+
if err != nil {
|
|
271
|
+
t.Fatal(err)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
vm := goja.New()
|
|
275
|
+
BindCore(vm)
|
|
276
|
+
vm.Set("collection", collection)
|
|
277
|
+
|
|
278
|
+
// without record data
|
|
279
|
+
// ---
|
|
280
|
+
v1, err := vm.RunString(`new Record(collection)`)
|
|
281
|
+
if err != nil {
|
|
282
|
+
t.Fatal(err)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
m1, ok := v1.Export().(*core.Record)
|
|
286
|
+
if !ok {
|
|
287
|
+
t.Fatalf("Expected m1 to be models.Record, got \n%v", m1)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// with record data
|
|
291
|
+
// ---
|
|
292
|
+
v2, err := vm.RunString(`new Record(collection, { email: "test@example.com" })`)
|
|
293
|
+
if err != nil {
|
|
294
|
+
t.Fatal(err)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
m2, ok := v2.Export().(*core.Record)
|
|
298
|
+
if !ok {
|
|
299
|
+
t.Fatalf("Expected m2 to be core.Record, got \n%v", m2)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if m2.Collection().Name != "users" {
|
|
303
|
+
t.Fatalf("Expected record with collection %q, got \n%v", "users", m2.Collection())
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if m2.Email() != "test@example.com" {
|
|
307
|
+
t.Fatalf("Expected record with email field set to %q, got \n%v", "test@example.com", m2)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func TestBindCoreCollection(t *testing.T) {
|
|
312
|
+
vm := goja.New()
|
|
313
|
+
BindCore(vm)
|
|
314
|
+
|
|
315
|
+
v, err := vm.RunString(`new Collection({ name: "test", createRule: "@request.auth.id != ''", fields: [{name: "title", "type": "text"}] })`)
|
|
316
|
+
if err != nil {
|
|
317
|
+
t.Fatal(err)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
m, ok := v.Export().(*core.Collection)
|
|
321
|
+
if !ok {
|
|
322
|
+
t.Fatalf("Expected core.Collection, got %v", m)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if m.Name != "test" {
|
|
326
|
+
t.Fatalf("Expected collection with name %q, got %q", "test", m.Name)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
expectedRule := "@request.auth.id != ''"
|
|
330
|
+
if m.CreateRule == nil || *m.CreateRule != expectedRule {
|
|
331
|
+
t.Fatalf("Expected create rule %q, got %v", "@request.auth.id != ''", m.CreateRule)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if f := m.Fields.GetByName("title"); f == nil {
|
|
335
|
+
t.Fatalf("Expected fields to be set, got %v", m.Fields)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
func TestBindCoreFieldsList(t *testing.T) {
|
|
340
|
+
vm := goja.New()
|
|
341
|
+
BindCore(vm)
|
|
342
|
+
|
|
343
|
+
v, err := vm.RunString(`new FieldsList([{name: "title", "type": "text"}])`)
|
|
344
|
+
if err != nil {
|
|
345
|
+
t.Fatal(err)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
m, ok := v.Export().(*core.FieldsList)
|
|
349
|
+
if !ok {
|
|
350
|
+
t.Fatalf("Expected core.FieldsList, got %v", m)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if f := m.GetByName("title"); f == nil {
|
|
354
|
+
t.Fatalf("Expected fields list to be loaded, got %v", m)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
func TestBindCoreField(t *testing.T) {
|
|
359
|
+
vm := goja.New()
|
|
360
|
+
BindCore(vm)
|
|
361
|
+
|
|
362
|
+
v, err := vm.RunString(`new Field({name: "test", "type": "bool"})`)
|
|
363
|
+
if err != nil {
|
|
364
|
+
t.Fatal(err)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
f, ok := v.Export().(*core.BoolField)
|
|
368
|
+
if !ok {
|
|
369
|
+
t.Fatalf("Expected *core.BoolField, got %v", f)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if f.Name != "test" {
|
|
373
|
+
t.Fatalf("Expected field %q, got %v", "test", f)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
func isType[T any](v any) bool {
|
|
378
|
+
_, ok := v.(T)
|
|
379
|
+
return ok
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
func TestBindCoreNamedFields(t *testing.T) {
|
|
383
|
+
t.Parallel()
|
|
384
|
+
|
|
385
|
+
vm := goja.New()
|
|
386
|
+
BindCore(vm)
|
|
387
|
+
|
|
388
|
+
scenarios := []struct {
|
|
389
|
+
js string
|
|
390
|
+
typeFunc func(v any) bool
|
|
391
|
+
}{
|
|
392
|
+
{
|
|
393
|
+
"new NumberField({name: 'test'})",
|
|
394
|
+
isType[*core.NumberField],
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
"new BoolField({name: 'test'})",
|
|
398
|
+
isType[*core.BoolField],
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"new TextField({name: 'test'})",
|
|
402
|
+
isType[*core.TextField],
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"new URLField({name: 'test'})",
|
|
406
|
+
isType[*core.URLField],
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"new EmailField({name: 'test'})",
|
|
410
|
+
isType[*core.EmailField],
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
"new EditorField({name: 'test'})",
|
|
414
|
+
isType[*core.EditorField],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"new PasswordField({name: 'test'})",
|
|
418
|
+
isType[*core.PasswordField],
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
"new DateField({name: 'test'})",
|
|
422
|
+
isType[*core.DateField],
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"new AutodateField({name: 'test'})",
|
|
426
|
+
isType[*core.AutodateField],
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
"new JSONField({name: 'test'})",
|
|
430
|
+
isType[*core.JSONField],
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
"new RelationField({name: 'test'})",
|
|
434
|
+
isType[*core.RelationField],
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"new SelectField({name: 'test'})",
|
|
438
|
+
isType[*core.SelectField],
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"new FileField({name: 'test'})",
|
|
442
|
+
isType[*core.FileField],
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"new GeoPointField({name: 'test'})",
|
|
446
|
+
isType[*core.GeoPointField],
|
|
447
|
+
},
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
for _, s := range scenarios {
|
|
451
|
+
t.Run(s.js, func(t *testing.T) {
|
|
452
|
+
v, err := vm.RunString(s.js)
|
|
453
|
+
if err != nil {
|
|
454
|
+
t.Fatal(err)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
f, ok := v.Export().(core.Field)
|
|
458
|
+
if !ok {
|
|
459
|
+
t.Fatalf("Expected core.Field instance, got %T (%v)", f, f)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if !s.typeFunc(f) {
|
|
463
|
+
t.Fatalf("Unexpected field type %T (%v)", f, f)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if f.GetName() != "test" {
|
|
467
|
+
t.Fatalf("Expected field %q, got %v", "test", f)
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
func TestBindCoreMailerMessage(t *testing.T) {
|
|
474
|
+
vm := goja.New()
|
|
475
|
+
BindCore(vm)
|
|
476
|
+
|
|
477
|
+
v, err := vm.RunString(`new MailerMessage({
|
|
478
|
+
from: {name: "test_from", address: "test_from@example.com"},
|
|
479
|
+
to: [
|
|
480
|
+
{name: "test_to1", address: "test_to1@example.com"},
|
|
481
|
+
{name: "test_to2", address: "test_to2@example.com"},
|
|
482
|
+
],
|
|
483
|
+
bcc: [
|
|
484
|
+
{name: "test_bcc1", address: "test_bcc1@example.com"},
|
|
485
|
+
{name: "test_bcc2", address: "test_bcc2@example.com"},
|
|
486
|
+
],
|
|
487
|
+
cc: [
|
|
488
|
+
{name: "test_cc1", address: "test_cc1@example.com"},
|
|
489
|
+
{name: "test_cc2", address: "test_cc2@example.com"},
|
|
490
|
+
],
|
|
491
|
+
subject: "test_subject",
|
|
492
|
+
html: "test_html",
|
|
493
|
+
text: "test_text",
|
|
494
|
+
headers: {
|
|
495
|
+
header1: "a",
|
|
496
|
+
header2: "b",
|
|
497
|
+
}
|
|
498
|
+
})`)
|
|
499
|
+
if err != nil {
|
|
500
|
+
t.Fatal(err)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
m, ok := v.Export().(*mailer.Message)
|
|
504
|
+
if !ok {
|
|
505
|
+
t.Fatalf("Expected mailer.Message, got %v", m)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
raw, err := json.Marshal(m, json.Deterministic(true))
|
|
509
|
+
if err != nil {
|
|
510
|
+
t.Fatal(err)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
expected := `{"from":{"Name":"test_from","Address":"test_from@example.com"},"to":[{"Name":"test_to1","Address":"test_to1@example.com"},{"Name":"test_to2","Address":"test_to2@example.com"}],"bcc":[{"Name":"test_bcc1","Address":"test_bcc1@example.com"},{"Name":"test_bcc2","Address":"test_bcc2@example.com"}],"cc":[{"Name":"test_cc1","Address":"test_cc1@example.com"},{"Name":"test_cc2","Address":"test_cc2@example.com"}],"subject":"test_subject","html":"test_html","text":"test_text","headers":{"header1":"a","header2":"b"},"attachments":{},"inlineAttachments":{}}`
|
|
514
|
+
|
|
515
|
+
if string(raw) != expected {
|
|
516
|
+
t.Fatalf("Expected \n%s, \ngot \n%s", expected, raw)
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
func TestBindCoreCommand(t *testing.T) {
|
|
521
|
+
vm := goja.New()
|
|
522
|
+
BindCore(vm)
|
|
523
|
+
|
|
524
|
+
_, err := vm.RunString(`
|
|
525
|
+
let runCalls = 0;
|
|
526
|
+
|
|
527
|
+
let cmd = new Command({
|
|
528
|
+
use: "test",
|
|
529
|
+
run: (c, args) => {
|
|
530
|
+
runCalls++;
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
cmd.run(null, []);
|
|
535
|
+
|
|
536
|
+
if (cmd.use != "test") {
|
|
537
|
+
throw new Error('Expected cmd.use "test", got: ' + cmd.use);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (runCalls != 1) {
|
|
541
|
+
throw new Error('Expected runCalls 1, got: ' + runCalls);
|
|
542
|
+
}
|
|
543
|
+
`)
|
|
544
|
+
if err != nil {
|
|
545
|
+
t.Fatal(err)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
func TestBindCoreRequestInfo(t *testing.T) {
|
|
550
|
+
vm := goja.New()
|
|
551
|
+
BindCore(vm)
|
|
552
|
+
|
|
553
|
+
_, err := vm.RunString(`
|
|
554
|
+
const info = new RequestInfo({
|
|
555
|
+
body: {"name": "test2"}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
if (info.body?.name != "test2") {
|
|
559
|
+
throw new Error('Expected info.body.name to be test2, got: ' + info.body?.name);
|
|
560
|
+
}
|
|
561
|
+
`)
|
|
562
|
+
if err != nil {
|
|
563
|
+
t.Fatal(err)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
func TestBindCoreMiddleware(t *testing.T) {
|
|
568
|
+
vm := goja.New()
|
|
569
|
+
BindCore(vm)
|
|
570
|
+
|
|
571
|
+
_, err := vm.RunString(`
|
|
572
|
+
const m = new Middleware(
|
|
573
|
+
(e) => {},
|
|
574
|
+
10,
|
|
575
|
+
"test"
|
|
576
|
+
);
|
|
577
|
+
|
|
578
|
+
if (!m) {
|
|
579
|
+
throw new Error('Expected non-empty Middleware instance');
|
|
580
|
+
}
|
|
581
|
+
`)
|
|
582
|
+
if err != nil {
|
|
583
|
+
t.Fatal(err)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
func TestBindCoreTimezone(t *testing.T) {
|
|
588
|
+
vm := goja.New()
|
|
589
|
+
BindCore(vm)
|
|
590
|
+
|
|
591
|
+
_, err := vm.RunString(`
|
|
592
|
+
const v0 = (new Timezone()).string();
|
|
593
|
+
if (v0 != "UTC") {
|
|
594
|
+
throw new Error("(v0) Expected UTC got " + v0)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const v1 = (new Timezone("invalid")).string();
|
|
598
|
+
if (v1 != "UTC") {
|
|
599
|
+
throw new Error("(v1) Expected UTC got " + v1)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const v2 = (new Timezone("EET")).string();
|
|
603
|
+
if (v2 != "EET") {
|
|
604
|
+
throw new Error("(v2) Expected EET got " + v2)
|
|
605
|
+
}
|
|
606
|
+
`)
|
|
607
|
+
if err != nil {
|
|
608
|
+
t.Fatal(err)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
func TestBindCoreDateTime(t *testing.T) {
|
|
613
|
+
vm := goja.New()
|
|
614
|
+
BindCore(vm)
|
|
615
|
+
|
|
616
|
+
_, err := vm.RunString(`
|
|
617
|
+
const now = new DateTime();
|
|
618
|
+
if (now.isZero()) {
|
|
619
|
+
throw new Error('(now) Expected to fallback to now, got zero value');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const nowPart = now.string().substring(0, 19)
|
|
623
|
+
|
|
624
|
+
const scenarios = [
|
|
625
|
+
// empty datetime string and no custom location
|
|
626
|
+
{date: new DateTime(''), expected: nowPart},
|
|
627
|
+
// empty datetime string and custom default location (should be ignored)
|
|
628
|
+
{date: new DateTime('', 'Asia/Tokyo'), expected: nowPart},
|
|
629
|
+
// full datetime string and no custom default location
|
|
630
|
+
{date: new DateTime('2023-01-01 00:00:00.000Z'), expected: "2023-01-01 00:00:00.000Z"},
|
|
631
|
+
// invalid location (fallback to UTC)
|
|
632
|
+
{date: new DateTime('2025-10-26 03:00:00', 'invalid'), expected: "2025-10-26 03:00:00.000Z"},
|
|
633
|
+
// CET
|
|
634
|
+
{date: new DateTime('2025-10-26 03:00:00', 'Europe/Amsterdam'), expected: "2025-10-26 02:00:00.000Z"},
|
|
635
|
+
// CEST
|
|
636
|
+
{date: new DateTime('2025-10-26 01:00:00', 'Europe/Amsterdam'), expected: "2025-10-25 23:00:00.000Z"},
|
|
637
|
+
// with timezone/offset in the date string (aka. should ignore the custom default location)
|
|
638
|
+
{date: new DateTime('2025-10-26 01:00:00 +0200', 'Asia/Tokyo'), expected: "2025-10-25 23:00:00.000Z"},
|
|
639
|
+
];
|
|
640
|
+
|
|
641
|
+
for (let i = 0; i < scenarios.length; i++) {
|
|
642
|
+
const s = scenarios[i];
|
|
643
|
+
if (!s.date.string().includes(s.expected)) {
|
|
644
|
+
throw new Error('(' + i + ') ' + s.date.string() + ' does not contain expected ' + s.expected);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
`)
|
|
648
|
+
if err != nil {
|
|
649
|
+
t.Fatal(err)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
func TestBindCoreValidationError(t *testing.T) {
|
|
654
|
+
vm := goja.New()
|
|
655
|
+
BindCore(vm)
|
|
656
|
+
|
|
657
|
+
scenarios := []struct {
|
|
658
|
+
js string
|
|
659
|
+
expectCode string
|
|
660
|
+
expectMessage string
|
|
661
|
+
}{
|
|
662
|
+
{
|
|
663
|
+
`new ValidationError()`,
|
|
664
|
+
"",
|
|
665
|
+
"",
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
`new ValidationError("test_code")`,
|
|
669
|
+
"test_code",
|
|
670
|
+
"",
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
`new ValidationError("test_code", "test_message")`,
|
|
674
|
+
"test_code",
|
|
675
|
+
"test_message",
|
|
676
|
+
},
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
for _, s := range scenarios {
|
|
680
|
+
v, err := vm.RunString(s.js)
|
|
681
|
+
if err != nil {
|
|
682
|
+
t.Fatal(err)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
m, ok := v.Export().(validation.Error)
|
|
686
|
+
if !ok {
|
|
687
|
+
t.Fatalf("[%s] Expected validation.Error, got %v", s.js, m)
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if m.Code() != s.expectCode {
|
|
691
|
+
t.Fatalf("[%s] Expected code %q, got %q", s.js, s.expectCode, m.Code())
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if m.Message() != s.expectMessage {
|
|
695
|
+
t.Fatalf("[%s] Expected message %q, got %q", s.js, s.expectMessage, m.Message())
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
func TestBindDbx(t *testing.T) {
|
|
701
|
+
app, _ := tests.NewTestApp()
|
|
702
|
+
defer app.Cleanup()
|
|
703
|
+
|
|
704
|
+
vm := goja.New()
|
|
705
|
+
vm.Set("db", app.DB())
|
|
706
|
+
BindCore(vm)
|
|
707
|
+
BindDbx(vm)
|
|
708
|
+
|
|
709
|
+
testBindsCount(vm, "$dbx", 15, t)
|
|
710
|
+
|
|
711
|
+
sceneraios := []struct {
|
|
712
|
+
js string
|
|
713
|
+
expected string
|
|
714
|
+
}{
|
|
715
|
+
{
|
|
716
|
+
`$dbx.exp("a = 1").build(db, {})`,
|
|
717
|
+
"a = 1",
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
`$dbx.hashExp({
|
|
721
|
+
"a": 1,
|
|
722
|
+
b: null,
|
|
723
|
+
c: [1, 2, 3],
|
|
724
|
+
}).build(db, {})`,
|
|
725
|
+
"`a`={:p0} AND `b` IS NULL AND `c` IN ({:p1}, {:p2}, {:p3})",
|
|
726
|
+
},
|
|
727
|
+
{
|
|
728
|
+
`$dbx.not($dbx.exp("a = 1")).build(db, {})`,
|
|
729
|
+
"NOT (a = 1)",
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
`$dbx.and($dbx.exp("a = 1"), $dbx.exp("b = 2")).build(db, {})`,
|
|
733
|
+
"(a = 1) AND (b = 2)",
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
`$dbx.or($dbx.exp("a = 1"), $dbx.exp("b = 2")).build(db, {})`,
|
|
737
|
+
"(a = 1) OR (b = 2)",
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
`$dbx.in("a", 1, 2, 3).build(db, {})`,
|
|
741
|
+
"`a` IN ({:p0}, {:p1}, {:p2})",
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
`$dbx.notIn("a", 1, 2, 3).build(db, {})`,
|
|
745
|
+
"`a` NOT IN ({:p0}, {:p1}, {:p2})",
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
`$dbx.like("a", "test1", "test2").match(true, false).build(db, {})`,
|
|
749
|
+
"`a` LIKE {:p0} AND `a` LIKE {:p1}",
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
`$dbx.orLike("a", "test1", "test2").match(false, true).build(db, {})`,
|
|
753
|
+
"`a` LIKE {:p0} OR `a` LIKE {:p1}",
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
`$dbx.notLike("a", "test1", "test2").match(true, false).build(db, {})`,
|
|
757
|
+
"`a` NOT LIKE {:p0} AND `a` NOT LIKE {:p1}",
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
`$dbx.orNotLike("a", "test1", "test2").match(false, false).build(db, {})`,
|
|
761
|
+
"`a` NOT LIKE {:p0} OR `a` NOT LIKE {:p1}",
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
`$dbx.exists($dbx.exp("a = 1")).build(db, {})`,
|
|
765
|
+
"EXISTS (a = 1)",
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
`$dbx.notExists($dbx.exp("a = 1")).build(db, {})`,
|
|
769
|
+
"NOT EXISTS (a = 1)",
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
`$dbx.between("a", 1, 2).build(db, {})`,
|
|
773
|
+
"`a` BETWEEN {:p0} AND {:p1}",
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
`$dbx.notBetween("a", 1, 2).build(db, {})`,
|
|
777
|
+
"`a` NOT BETWEEN {:p0} AND {:p1}",
|
|
778
|
+
},
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
for _, s := range sceneraios {
|
|
782
|
+
result, err := vm.RunString(s.js)
|
|
783
|
+
if err != nil {
|
|
784
|
+
t.Fatalf("[%s] Failed to execute js script, got %v", s.js, err)
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
v, _ := result.Export().(string)
|
|
788
|
+
|
|
789
|
+
if v != s.expected {
|
|
790
|
+
t.Fatalf("[%s] Expected \n%s, \ngot \n%s", s.js, s.expected, v)
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
func TestBindMailsCount(t *testing.T) {
|
|
796
|
+
vm := goja.New()
|
|
797
|
+
BindMails(vm)
|
|
798
|
+
|
|
799
|
+
testBindsCount(vm, "$mails", 5, t)
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
func TestBindMails(t *testing.T) {
|
|
803
|
+
app, _ := tests.NewTestApp()
|
|
804
|
+
defer app.Cleanup()
|
|
805
|
+
|
|
806
|
+
record, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
|
807
|
+
if err != nil {
|
|
808
|
+
t.Fatal(err)
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
vm := goja.New()
|
|
812
|
+
BindCore(vm)
|
|
813
|
+
BindMails(vm)
|
|
814
|
+
vm.Set("$app", app)
|
|
815
|
+
vm.Set("record", record)
|
|
816
|
+
|
|
817
|
+
_, vmErr := vm.RunString(`
|
|
818
|
+
$mails.sendRecordPasswordReset($app, record);
|
|
819
|
+
if (!$app.testMailer.lastMessage().html.includes("/_/#/auth/confirm-password-reset/")) {
|
|
820
|
+
throw new Error("Expected record password reset email, got:" + JSON.stringify($app.testMailer.lastMessage()))
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
$mails.sendRecordVerification($app, record);
|
|
824
|
+
if (!$app.testMailer.lastMessage().html.includes("/_/#/auth/confirm-verification/")) {
|
|
825
|
+
throw new Error("Expected record verification email, got:" + JSON.stringify($app.testMailer.lastMessage()))
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
$mails.sendRecordChangeEmail($app, record, "new@example.com");
|
|
829
|
+
if (!$app.testMailer.lastMessage().html.includes("/_/#/auth/confirm-email-change/")) {
|
|
830
|
+
throw new Error("Expected record email change email, got:" + JSON.stringify($app.testMailer.lastMessage()))
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
$mails.sendRecordOTP($app, record, "test_otp_id", "test_otp_pass");
|
|
834
|
+
if (!$app.testMailer.lastMessage().html.includes("test_otp_pass")) {
|
|
835
|
+
throw new Error("Expected record OTP email, got:" + JSON.stringify($app.testMailer.lastMessage()))
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
$mails.sendRecordAuthAlert($app, record, "test_alert_info");
|
|
839
|
+
if (!$app.testMailer.lastMessage().html.includes("test_alert_info")) {
|
|
840
|
+
throw new Error("Expected record OTP email, got:" + JSON.stringify($app.testMailer.lastMessage()))
|
|
841
|
+
}
|
|
842
|
+
`)
|
|
843
|
+
if vmErr != nil {
|
|
844
|
+
t.Fatal(vmErr)
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
func TestBindSecurityCount(t *testing.T) {
|
|
849
|
+
vm := goja.New()
|
|
850
|
+
BindSecurity(vm)
|
|
851
|
+
|
|
852
|
+
testBindsCount(vm, "$security", 16, t)
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
func TestSecurityCryptoBinds(t *testing.T) {
|
|
856
|
+
vm := goja.New()
|
|
857
|
+
BindCore(vm)
|
|
858
|
+
BindSecurity(vm)
|
|
859
|
+
|
|
860
|
+
sceneraios := []struct {
|
|
861
|
+
js string
|
|
862
|
+
expected string
|
|
863
|
+
}{
|
|
864
|
+
{`$security.md5("123")`, "202cb962ac59075b964b07152d234b70"},
|
|
865
|
+
{`$security.sha256("123")`, "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"},
|
|
866
|
+
{`$security.sha512("123")`, "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2"},
|
|
867
|
+
{`$security.hs256("hello", "test")`, "f151ea24bda91a18e89b8bb5793ef324b2a02133cce15a28a719acbd2e58a986"},
|
|
868
|
+
{`$security.hs512("hello", "test")`, "44f280e11103e295c26cd61dd1cdd8178b531b860466867c13b1c37a26b6389f8af110efbe0bb0717b9d9c87f6fe1c97b3b1690936578890e5669abf279fe7fd"},
|
|
869
|
+
{`$security.equal("abc", "abc")`, "true"},
|
|
870
|
+
{`$security.equal("abc", "abcd")`, "false"},
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
for _, s := range sceneraios {
|
|
874
|
+
t.Run(s.js, func(t *testing.T) {
|
|
875
|
+
result, err := vm.RunString(s.js)
|
|
876
|
+
if err != nil {
|
|
877
|
+
t.Fatalf("Failed to execute js script, got %v", err)
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
v := cast.ToString(result.Export())
|
|
881
|
+
|
|
882
|
+
if v != s.expected {
|
|
883
|
+
t.Fatalf("Expected %v \ngot \n%v", s.expected, v)
|
|
884
|
+
}
|
|
885
|
+
})
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
func TestSecurityRandomStringBinds(t *testing.T) {
|
|
890
|
+
vm := goja.New()
|
|
891
|
+
BindCore(vm)
|
|
892
|
+
BindSecurity(vm)
|
|
893
|
+
|
|
894
|
+
sceneraios := []struct {
|
|
895
|
+
js string
|
|
896
|
+
length int
|
|
897
|
+
}{
|
|
898
|
+
{`$security.randomString(6)`, 6},
|
|
899
|
+
{`$security.randomStringWithAlphabet(7, "abc")`, 7},
|
|
900
|
+
{`$security.pseudorandomString(8)`, 8},
|
|
901
|
+
{`$security.pseudorandomStringWithAlphabet(9, "abc")`, 9},
|
|
902
|
+
{`$security.randomStringByRegex("abc")`, 3},
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
for _, s := range sceneraios {
|
|
906
|
+
t.Run(s.js, func(t *testing.T) {
|
|
907
|
+
result, err := vm.RunString(s.js)
|
|
908
|
+
if err != nil {
|
|
909
|
+
t.Fatalf("Failed to execute js script, got %v", err)
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
v, _ := result.Export().(string)
|
|
913
|
+
|
|
914
|
+
if len(v) != s.length {
|
|
915
|
+
t.Fatalf("Expected %d length string, \ngot \n%v", s.length, v)
|
|
916
|
+
}
|
|
917
|
+
})
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
func TestSecurityJWTBinds(t *testing.T) {
|
|
922
|
+
sceneraios := []struct {
|
|
923
|
+
name string
|
|
924
|
+
js string
|
|
925
|
+
}{
|
|
926
|
+
{
|
|
927
|
+
"$security.parseUnverifiedJWT",
|
|
928
|
+
`
|
|
929
|
+
const result = $security.parseUnverifiedJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.aXzC7q7z1lX_hxk5P0R368xEU7H1xRwnBQQcLAmG0EY")
|
|
930
|
+
if (result.name != "John Doe") {
|
|
931
|
+
throw new Error("Expected result.name 'John Doe', got " + result.name)
|
|
932
|
+
}
|
|
933
|
+
if (result.sub != "1234567890") {
|
|
934
|
+
throw new Error("Expected result.sub '1234567890', got " + result.sub)
|
|
935
|
+
}
|
|
936
|
+
`,
|
|
937
|
+
},
|
|
938
|
+
{
|
|
939
|
+
"$security.parseJWT",
|
|
940
|
+
`
|
|
941
|
+
const result = $security.parseJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.aXzC7q7z1lX_hxk5P0R368xEU7H1xRwnBQQcLAmG0EY", "test")
|
|
942
|
+
if (result.name != "John Doe") {
|
|
943
|
+
throw new Error("Expected result.name 'John Doe', got " + result.name)
|
|
944
|
+
}
|
|
945
|
+
if (result.sub != "1234567890") {
|
|
946
|
+
throw new Error("Expected result.sub '1234567890', got " + result.sub)
|
|
947
|
+
}
|
|
948
|
+
`,
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
"$security.createJWT",
|
|
952
|
+
`
|
|
953
|
+
// overwrite the exp claim for static token
|
|
954
|
+
const result = $security.createJWT({"exp": 123}, "test", 0)
|
|
955
|
+
|
|
956
|
+
const expected = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyM30.7gbv7w672gApdBRASI6OniCtKwkKjhieSxsr6vxSrtw";
|
|
957
|
+
if (result != expected) {
|
|
958
|
+
throw new Error("Expected token \n" + expected + ", got \n" + result)
|
|
959
|
+
}
|
|
960
|
+
`,
|
|
961
|
+
},
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
for _, s := range sceneraios {
|
|
965
|
+
t.Run(s.name, func(t *testing.T) {
|
|
966
|
+
vm := goja.New()
|
|
967
|
+
BindCore(vm)
|
|
968
|
+
BindSecurity(vm)
|
|
969
|
+
|
|
970
|
+
_, err := vm.RunString(s.js)
|
|
971
|
+
if err != nil {
|
|
972
|
+
t.Fatalf("Failed to execute js script, got %v", err)
|
|
973
|
+
}
|
|
974
|
+
})
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
func TestSecurityEncryptAndDecryptBinds(t *testing.T) {
|
|
979
|
+
vm := goja.New()
|
|
980
|
+
BindCore(vm)
|
|
981
|
+
BindSecurity(vm)
|
|
982
|
+
|
|
983
|
+
_, err := vm.RunString(`
|
|
984
|
+
const key = "abcdabcdabcdabcdabcdabcdabcdabcd"
|
|
985
|
+
|
|
986
|
+
const encrypted = $security.encrypt("123", key)
|
|
987
|
+
|
|
988
|
+
const decrypted = $security.decrypt(encrypted, key)
|
|
989
|
+
|
|
990
|
+
if (decrypted != "123") {
|
|
991
|
+
throw new Error("Expected decrypted '123', got " + decrypted)
|
|
992
|
+
}
|
|
993
|
+
`)
|
|
994
|
+
if err != nil {
|
|
995
|
+
t.Fatal(err)
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
func TestBindFilesystem(t *testing.T) {
|
|
1000
|
+
app, _ := tests.NewTestApp()
|
|
1001
|
+
defer app.Cleanup()
|
|
1002
|
+
|
|
1003
|
+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
1004
|
+
if r.URL.Path == "/error" {
|
|
1005
|
+
w.WriteHeader(http.StatusInternalServerError)
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
fmt.Fprintf(w, "test")
|
|
1009
|
+
}))
|
|
1010
|
+
defer srv.Close()
|
|
1011
|
+
|
|
1012
|
+
tmpDir, err := os.MkdirTemp("", "jsvm")
|
|
1013
|
+
if err != nil {
|
|
1014
|
+
t.Fatal(err)
|
|
1015
|
+
}
|
|
1016
|
+
defer os.RemoveAll(tmpDir)
|
|
1017
|
+
|
|
1018
|
+
vm := goja.New()
|
|
1019
|
+
vm.Set("mh", &multipart.FileHeader{Filename: "test"})
|
|
1020
|
+
vm.Set("tmpDir", tmpDir)
|
|
1021
|
+
vm.Set("testFile", filepath.Join(app.DataDir(), "data.db"))
|
|
1022
|
+
vm.Set("baseURL", srv.URL)
|
|
1023
|
+
BindCore(vm)
|
|
1024
|
+
BindFilesystem(vm)
|
|
1025
|
+
|
|
1026
|
+
testBindsCount(vm, "$filesystem", 6, t)
|
|
1027
|
+
|
|
1028
|
+
// s3
|
|
1029
|
+
{
|
|
1030
|
+
v, err := vm.RunString(`$filesystem.s3("bucketName", "region", "endpoint", "accessKey", "secretKey", true)`)
|
|
1031
|
+
if err != nil {
|
|
1032
|
+
t.Fatal(err)
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
fsys, ok := v.Export().(*filesystem.System)
|
|
1036
|
+
if !ok {
|
|
1037
|
+
t.Fatalf("[s3] Expected System instance got %v", fsys)
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// local
|
|
1042
|
+
{
|
|
1043
|
+
v, err := vm.RunString(`$filesystem.local(tmpDir)`)
|
|
1044
|
+
if err != nil {
|
|
1045
|
+
t.Fatal(err)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
fsys, ok := v.Export().(*filesystem.System)
|
|
1049
|
+
if !ok {
|
|
1050
|
+
t.Fatalf("[s3] Expected System instance got %v", fsys)
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// fileFromPath
|
|
1055
|
+
{
|
|
1056
|
+
v, err := vm.RunString(`$filesystem.fileFromPath(testFile)`)
|
|
1057
|
+
if err != nil {
|
|
1058
|
+
t.Fatal(err)
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
file, _ := v.Export().(*filesystem.File)
|
|
1062
|
+
|
|
1063
|
+
if file == nil || file.OriginalName != "data.db" {
|
|
1064
|
+
t.Fatalf("[fileFromPath] Expected file with name %q, got %v", file.OriginalName, file)
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// fileFromBytes
|
|
1069
|
+
{
|
|
1070
|
+
v, err := vm.RunString(`$filesystem.fileFromBytes([1, 2, 3], "test")`)
|
|
1071
|
+
if err != nil {
|
|
1072
|
+
t.Fatal(err)
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
file, _ := v.Export().(*filesystem.File)
|
|
1076
|
+
|
|
1077
|
+
if file == nil || file.OriginalName != "test" {
|
|
1078
|
+
t.Fatalf("[fileFromBytes] Expected file with name %q, got %v", file.OriginalName, file)
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// fileFromMultipart
|
|
1083
|
+
{
|
|
1084
|
+
v, err := vm.RunString(`$filesystem.fileFromMultipart(mh)`)
|
|
1085
|
+
if err != nil {
|
|
1086
|
+
t.Fatal(err)
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
file, _ := v.Export().(*filesystem.File)
|
|
1090
|
+
|
|
1091
|
+
if file == nil || file.OriginalName != "test" {
|
|
1092
|
+
t.Fatalf("[fileFromMultipart] Expected file with name %q, got %v", file.OriginalName, file)
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// fileFromURL (success)
|
|
1097
|
+
{
|
|
1098
|
+
v, err := vm.RunString(`$filesystem.fileFromURL(baseURL + "/test")`)
|
|
1099
|
+
if err != nil {
|
|
1100
|
+
t.Fatal(err)
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
file, _ := v.Export().(*filesystem.File)
|
|
1104
|
+
|
|
1105
|
+
if file == nil || file.OriginalName != "test" {
|
|
1106
|
+
t.Fatalf("[fileFromURL] Expected file with name %q, got %v", file.OriginalName, file)
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// fileFromURL (failure)
|
|
1111
|
+
{
|
|
1112
|
+
_, err := vm.RunString(`$filesystem.fileFromURL(baseURL + "/error")`)
|
|
1113
|
+
if err == nil {
|
|
1114
|
+
t.Fatal("Expected url fetch error")
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
func TestBindForms(t *testing.T) {
|
|
1120
|
+
vm := goja.New()
|
|
1121
|
+
BindForms(vm)
|
|
1122
|
+
|
|
1123
|
+
testBindsCount(vm, "this", 4, t)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
func TestBindApisCount(t *testing.T) {
|
|
1127
|
+
vm := goja.New()
|
|
1128
|
+
BindApis(vm)
|
|
1129
|
+
|
|
1130
|
+
testBindsCount(vm, "this", 8, t)
|
|
1131
|
+
testBindsCount(vm, "$apis", 11, t)
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
func TestBindApisErrors(t *testing.T) {
|
|
1135
|
+
vm := goja.New()
|
|
1136
|
+
BindApis(vm)
|
|
1137
|
+
|
|
1138
|
+
scenarios := []struct {
|
|
1139
|
+
js string
|
|
1140
|
+
expectStatus int
|
|
1141
|
+
expectMessage string
|
|
1142
|
+
expectData string
|
|
1143
|
+
}{
|
|
1144
|
+
{"new ApiError()", 0, "", "null"},
|
|
1145
|
+
{"new ApiError(100, 'test', {'test': 1})", 100, "Test.", `{"test":1}`},
|
|
1146
|
+
{"new NotFoundError()", 404, "The requested resource wasn't found.", "null"},
|
|
1147
|
+
{"new NotFoundError('test', {'test': 1})", 404, "Test.", `{"test":1}`},
|
|
1148
|
+
{"new BadRequestError()", 400, "Something went wrong while processing your request.", "null"},
|
|
1149
|
+
{"new BadRequestError('test', {'test': 1})", 400, "Test.", `{"test":1}`},
|
|
1150
|
+
{"new ForbiddenError()", 403, "You are not allowed to perform this request.", "null"},
|
|
1151
|
+
{"new ForbiddenError('test', {'test': 1})", 403, "Test.", `{"test":1}`},
|
|
1152
|
+
{"new UnauthorizedError()", 401, "Missing or invalid authentication.", "null"},
|
|
1153
|
+
{"new UnauthorizedError('test', {'test': 1})", 401, "Test.", `{"test":1}`},
|
|
1154
|
+
{"new TooManyRequestsError()", 429, "Too Many Requests.", "null"},
|
|
1155
|
+
{"new TooManyRequestsError('test', {'test': 1})", 429, "Test.", `{"test":1}`},
|
|
1156
|
+
{"new InternalServerError()", 500, "Something went wrong while processing your request.", "null"},
|
|
1157
|
+
{"new InternalServerError('test', {'test': 1})", 500, "Test.", `{"test":1}`},
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
for _, s := range scenarios {
|
|
1161
|
+
v, err := vm.RunString(s.js)
|
|
1162
|
+
if err != nil {
|
|
1163
|
+
t.Errorf("[%s] %v", s.js, err)
|
|
1164
|
+
continue
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
apiErr, ok := v.Export().(*router.ApiError)
|
|
1168
|
+
if !ok {
|
|
1169
|
+
t.Errorf("[%s] Expected ApiError, got %v", s.js, v)
|
|
1170
|
+
continue
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if apiErr.Status != s.expectStatus {
|
|
1174
|
+
t.Errorf("[%s] Expected Status %d, got %d", s.js, s.expectStatus, apiErr.Status)
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if apiErr.Message != s.expectMessage {
|
|
1178
|
+
t.Errorf("[%s] Expected Message %q, got %q", s.js, s.expectMessage, apiErr.Message)
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
dataRaw, _ := json.Marshal(apiErr.RawData(), json.Deterministic(true))
|
|
1182
|
+
if string(dataRaw) != s.expectData {
|
|
1183
|
+
t.Errorf("[%s] Expected Data %q, got %q", s.js, s.expectData, dataRaw)
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
func TestLoadingDynamicModel(t *testing.T) {
|
|
1189
|
+
app, _ := tests.NewTestApp()
|
|
1190
|
+
defer app.Cleanup()
|
|
1191
|
+
|
|
1192
|
+
vm := goja.New()
|
|
1193
|
+
BindCore(vm)
|
|
1194
|
+
BindDbx(vm)
|
|
1195
|
+
vm.Set("$app", app)
|
|
1196
|
+
|
|
1197
|
+
_, err := vm.RunString(`
|
|
1198
|
+
let result = new DynamicModel({
|
|
1199
|
+
string: "",
|
|
1200
|
+
nullString: nullString(),
|
|
1201
|
+
nullStringEmpty: nullString(),
|
|
1202
|
+
|
|
1203
|
+
bool: false,
|
|
1204
|
+
nullBool: nullBool(),
|
|
1205
|
+
nullBoolEmpty: nullBool(),
|
|
1206
|
+
|
|
1207
|
+
int: 0,
|
|
1208
|
+
nullInt: nullInt(),
|
|
1209
|
+
nullIntEmpty: nullInt(),
|
|
1210
|
+
|
|
1211
|
+
float: -0,
|
|
1212
|
+
nullFloat: nullFloat(),
|
|
1213
|
+
nullFloatEmpty: nullFloat(),
|
|
1214
|
+
|
|
1215
|
+
array: [],
|
|
1216
|
+
nullArray: nullArray(),
|
|
1217
|
+
nullArrayEmpty: nullArray(),
|
|
1218
|
+
|
|
1219
|
+
object: {},
|
|
1220
|
+
nullObject: nullObject(),
|
|
1221
|
+
nullObjectEmpty: nullObject(),
|
|
1222
|
+
})
|
|
1223
|
+
|
|
1224
|
+
const expectations = {
|
|
1225
|
+
"string": "a",
|
|
1226
|
+
"nullString": "b",
|
|
1227
|
+
"nullStringEmpty": null,
|
|
1228
|
+
|
|
1229
|
+
"bool": false,
|
|
1230
|
+
"nullBool": true,
|
|
1231
|
+
"nullBoolEmpty": null,
|
|
1232
|
+
|
|
1233
|
+
"int": 1,
|
|
1234
|
+
"nullInt": 2,
|
|
1235
|
+
"nullIntEmpty": null,
|
|
1236
|
+
|
|
1237
|
+
"float": 1.1,
|
|
1238
|
+
"nullFloat": 1.2,
|
|
1239
|
+
"nullFloatEmpty": null,
|
|
1240
|
+
|
|
1241
|
+
"array": [1,2],
|
|
1242
|
+
"nullArray": [3,4],
|
|
1243
|
+
"nullArrayEmpty": null,
|
|
1244
|
+
|
|
1245
|
+
"object": {a:1},
|
|
1246
|
+
"nullObject": {a:2},
|
|
1247
|
+
"nullObjectEmpty": null,
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
// construct dummy SELECT column value literals based on the expectations
|
|
1251
|
+
const selectColumns = [];
|
|
1252
|
+
for (const col in expectations) {
|
|
1253
|
+
const val = expectations[col]
|
|
1254
|
+
|
|
1255
|
+
if (val === null) {
|
|
1256
|
+
selectColumns.push("null as [[" + col + "]]")
|
|
1257
|
+
} else if (typeof val === "string") {
|
|
1258
|
+
selectColumns.push("'" + val + "' as [[" + col + "]]")
|
|
1259
|
+
} else if (typeof val === "object") {
|
|
1260
|
+
selectColumns.push("'" + JSON.stringify(val) + "' as [[" + col + "]]")
|
|
1261
|
+
} else {
|
|
1262
|
+
selectColumns.push(val + " as [[" + col + "]]")
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
$app.db()
|
|
1267
|
+
.newQuery("SELECT " + selectColumns.join(", "))
|
|
1268
|
+
.one(result)
|
|
1269
|
+
|
|
1270
|
+
for (const col in expectations) {
|
|
1271
|
+
let expVal = expectations[col];
|
|
1272
|
+
let resVal = result[col];
|
|
1273
|
+
|
|
1274
|
+
if (expVal !== null && typeof expVal === "object") {
|
|
1275
|
+
expVal = JSON.stringify(expVal)
|
|
1276
|
+
resVal = JSON.stringify(resVal)
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (expVal != resVal) {
|
|
1280
|
+
throw new Error("Expected '" + col + "' value " + expVal + ", got " + resVal);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
`)
|
|
1284
|
+
if err != nil {
|
|
1285
|
+
t.Fatal(err)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
func TestDynamicModelMapFieldCaching(t *testing.T) {
|
|
1290
|
+
app, _ := tests.NewTestApp()
|
|
1291
|
+
defer app.Cleanup()
|
|
1292
|
+
|
|
1293
|
+
vm := goja.New()
|
|
1294
|
+
BindCore(vm)
|
|
1295
|
+
BindDbx(vm)
|
|
1296
|
+
vm.Set("$app", app)
|
|
1297
|
+
|
|
1298
|
+
_, err := vm.RunString(`
|
|
1299
|
+
let m1 = new DynamicModel({
|
|
1300
|
+
int: 0,
|
|
1301
|
+
float: -0,
|
|
1302
|
+
text: "",
|
|
1303
|
+
bool: false,
|
|
1304
|
+
obj: {},
|
|
1305
|
+
arr: [],
|
|
1306
|
+
})
|
|
1307
|
+
|
|
1308
|
+
let m2 = new DynamicModel({
|
|
1309
|
+
int: 0,
|
|
1310
|
+
float: -0,
|
|
1311
|
+
text: "",
|
|
1312
|
+
bool: false,
|
|
1313
|
+
obj: {},
|
|
1314
|
+
arr: [],
|
|
1315
|
+
})
|
|
1316
|
+
|
|
1317
|
+
m1.int = 1
|
|
1318
|
+
m1.float = 1.5
|
|
1319
|
+
m1.text = "a"
|
|
1320
|
+
m1.bool = true
|
|
1321
|
+
m1.obj.set("a", 1)
|
|
1322
|
+
m1.arr.push(1)
|
|
1323
|
+
|
|
1324
|
+
m2.int = 2
|
|
1325
|
+
m2.float = 2.5
|
|
1326
|
+
m2.text = "b"
|
|
1327
|
+
m2.bool = false
|
|
1328
|
+
m2.obj.set("b", 1)
|
|
1329
|
+
m2.arr.push(2)
|
|
1330
|
+
|
|
1331
|
+
let m1Expected = '{"arr":[1],"bool":true,"float":1.5,"int":1,"obj":{"a":1},"text":"a"}';
|
|
1332
|
+
let m1Serialized = JSON.stringify(m1);
|
|
1333
|
+
if (m1Serialized != m1Expected) {
|
|
1334
|
+
throw new Error("Expected m1 \n" + m1Expected + "\ngot\n" + m1Serialized);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
let m2Expected = '{"arr":[2],"bool":false,"float":2.5,"int":2,"obj":{"b":1},"text":"b"}';
|
|
1338
|
+
let m2Serialized = JSON.stringify(m2);
|
|
1339
|
+
if (m2Serialized != m2Expected) {
|
|
1340
|
+
throw new Error("Expected m2 \n" + m2Expected + "\ngot\n" + m2Serialized);
|
|
1341
|
+
}
|
|
1342
|
+
`)
|
|
1343
|
+
if err != nil {
|
|
1344
|
+
t.Fatal(err)
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
func TestLoadingArrayOf(t *testing.T) {
|
|
1349
|
+
app, _ := tests.NewTestApp()
|
|
1350
|
+
defer app.Cleanup()
|
|
1351
|
+
|
|
1352
|
+
vm := goja.New()
|
|
1353
|
+
BindCore(vm)
|
|
1354
|
+
BindDbx(vm)
|
|
1355
|
+
vm.Set("$app", app)
|
|
1356
|
+
|
|
1357
|
+
_, err := vm.RunString(`
|
|
1358
|
+
let result = arrayOf(new DynamicModel({
|
|
1359
|
+
id: "",
|
|
1360
|
+
text: "",
|
|
1361
|
+
}))
|
|
1362
|
+
|
|
1363
|
+
$app.db()
|
|
1364
|
+
.select("id", "text")
|
|
1365
|
+
.from("demo1")
|
|
1366
|
+
.where($dbx.exp("id='84nmscqy84lsi1t' OR id='al1h9ijdeojtsjy'"))
|
|
1367
|
+
.limit(2)
|
|
1368
|
+
.orderBy("text ASC")
|
|
1369
|
+
.all(result)
|
|
1370
|
+
|
|
1371
|
+
if (result.length != 2) {
|
|
1372
|
+
throw new Error('Expected 2 list items, got ' + result.length);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
if (result[0].id != "84nmscqy84lsi1t") {
|
|
1376
|
+
throw new Error('Expected 0.id "84nmscqy84lsi1t", got ' + result[0].id);
|
|
1377
|
+
}
|
|
1378
|
+
if (result[0].text != "test") {
|
|
1379
|
+
throw new Error('Expected 0.text "test", got ' + result[0].text);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
if (result[1].id != "al1h9ijdeojtsjy") {
|
|
1383
|
+
throw new Error('Expected 1.id "al1h9ijdeojtsjy", got ' + result[1].id);
|
|
1384
|
+
}
|
|
1385
|
+
if (result[1].text != "test2") {
|
|
1386
|
+
throw new Error('Expected 1.text "test2", got ' + result[1].text);
|
|
1387
|
+
}
|
|
1388
|
+
`)
|
|
1389
|
+
if err != nil {
|
|
1390
|
+
t.Fatal(err)
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
func TestBindHTTPCount(t *testing.T) {
|
|
1395
|
+
app, _ := tests.NewTestApp()
|
|
1396
|
+
defer app.Cleanup()
|
|
1397
|
+
|
|
1398
|
+
vm := goja.New()
|
|
1399
|
+
BindHTTP(vm)
|
|
1400
|
+
|
|
1401
|
+
testBindsCount(vm, "this", 2, t) // + FormData
|
|
1402
|
+
testBindsCount(vm, "$http", 1, t)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
func TestBindHTTPSend(t *testing.T) {
|
|
1406
|
+
t.Parallel()
|
|
1407
|
+
|
|
1408
|
+
// start a test server
|
|
1409
|
+
server := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
|
1410
|
+
if req.URL.Query().Get("testError") != "" {
|
|
1411
|
+
res.WriteHeader(400)
|
|
1412
|
+
return
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
timeoutStr := req.URL.Query().Get("testTimeout")
|
|
1416
|
+
timeout, _ := strconv.Atoi(timeoutStr)
|
|
1417
|
+
if timeout > 0 {
|
|
1418
|
+
time.Sleep(time.Duration(timeout) * time.Second)
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
bodyRaw, _ := io.ReadAll(req.Body)
|
|
1422
|
+
defer req.Body.Close()
|
|
1423
|
+
|
|
1424
|
+
// normalize headers
|
|
1425
|
+
headers := make(map[string]string, len(req.Header))
|
|
1426
|
+
for k, v := range req.Header {
|
|
1427
|
+
if len(v) > 0 {
|
|
1428
|
+
headers[strings.ToLower(strings.ReplaceAll(k, "-", "_"))] = v[0]
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
info := map[string]any{
|
|
1433
|
+
"method": req.Method,
|
|
1434
|
+
"headers": headers,
|
|
1435
|
+
"body": string(bodyRaw),
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// add custom headers and cookies
|
|
1439
|
+
res.Header().Add("X-Custom", "custom_header")
|
|
1440
|
+
res.Header().Add("Set-Cookie", "sessionId=123456")
|
|
1441
|
+
|
|
1442
|
+
infoRaw, _ := json.Marshal(info, json.Deterministic(true))
|
|
1443
|
+
|
|
1444
|
+
// write back the submitted request
|
|
1445
|
+
res.Write(infoRaw)
|
|
1446
|
+
}))
|
|
1447
|
+
defer server.Close()
|
|
1448
|
+
|
|
1449
|
+
vm := goja.New()
|
|
1450
|
+
BindCore(vm)
|
|
1451
|
+
BindHTTP(vm)
|
|
1452
|
+
vm.Set("testURL", server.URL)
|
|
1453
|
+
|
|
1454
|
+
_, err := vm.RunString(`
|
|
1455
|
+
function getNestedVal(data, path) {
|
|
1456
|
+
let result = data || {};
|
|
1457
|
+
let parts = path.split(".");
|
|
1458
|
+
|
|
1459
|
+
for (const part of parts) {
|
|
1460
|
+
if (
|
|
1461
|
+
result == null ||
|
|
1462
|
+
typeof result !== "object" ||
|
|
1463
|
+
typeof result[part] === "undefined"
|
|
1464
|
+
) {
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
result = result[part];
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
return result;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
let testTimeout;
|
|
1475
|
+
try {
|
|
1476
|
+
$http.send({
|
|
1477
|
+
url: testURL + "?testTimeout=3",
|
|
1478
|
+
timeout: 1
|
|
1479
|
+
})
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
testTimeout = err
|
|
1482
|
+
}
|
|
1483
|
+
if (!testTimeout) {
|
|
1484
|
+
throw new Error("Expected timeout error")
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
// error response check
|
|
1488
|
+
const test0 = $http.send({
|
|
1489
|
+
url: testURL + "?testError=1",
|
|
1490
|
+
})
|
|
1491
|
+
|
|
1492
|
+
// basic fields check
|
|
1493
|
+
const test1 = $http.send({
|
|
1494
|
+
method: "post",
|
|
1495
|
+
url: testURL,
|
|
1496
|
+
headers: {"header1": "123", "header2": "456"},
|
|
1497
|
+
body: '789',
|
|
1498
|
+
})
|
|
1499
|
+
|
|
1500
|
+
// with custom content-type header
|
|
1501
|
+
const test2 = $http.send({
|
|
1502
|
+
url: testURL,
|
|
1503
|
+
headers: {"content-type": "text/plain"},
|
|
1504
|
+
})
|
|
1505
|
+
|
|
1506
|
+
// with FormData
|
|
1507
|
+
const formData = new FormData()
|
|
1508
|
+
formData.append("title", "123")
|
|
1509
|
+
const test3 = $http.send({
|
|
1510
|
+
url: testURL,
|
|
1511
|
+
body: formData,
|
|
1512
|
+
headers: {"content-type": "text/plain"}, // should be ignored
|
|
1513
|
+
})
|
|
1514
|
+
|
|
1515
|
+
// raw body response field check
|
|
1516
|
+
const test4 = $http.send({
|
|
1517
|
+
method: "post",
|
|
1518
|
+
url: testURL,
|
|
1519
|
+
body: 'test',
|
|
1520
|
+
})
|
|
1521
|
+
|
|
1522
|
+
const scenarios = [
|
|
1523
|
+
[test0, {
|
|
1524
|
+
"statusCode": "400",
|
|
1525
|
+
}],
|
|
1526
|
+
[test1, {
|
|
1527
|
+
"statusCode": "200",
|
|
1528
|
+
"headers.X-Custom.0": "custom_header",
|
|
1529
|
+
"cookies.sessionId.value": "123456",
|
|
1530
|
+
"json.method": "POST",
|
|
1531
|
+
"json.headers.header1": "123",
|
|
1532
|
+
"json.headers.header2": "456",
|
|
1533
|
+
"json.body": "789",
|
|
1534
|
+
}],
|
|
1535
|
+
[test2, {
|
|
1536
|
+
"statusCode": "200",
|
|
1537
|
+
"headers.X-Custom.0": "custom_header",
|
|
1538
|
+
"cookies.sessionId.value": "123456",
|
|
1539
|
+
"json.method": "GET",
|
|
1540
|
+
"json.headers.content_type": "text/plain",
|
|
1541
|
+
}],
|
|
1542
|
+
[test3, {
|
|
1543
|
+
"statusCode": "200",
|
|
1544
|
+
"headers.X-Custom.0": "custom_header",
|
|
1545
|
+
"cookies.sessionId.value": "123456",
|
|
1546
|
+
"json.method": "GET",
|
|
1547
|
+
"json.body": [
|
|
1548
|
+
"\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n123\r\n--",
|
|
1549
|
+
],
|
|
1550
|
+
"json.headers.content_type": [
|
|
1551
|
+
"multipart/form-data; boundary="
|
|
1552
|
+
],
|
|
1553
|
+
}],
|
|
1554
|
+
[test4, {
|
|
1555
|
+
"statusCode": "200",
|
|
1556
|
+
"headers.X-Custom.0": "custom_header",
|
|
1557
|
+
"cookies.sessionId.value": "123456",
|
|
1558
|
+
// {"body":"test","headers":{"accept_encoding":"gzip","content_length":"4","user_agent":"Go-http-client/1.1"},"method":"POST"}
|
|
1559
|
+
"body": [123,34,98,111,100,121,34,58,34,116,101,115,116,34,44,34,104,101,97,100,101,114,115,34,58,123,34,97,99,99,101,112,116,95,101,110,99,111,100,105,110,103,34,58,34,103,122,105,112,34,44,34,99,111,110,116,101,110,116,95,108,101,110,103,116,104,34,58,34,52,34,44,34,117,115,101,114,95,97,103,101,110,116,34,58,34,71,111,45,104,116,116,112,45,99,108,105,101,110,116,47,49,46,49,34,125,44,34,109,101,116,104,111,100,34,58,34,80,79,83,84,34,125],
|
|
1560
|
+
}],
|
|
1561
|
+
]
|
|
1562
|
+
|
|
1563
|
+
for (let scenario of scenarios) {
|
|
1564
|
+
const result = scenario[0];
|
|
1565
|
+
const expectations = scenario[1];
|
|
1566
|
+
|
|
1567
|
+
for (let key in expectations) {
|
|
1568
|
+
const value = getNestedVal(result, key);
|
|
1569
|
+
const expectation = expectations[key]
|
|
1570
|
+
if (Array.isArray(expectation)) {
|
|
1571
|
+
// check for partial match(es)
|
|
1572
|
+
for (let exp of expectation) {
|
|
1573
|
+
if (!value.includes(exp)) {
|
|
1574
|
+
throw new Error('Expected ' + key + ' to contain ' + exp + ', got: ' + toString(result.body));
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
} else {
|
|
1578
|
+
// check for direct match
|
|
1579
|
+
if (value != expectation) {
|
|
1580
|
+
throw new Error('Expected ' + key + ' ' + expectation + ', got: ' + toString(result.body));
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
`)
|
|
1586
|
+
if err != nil {
|
|
1587
|
+
t.Fatal(err)
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
func TestCronBindsCount(t *testing.T) {
|
|
1592
|
+
app, _ := tests.NewTestApp()
|
|
1593
|
+
defer app.Cleanup()
|
|
1594
|
+
|
|
1595
|
+
vm := goja.New()
|
|
1596
|
+
|
|
1597
|
+
pool := newPool(1, func() *goja.Runtime { return goja.New() })
|
|
1598
|
+
|
|
1599
|
+
cronBinds(app, vm, pool)
|
|
1600
|
+
|
|
1601
|
+
testBindsCount(vm, "this", 2, t)
|
|
1602
|
+
|
|
1603
|
+
pool.run(func(poolVM *goja.Runtime) error {
|
|
1604
|
+
testBindsCount(poolVM, "this", 2, t)
|
|
1605
|
+
return nil
|
|
1606
|
+
})
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
func TestHooksBindsCount(t *testing.T) {
|
|
1610
|
+
app, _ := tests.NewTestApp()
|
|
1611
|
+
defer app.Cleanup()
|
|
1612
|
+
|
|
1613
|
+
vm := goja.New()
|
|
1614
|
+
hooksBinds(app, vm, nil)
|
|
1615
|
+
|
|
1616
|
+
testBindsCount(vm, "this", 82, t)
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
func TestHooksBinds(t *testing.T) {
|
|
1620
|
+
app, _ := tests.NewTestApp()
|
|
1621
|
+
defer app.Cleanup()
|
|
1622
|
+
|
|
1623
|
+
result := &struct {
|
|
1624
|
+
Called int
|
|
1625
|
+
}{}
|
|
1626
|
+
|
|
1627
|
+
vmFactory := func() *goja.Runtime {
|
|
1628
|
+
vm := goja.New()
|
|
1629
|
+
BindCore(vm)
|
|
1630
|
+
vm.Set("$app", app)
|
|
1631
|
+
vm.Set("result", result)
|
|
1632
|
+
return vm
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
pool := newPool(1, vmFactory)
|
|
1636
|
+
|
|
1637
|
+
vm := vmFactory()
|
|
1638
|
+
hooksBinds(app, vm, pool)
|
|
1639
|
+
|
|
1640
|
+
_, err := vm.RunString(`
|
|
1641
|
+
onModelUpdate((e) => {
|
|
1642
|
+
result.called++;
|
|
1643
|
+
e.next()
|
|
1644
|
+
}, "demo1")
|
|
1645
|
+
|
|
1646
|
+
onModelUpdate((e) => {
|
|
1647
|
+
throw new Error("example");
|
|
1648
|
+
}, "demo1")
|
|
1649
|
+
|
|
1650
|
+
onModelUpdate((e) => {
|
|
1651
|
+
result.called++;
|
|
1652
|
+
e.next();
|
|
1653
|
+
}, "demo2")
|
|
1654
|
+
|
|
1655
|
+
onModelUpdate((e) => {
|
|
1656
|
+
result.called++;
|
|
1657
|
+
e.next()
|
|
1658
|
+
}, "demo2")
|
|
1659
|
+
|
|
1660
|
+
onModelUpdate((e) => {
|
|
1661
|
+
// stop propagation
|
|
1662
|
+
}, "demo2")
|
|
1663
|
+
|
|
1664
|
+
onModelUpdate((e) => {
|
|
1665
|
+
result.called++;
|
|
1666
|
+
e.next();
|
|
1667
|
+
}, "demo2")
|
|
1668
|
+
|
|
1669
|
+
onBootstrap((e) => {
|
|
1670
|
+
e.next()
|
|
1671
|
+
|
|
1672
|
+
// check hooks propagation and tags filtering
|
|
1673
|
+
const recordA = $app.findFirstRecordByFilter("demo2", "1=1")
|
|
1674
|
+
recordA.set("title", "update")
|
|
1675
|
+
$app.save(recordA)
|
|
1676
|
+
if (result.called != 2) {
|
|
1677
|
+
throw new Error("Expected result.called to be 2, got " + result.called)
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// reset
|
|
1681
|
+
result.called = 0;
|
|
1682
|
+
|
|
1683
|
+
// check error handling
|
|
1684
|
+
let hasErr = false
|
|
1685
|
+
try {
|
|
1686
|
+
const recordB = $app.findFirstRecordByFilter("demo1", "1=1")
|
|
1687
|
+
recordB.set("text", "update")
|
|
1688
|
+
$app.save(recordB)
|
|
1689
|
+
} catch (err) {
|
|
1690
|
+
hasErr = true
|
|
1691
|
+
}
|
|
1692
|
+
if (!hasErr) {
|
|
1693
|
+
throw new Error("Expected an error to be thrown")
|
|
1694
|
+
}
|
|
1695
|
+
if (result.called != 1) {
|
|
1696
|
+
throw new Error("Expected result.called to be 1, got " + result.called)
|
|
1697
|
+
}
|
|
1698
|
+
})
|
|
1699
|
+
|
|
1700
|
+
$app.bootstrap();
|
|
1701
|
+
`)
|
|
1702
|
+
if err != nil {
|
|
1703
|
+
t.Fatal(err)
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
func TestHooksExceptionUnwrapping(t *testing.T) {
|
|
1708
|
+
app, _ := tests.NewTestApp()
|
|
1709
|
+
defer app.Cleanup()
|
|
1710
|
+
|
|
1711
|
+
goErr := errors.New("test")
|
|
1712
|
+
|
|
1713
|
+
vmFactory := func() *goja.Runtime {
|
|
1714
|
+
vm := goja.New()
|
|
1715
|
+
BindCore(vm)
|
|
1716
|
+
vm.Set("$app", app)
|
|
1717
|
+
vm.Set("goErr", goErr)
|
|
1718
|
+
return vm
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
pool := newPool(1, vmFactory)
|
|
1722
|
+
|
|
1723
|
+
vm := vmFactory()
|
|
1724
|
+
hooksBinds(app, vm, pool)
|
|
1725
|
+
|
|
1726
|
+
_, err := vm.RunString(`
|
|
1727
|
+
onModelUpdate((e) => {
|
|
1728
|
+
throw goErr
|
|
1729
|
+
}, "demo1")
|
|
1730
|
+
`)
|
|
1731
|
+
if err != nil {
|
|
1732
|
+
t.Fatal(err)
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
record, err := app.FindFirstRecordByFilter("demo1", "1=1")
|
|
1736
|
+
if err != nil {
|
|
1737
|
+
t.Fatal(err)
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
record.Set("text", "update")
|
|
1741
|
+
|
|
1742
|
+
err = app.Save(record)
|
|
1743
|
+
if !errors.Is(err, goErr) {
|
|
1744
|
+
t.Fatalf("Expected goError, got %v", err)
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
func TestRouterBindsCount(t *testing.T) {
|
|
1749
|
+
app, _ := tests.NewTestApp()
|
|
1750
|
+
defer app.Cleanup()
|
|
1751
|
+
|
|
1752
|
+
vm := goja.New()
|
|
1753
|
+
routerBinds(app, vm, nil)
|
|
1754
|
+
|
|
1755
|
+
testBindsCount(vm, "this", 2, t)
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
func TestRouterBinds(t *testing.T) {
|
|
1759
|
+
app, _ := tests.NewTestApp()
|
|
1760
|
+
defer app.Cleanup()
|
|
1761
|
+
|
|
1762
|
+
result := &struct {
|
|
1763
|
+
RouteMiddlewareCalls int
|
|
1764
|
+
GlobalMiddlewareCalls int
|
|
1765
|
+
}{}
|
|
1766
|
+
|
|
1767
|
+
vmFactory := func() *goja.Runtime {
|
|
1768
|
+
vm := goja.New()
|
|
1769
|
+
BindCore(vm)
|
|
1770
|
+
BindApis(vm)
|
|
1771
|
+
vm.Set("$app", app)
|
|
1772
|
+
vm.Set("result", result)
|
|
1773
|
+
return vm
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
pool := newPool(1, vmFactory)
|
|
1777
|
+
|
|
1778
|
+
vm := vmFactory()
|
|
1779
|
+
routerBinds(app, vm, pool)
|
|
1780
|
+
|
|
1781
|
+
_, err := vm.RunString(`
|
|
1782
|
+
routerAdd("GET", "/test", (e) => {
|
|
1783
|
+
result.routeMiddlewareCalls++;
|
|
1784
|
+
}, (e) => {
|
|
1785
|
+
result.routeMiddlewareCalls++;
|
|
1786
|
+
return e.next();
|
|
1787
|
+
})
|
|
1788
|
+
|
|
1789
|
+
// Promise is not technically supported as return result
|
|
1790
|
+
// but we try to resolve it at least for thrown errors
|
|
1791
|
+
routerAdd("GET", "/error", async (e) => {
|
|
1792
|
+
throw new ApiError(456, 'test', null)
|
|
1793
|
+
})
|
|
1794
|
+
|
|
1795
|
+
routerUse((e) => {
|
|
1796
|
+
result.globalMiddlewareCalls++;
|
|
1797
|
+
|
|
1798
|
+
return e.next();
|
|
1799
|
+
})
|
|
1800
|
+
`)
|
|
1801
|
+
if err != nil {
|
|
1802
|
+
t.Fatal(err)
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
pbRouter, err := apis.NewRouter(app)
|
|
1806
|
+
if err != nil {
|
|
1807
|
+
t.Fatal(err)
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
serveEvent := new(core.ServeEvent)
|
|
1811
|
+
serveEvent.App = app
|
|
1812
|
+
serveEvent.Router = pbRouter
|
|
1813
|
+
if err = app.OnServe().Trigger(serveEvent); err != nil {
|
|
1814
|
+
t.Fatal(err)
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
mux, err := serveEvent.Router.BuildMux()
|
|
1818
|
+
if err != nil {
|
|
1819
|
+
t.Fatalf("Failed to build router mux: %v", err)
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
scenarios := []struct {
|
|
1823
|
+
method string
|
|
1824
|
+
path string
|
|
1825
|
+
expectedRouteMiddlewareCalls int
|
|
1826
|
+
expectedGlobalMiddlewareCalls int
|
|
1827
|
+
expectedCode int
|
|
1828
|
+
}{
|
|
1829
|
+
{"GET", "/test", 2, 1, 200},
|
|
1830
|
+
{"GET", "/error", 0, 1, 456},
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
for _, s := range scenarios {
|
|
1834
|
+
t.Run(s.method+" "+s.path, func(t *testing.T) {
|
|
1835
|
+
// reset
|
|
1836
|
+
result.RouteMiddlewareCalls = 0
|
|
1837
|
+
result.GlobalMiddlewareCalls = 0
|
|
1838
|
+
|
|
1839
|
+
rec := httptest.NewRecorder()
|
|
1840
|
+
req := httptest.NewRequest(s.method, s.path, nil)
|
|
1841
|
+
mux.ServeHTTP(rec, req)
|
|
1842
|
+
|
|
1843
|
+
if result.RouteMiddlewareCalls != s.expectedRouteMiddlewareCalls {
|
|
1844
|
+
t.Fatalf("Expected RouteMiddlewareCalls %d, got %d", s.expectedRouteMiddlewareCalls, result.RouteMiddlewareCalls)
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
if result.GlobalMiddlewareCalls != s.expectedGlobalMiddlewareCalls {
|
|
1848
|
+
t.Fatalf("Expected GlobalMiddlewareCalls %d, got %d", s.expectedGlobalMiddlewareCalls, result.GlobalMiddlewareCalls)
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
if rec.Code != s.expectedCode {
|
|
1852
|
+
t.Fatalf("Expected status code %d, got %d", s.expectedCode, rec.Code)
|
|
1853
|
+
}
|
|
1854
|
+
})
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
func TestBindFilepathCount(t *testing.T) {
|
|
1859
|
+
vm := goja.New()
|
|
1860
|
+
BindFilepath(vm)
|
|
1861
|
+
|
|
1862
|
+
testBindsCount(vm, "$filepath", 15, t)
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
func TestBindOSCount(t *testing.T) {
|
|
1866
|
+
vm := goja.New()
|
|
1867
|
+
BindOS(vm)
|
|
1868
|
+
|
|
1869
|
+
testBindsCount(vm, "$os", 20, t)
|
|
1870
|
+
}
|