@invisibleloop/pulse 0.20.37 → 0.20.39

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.
@@ -1,467 +0,0 @@
1
- /**
2
- * Spec schema tests — run with: node src/spec/schema.test.js
3
- */
4
- import { validateSpec, assertValidSpec, getStreamOrder, getViewSegments } from './schema.js'
5
-
6
- let passed = 0
7
- let failed = 0
8
-
9
- function test(label, fn) {
10
- try {
11
- fn()
12
- console.log(` ✓ ${label}`)
13
- passed++
14
- } catch (e) {
15
- console.log(` ✗ ${label}`)
16
- console.log(` ${e.message}`)
17
- failed++
18
- }
19
- }
20
-
21
- function assert(condition, msg) {
22
- if (!condition) throw new Error(msg || 'Assertion failed')
23
- }
24
-
25
- function assertErrors(spec, ...expectedFragments) {
26
- const { valid, errors } = validateSpec(spec)
27
- assert(!valid, 'Expected spec to be invalid but it passed')
28
- for (const fragment of expectedFragments) {
29
- const found = errors.some(e => e.includes(fragment))
30
- assert(found, `Expected error containing "${fragment}" but got:\n ${errors.join('\n ')}`)
31
- }
32
- }
33
-
34
- function assertValid(spec) {
35
- const { valid, errors } = validateSpec(spec)
36
- assert(valid, `Expected spec to be valid but got errors:\n ${errors.join('\n ')}`)
37
- }
38
-
39
- // ---------------------------------------------------------------------------
40
-
41
- console.log('\nSpec validation\n')
42
-
43
- test('rejects non-object', () => {
44
- assertErrors(null, 'must be a plain object')
45
- assertErrors('string', 'must be a plain object')
46
- })
47
-
48
- test('route is optional (derived server-side by convention)', () => {
49
- const { valid } = validateSpec({ state: {}, view: () => '' })
50
- assert(valid, 'spec without route should be valid for client-side use')
51
- })
52
-
53
- test('rejects route that does not start with /', () => {
54
- assertErrors({ route: 'contact', state: {}, view: () => '' }, 'must start with "/"')
55
- })
56
-
57
- test('state is optional — omitting it is valid', () => {
58
- const { valid, errors } = validateSpec({ route: '/contact', view: () => '' })
59
- if (!valid) throw new Error(`Expected valid but got: ${errors.join(', ')}`)
60
- })
61
-
62
- test('state must be a plain object when provided', () => {
63
- assertErrors({ route: '/contact', state: 'bad', view: () => '' }, 'spec.state must be a plain object')
64
- })
65
-
66
- test('requires view', () => {
67
- assertErrors({ route: '/contact', state: {} }, 'spec.view is required')
68
- })
69
-
70
- test('accepts function view', () => {
71
- assertValid({ route: '/contact', state: {}, view: () => '<div/>' })
72
- })
73
-
74
- test('accepts segmented view', () => {
75
- assertValid({
76
- route: '/contact',
77
- state: {},
78
- view: {
79
- header: () => '<header/>',
80
- form: () => '<form/>'
81
- }
82
- })
83
- })
84
-
85
- test('rejects view segment that is not a function', () => {
86
- assertErrors(
87
- { route: '/contact', state: {}, view: { header: '<header/>' } },
88
- 'spec.view.header must be a function'
89
- )
90
- })
91
-
92
- test('rejects stream referencing undefined segment', () => {
93
- assertErrors(
94
- {
95
- route: '/contact',
96
- state: {},
97
- view: { header: () => '' },
98
- stream: { shell: ['header', 'missing'] }
99
- },
100
- 'spec.stream references "missing"'
101
- )
102
- })
103
-
104
- test('rejects stream.shell that is not an array', () => {
105
- assertErrors(
106
- { route: '/contact', state: {}, view: () => '', stream: { shell: 'header' } },
107
- 'spec.stream.shell must be an array'
108
- )
109
- })
110
-
111
- test('accepts valid stream.scope', () => {
112
- const spec = {
113
- route: '/s',
114
- state: {},
115
- server: { user: async () => {}, posts: async () => {} },
116
- stream: {
117
- shell: ['header'],
118
- deferred: ['feed'],
119
- scope: { header: ['user'], feed: ['posts'] },
120
- },
121
- view: { header: () => '', feed: () => '' },
122
- }
123
- const { valid } = validateSpec(spec)
124
- assert(valid, 'Expected valid spec with stream.scope')
125
- })
126
-
127
- test('rejects stream.scope referencing unknown segment', () => {
128
- assertErrors(
129
- {
130
- route: '/s',
131
- state: {},
132
- server: { user: async () => {} },
133
- stream: { shell: ['header'], scope: { unknown: ['user'] } },
134
- view: { header: () => '' },
135
- },
136
- 'spec.stream.scope references unknown segment "unknown"'
137
- )
138
- })
139
-
140
- test('rejects stream.scope referencing unknown server key', () => {
141
- assertErrors(
142
- {
143
- route: '/s',
144
- state: {},
145
- server: { user: async () => {} },
146
- stream: { shell: ['header'], scope: { header: ['missing'] } },
147
- view: { header: () => '' },
148
- },
149
- 'spec.stream.scope["header"] references unknown server key "missing"'
150
- )
151
- })
152
-
153
- test('rejects stream.scope with non-array value', () => {
154
- assertErrors(
155
- {
156
- route: '/s',
157
- state: {},
158
- server: { user: async () => {} },
159
- stream: { shell: ['header'], scope: { header: 'user' } },
160
- view: { header: () => '' },
161
- },
162
- 'spec.stream.scope["header"] must be an array'
163
- )
164
- })
165
-
166
- test('rejects mutations with non-function values', () => {
167
- assertErrors(
168
- { route: '/x', state: {}, view: () => '', mutations: { inc: 'not a function' } },
169
- 'spec.mutations.inc must be a function'
170
- )
171
- })
172
-
173
- test('rejects action missing run', () => {
174
- assertErrors(
175
- {
176
- route: '/x', state: {}, view: () => '',
177
- actions: { send: { onSuccess: () => {}, onError: () => {} } }
178
- },
179
- 'spec.actions.send.run must be'
180
- )
181
- })
182
-
183
- test('rejects action missing onSuccess', () => {
184
- assertErrors(
185
- {
186
- route: '/x', state: {}, view: () => '',
187
- actions: { send: { run: async () => {}, onError: () => {} } }
188
- },
189
- 'spec.actions.send.onSuccess must be'
190
- )
191
- })
192
-
193
- test('rejects unknown validation rule', () => {
194
- assertErrors(
195
- {
196
- route: '/x', state: {}, view: () => '',
197
- validation: { 'fields.email': { required: true, unknown: true } }
198
- },
199
- 'unknown is not a recognised rule'
200
- )
201
- })
202
-
203
- test('accepts full valid spec', () => {
204
- assertValid({
205
- route: '/contact',
206
- stream: {
207
- shell: ['header', 'form'],
208
- deferred: ['recent']
209
- },
210
- server: {
211
- recent: async () => []
212
- },
213
- state: {
214
- fields: { name: '', email: '', message: '' },
215
- status: 'idle'
216
- },
217
- validation: {
218
- 'fields.name': { required: true, minLength: 2 },
219
- 'fields.email': { required: true, format: 'email' },
220
- 'fields.message': { required: true, minLength: 10, maxLength: 1000 }
221
- },
222
- view: {
223
- header: () => '<header/>',
224
- form: (state) => `<form>${state.status}</form>`,
225
- recent: (state, server) => `<aside>${server.recent.length}</aside>`
226
- },
227
- mutations: {
228
- updateField: (state, e) => ({ fields: { ...state.fields, [e.target.name]: e.target.value } })
229
- },
230
- actions: {
231
- sendMessage: {
232
- validate: true,
233
- run: async (state) => {},
234
- onSuccess: () => ({ status: 'success' }),
235
- onError: () => ({ status: 'error' })
236
- }
237
- },
238
- meta: { title: 'Contact Us' }
239
- })
240
- })
241
-
242
- // ---------------------------------------------------------------------------
243
-
244
- console.log('\nRaw content specs (contentType)\n')
245
-
246
- test('accepts a valid raw content spec', () => {
247
- assertValid({
248
- route: '/feed.xml',
249
- contentType: 'application/rss+xml; charset=utf-8',
250
- render: (ctx, server) => `<rss/>`
251
- })
252
- })
253
-
254
- test('raw spec does not require state or view', () => {
255
- const { valid } = validateSpec({
256
- route: '/sitemap.xml',
257
- contentType: 'application/xml',
258
- render: () => `<?xml?>`
259
- })
260
- assert(valid, 'raw spec without state/view should be valid')
261
- })
262
-
263
- test('raw spec accepts server data', () => {
264
- assertValid({
265
- route: '/feed.json',
266
- contentType: 'application/json',
267
- server: { posts: async () => [] },
268
- render: (ctx, server) => JSON.stringify(server.posts)
269
- })
270
- })
271
-
272
- test('rejects raw spec missing render', () => {
273
- assertErrors(
274
- { route: '/feed.xml', contentType: 'application/rss+xml' },
275
- 'spec.render is required'
276
- )
277
- })
278
-
279
- test('rejects raw spec with empty contentType', () => {
280
- assertErrors(
281
- { route: '/feed.xml', contentType: '', render: () => '' },
282
- 'spec.contentType must be a non-empty string'
283
- )
284
- })
285
-
286
- // ---------------------------------------------------------------------------
287
-
288
- console.log('\ngetViewSegments\n')
289
-
290
- test('function view returns ["default"]', () => {
291
- const segments = getViewSegments({ view: () => '' })
292
- assert(segments.length === 1 && segments[0] === 'default', `Got ${segments}`)
293
- })
294
-
295
- test('segmented view returns keys', () => {
296
- const segments = getViewSegments({ view: { header: () => '', form: () => '' } })
297
- assert(segments.includes('header') && segments.includes('form'), `Got ${segments}`)
298
- })
299
-
300
- // ---------------------------------------------------------------------------
301
-
302
- console.log('\ngetStreamOrder\n')
303
-
304
- test('no stream config — all segments in shell', () => {
305
- const order = getStreamOrder({ view: { header: () => '', form: () => '' } })
306
- assert(order.shell.includes('header'), 'header should be in shell')
307
- assert(order.deferred.length === 0, 'deferred should be empty')
308
- })
309
-
310
- test('stream config is respected', () => {
311
- const order = getStreamOrder({
312
- view: { header: () => '', form: () => '', recent: () => '' },
313
- stream: { shell: ['header', 'form'], deferred: ['recent'] }
314
- })
315
- assert(order.shell.includes('header'), 'header in shell')
316
- assert(order.shell.includes('form'), 'form in shell')
317
- assert(order.deferred.includes('recent'), 'recent in deferred')
318
- })
319
-
320
- // ---------------------------------------------------------------------------
321
- // guard
322
- // ---------------------------------------------------------------------------
323
-
324
- test('accepts a valid guard function', () => {
325
- const { valid, errors } = validateSpec({
326
- route: '/admin',
327
- state: {},
328
- view: () => '<main id="main-content">Admin</main>',
329
- guard: async (ctx) => { if (!ctx.cookies.session) return { redirect: '/login' } },
330
- })
331
- assert(valid, `Expected valid spec, got errors: ${errors.join(', ')}`)
332
- })
333
-
334
- test('rejects guard that is not a function', () => {
335
- assertErrors(
336
- { route: '/admin', state: {}, view: () => '', guard: '/login' },
337
- 'spec.guard must be a function'
338
- )
339
- })
340
-
341
- test('rejects guard that is an object', () => {
342
- assertErrors(
343
- { route: '/admin', state: {}, view: () => '', guard: { redirect: '/login' } },
344
- 'spec.guard must be a function'
345
- )
346
- })
347
-
348
- // ---------------------------------------------------------------------------
349
- // assertValidSpec — framework-injected hydrate must not produce a warning
350
- // ---------------------------------------------------------------------------
351
-
352
- console.log('\nassertValidSpec\n')
353
-
354
- test('does not warn when hydrate is framework-injected', () => {
355
- // The server injects hydrate onto specs that need it. assertValidSpec is called
356
- // on every render — it must not print a false-positive warning about hydrate.
357
- const warnings = []
358
- const orig = console.warn
359
- console.warn = (...args) => warnings.push(args.join(' '))
360
- try {
361
- assertValidSpec({
362
- route: '/entry',
363
- hydrate: '/src/pages/entry.js',
364
- state: { editing: false },
365
- view: () => '<main id="main-content"></main>',
366
- mutations: { startEdit: () => ({ editing: true }) },
367
- meta: { title: 'Entry', description: 'A page' },
368
- })
369
- } finally {
370
- console.warn = orig
371
- }
372
- const hydrateWarning = warnings.some(w => w.includes('hydrate'))
373
- assert(!hydrateWarning, `Unexpected hydrate warning: ${warnings.join('; ')}`)
374
- })
375
-
376
- test('validateSpec still warns when hydrate is set manually in source', () => {
377
- // validateSpec (used by the MCP tool on raw source files) should still warn.
378
- const { warnings } = validateSpec({
379
- route: '/entry',
380
- hydrate: '/src/pages/entry.js',
381
- view: () => '<main id="main-content"></main>',
382
- meta: { title: 'Entry', description: 'A page' },
383
- })
384
- assert(warnings.some(w => w.includes('hydrate')), 'Expected hydrate warning from validateSpec')
385
- })
386
-
387
- // meta.theme warnings
388
- test('warns when meta.theme is not set', () => {
389
- const { warnings } = validateSpec({
390
- route: '/entry',
391
- view: () => '<main id="main-content"></main>',
392
- meta: { title: 'Entry', description: 'A page' },
393
- })
394
- assert(warnings.some(w => w.includes('meta.theme')), 'Expected meta.theme warning')
395
- })
396
-
397
- test('does not warn when meta.theme is a string', () => {
398
- const { warnings } = validateSpec({
399
- route: '/entry',
400
- view: () => '<main id="main-content"></main>',
401
- meta: { title: 'Entry', description: 'A page', theme: 'dark' },
402
- })
403
- assert(!warnings.some(w => w.includes('meta.theme')), 'Unexpected meta.theme warning when theme is set')
404
- })
405
-
406
- test('does not warn when meta.theme is a function', () => {
407
- const { warnings } = validateSpec({
408
- route: '/entry',
409
- view: () => '<main id="main-content"></main>',
410
- meta: { title: 'Entry', description: 'A page', theme: (ctx) => ctx.brand?.theme ?? 'dark' },
411
- })
412
- assert(!warnings.some(w => w.includes('meta.theme')), 'Unexpected meta.theme warning when theme is a function')
413
- })
414
-
415
- // --- route '*', submit, csrf ------------------------------------------------
416
-
417
- test('route "*" (custom not-found page) is valid', () => {
418
- const { valid, errors } = validateSpec({ route: '*', view: () => '<main id="main-content">404</main>' })
419
- assert(valid, `Expected valid, got: ${errors.join('; ')}`)
420
- })
421
-
422
- test('non-"/" routes other than "*" are still rejected', () => {
423
- const { valid } = validateSpec({ route: 'about', view: () => '' })
424
- assert(!valid, 'Expected route without leading / to be invalid')
425
- })
426
-
427
- test('submit must be a function', () => {
428
- const { valid, errors } = validateSpec({ route: '/x', view: () => '', submit: 'nope' })
429
- assert(!valid && errors.some(e => e.includes('submit')), 'Expected submit type error')
430
- })
431
-
432
- test('a valid submit function passes', () => {
433
- const { valid } = validateSpec({ route: '/x', view: () => '', submit: async () => ({ redirect: '/' }) })
434
- assert(valid, 'Expected spec with submit function to be valid')
435
- })
436
-
437
- test('csrf must be a boolean', () => {
438
- const { valid, errors } = validateSpec({ route: '/x', view: () => '', csrf: 'off' })
439
- assert(!valid && errors.some(e => e.includes('csrf')), 'Expected csrf type error')
440
- })
441
-
442
- test('csrf: false with submit warns about unprotected POSTs', () => {
443
- const { valid, warnings } = validateSpec({ route: '/x', view: () => '', submit: async () => {}, csrf: false })
444
- assert(valid, 'csrf: false is valid')
445
- assert(warnings.some(w => w.includes('CSRF')), 'Expected a warning about disabled CSRF protection')
446
- })
447
-
448
- // --- sitemap ------------------------------------------------------------------
449
-
450
- test('sitemap accepts boolean, array, and function', () => {
451
- for (const v of [true, false, ['/a', { path: '/b', lastmod: '2026-01-01' }], async () => ['/a']]) {
452
- const { valid, errors } = validateSpec({ route: '/x', view: () => '', sitemap: v })
453
- assert(valid, `Expected sitemap ${typeof v} to be valid: ${errors.join('; ')}`)
454
- }
455
- })
456
-
457
- test('sitemap rejects invalid types and malformed array entries', () => {
458
- const { valid: v1 } = validateSpec({ route: '/x', view: () => '', sitemap: 'yes' })
459
- assert(!v1, 'Expected string sitemap to be invalid')
460
- const { valid: v2, errors } = validateSpec({ route: '/x', view: () => '', sitemap: ['no-slash'] })
461
- assert(!v2 && errors.some(e => e.includes('sitemap')), 'Expected malformed array entry to be invalid')
462
- })
463
-
464
- // ---------------------------------------------------------------------------
465
-
466
- console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed\n`)
467
- if (failed > 0) process.exit(1)
@@ -1,210 +0,0 @@
1
- /**
2
- * Pulse — Store tests
3
- * run: node src/store/store.test.js
4
- */
5
-
6
- import { validateStore, resolveStoreState } from './index.js'
7
-
8
- let passed = 0
9
- let failed = 0
10
-
11
- async function test(label, fn) {
12
- try {
13
- await fn()
14
- console.log(` ✓ ${label}`)
15
- passed++
16
- } catch (e) {
17
- console.log(` ✗ ${label}`)
18
- console.log(` ${e.message}`)
19
- failed++
20
- }
21
- }
22
-
23
- function assert(condition, msg) {
24
- if (!condition) throw new Error(msg || 'Assertion failed')
25
- }
26
-
27
- function assertEqual(a, b, msg) {
28
- if (a !== b) throw new Error(msg || `Expected ${JSON.stringify(a)} === ${JSON.stringify(b)}`)
29
- }
30
-
31
- // ---------------------------------------------------------------------------
32
-
33
- console.log('\nvalidateStore\n')
34
-
35
- await test('accepts a valid store with state and server', () => {
36
- const { valid } = validateStore({
37
- state: { user: null },
38
- server: { user: async () => null },
39
- })
40
- assert(valid, 'Expected valid store')
41
- })
42
-
43
- await test('accepts a store with only state', () => {
44
- const { valid } = validateStore({ state: { theme: 'dark' } })
45
- assert(valid)
46
- })
47
-
48
- await test('accepts a store with only server', () => {
49
- const { valid } = validateStore({ server: { user: async () => null } })
50
- assert(valid)
51
- })
52
-
53
- await test('accepts a store with mutations', () => {
54
- const { valid } = validateStore({
55
- state: { count: 0 },
56
- mutations: { increment: (s) => ({ count: s.count + 1 }) },
57
- })
58
- assert(valid)
59
- })
60
-
61
- await test('accepts a store with hydrate', () => {
62
- const { valid } = validateStore({ state: {}, hydrate: '/pulse.store.js' })
63
- assert(valid)
64
- })
65
-
66
- await test('rejects null', () => {
67
- const { valid, errors } = validateStore(null)
68
- assert(!valid)
69
- assert(errors.length > 0)
70
- })
71
-
72
- await test('rejects a non-object', () => {
73
- const { valid } = validateStore('not an object')
74
- assert(!valid)
75
- })
76
-
77
- await test('rejects an array', () => {
78
- const { valid } = validateStore([])
79
- assert(!valid)
80
- })
81
-
82
- await test('rejects state that is an array', () => {
83
- const { valid, errors } = validateStore({ state: [] })
84
- assert(!valid)
85
- assert(errors.some(e => e.includes('state')), `Expected state error in: ${errors}`)
86
- })
87
-
88
- await test('rejects state that is a string', () => {
89
- const { valid } = validateStore({ state: 'bad' })
90
- assert(!valid)
91
- })
92
-
93
- await test('rejects server that is an array', () => {
94
- const { valid, errors } = validateStore({ server: ['fn'] })
95
- assert(!valid)
96
- assert(errors.some(e => e.includes('server')), `Expected server error in: ${errors}`)
97
- })
98
-
99
- await test('rejects a server entry that is not a function', () => {
100
- const { valid, errors } = validateStore({ server: { user: 42 } })
101
- assert(!valid)
102
- assert(errors.some(e => e.includes('store.server.user')), `Expected named error in: ${errors}`)
103
- })
104
-
105
- await test('rejects mutations that is an array', () => {
106
- const { valid, errors } = validateStore({ mutations: [() => {}] })
107
- assert(!valid)
108
- assert(errors.some(e => e.includes('mutations')), `Expected mutations error in: ${errors}`)
109
- })
110
-
111
- await test('rejects a mutation entry that is not a function', () => {
112
- const { valid, errors } = validateStore({ mutations: { toggle: 'bad' } })
113
- assert(!valid)
114
- assert(errors.some(e => e.includes('store.mutations.toggle')), `Expected named error in: ${errors}`)
115
- })
116
-
117
- await test('rejects hydrate that does not start with /', () => {
118
- const { valid, errors } = validateStore({ hydrate: 'pulse.store.js' })
119
- assert(!valid)
120
- assert(errors.some(e => e.includes('hydrate')), `Expected hydrate error in: ${errors}`)
121
- })
122
-
123
- await test('rejects hydrate that is not a string', () => {
124
- const { valid } = validateStore({ hydrate: 42 })
125
- assert(!valid)
126
- })
127
-
128
- await test('returns all errors for multiple violations', () => {
129
- const { valid, errors } = validateStore({ state: [], server: { x: 'not-a-fn' } })
130
- assert(!valid)
131
- assert(errors.length >= 2, `Expected >= 2 errors, got ${errors.length}: ${errors.join(', ')}`)
132
- })
133
-
134
- // ---------------------------------------------------------------------------
135
-
136
- console.log('\nresolveStoreState\n')
137
-
138
- await test('returns state defaults when no server fetchers', async () => {
139
- const result = await resolveStoreState({ state: { user: null, theme: 'dark' } }, {})
140
- assertEqual(result.theme, 'dark')
141
- assert(result.user === null)
142
- })
143
-
144
- await test('resolves server fetchers and returns their results', async () => {
145
- const result = await resolveStoreState({
146
- state: { user: null, settings: {} },
147
- server: {
148
- user: async () => ({ id: 1, name: 'Alice' }),
149
- settings: async () => ({ theme: 'light' }),
150
- },
151
- }, {})
152
- assertEqual(result.user.name, 'Alice')
153
- assertEqual(result.settings.theme, 'light')
154
- })
155
-
156
- await test('server values override state defaults', async () => {
157
- const result = await resolveStoreState({
158
- state: { user: 'default' },
159
- server: { user: async () => 'from-server' },
160
- }, {})
161
- assertEqual(result.user, 'from-server')
162
- })
163
-
164
- await test('state defaults are preserved for keys not covered by server', async () => {
165
- const result = await resolveStoreState({
166
- state: { user: null, nav: [1, 2, 3] },
167
- server: { user: async () => ({ id: 1 }) },
168
- }, {})
169
- assertEqual(result.nav.length, 3)
170
- })
171
-
172
- await test('passes ctx to server fetchers', async () => {
173
- let received
174
- await resolveStoreState({
175
- server: { data: async (ctx) => { received = ctx; return null } },
176
- }, { cookies: { session: 'abc' } })
177
- assertEqual(received.cookies.session, 'abc')
178
- })
179
-
180
- await test('returns empty object when store has no state and no server', async () => {
181
- const result = await resolveStoreState({}, {})
182
- assert(typeof result === 'object' && result !== null)
183
- assertEqual(Object.keys(result).length, 0)
184
- })
185
-
186
- await test('timeout rejects if a fetcher exceeds fetcherTimeout', async () => {
187
- let threw = false
188
- try {
189
- await resolveStoreState({
190
- server: { slow: async () => new Promise(r => setTimeout(r, 200)) },
191
- }, { fetcherTimeout: 20 })
192
- } catch (e) {
193
- threw = true
194
- assert(e.message.includes('slow'), `Expected fetcher name in error: ${e.message}`)
195
- assert(e.message.toLowerCase().includes('timed out'), `Expected timeout message: ${e.message}`)
196
- }
197
- assert(threw, 'Expected timeout to throw')
198
- })
199
-
200
- await test('no timeout when fetcherTimeout is not set', async () => {
201
- const result = await resolveStoreState({
202
- server: { fast: async () => 'ok' },
203
- }, {})
204
- assertEqual(result.fast, 'ok')
205
- })
206
-
207
- // ---------------------------------------------------------------------------
208
-
209
- console.log(`\n${passed} passed, ${failed} failed\n`)
210
- if (failed > 0) process.exit(1)