File
Blob: src/workerd/api/tests/sync-kv-test.js
| 1 | // Copyright (c) 2025 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | import { DurableObject } from 'cloudflare:workers'; |
| 5 | import assert from 'node:assert'; |
| 6 | |
| 7 | export class MyActor extends DurableObject { |
| 8 | run() { |
| 9 | let kv = this.ctx.storage.kv; |
| 10 | |
| 11 | assert.strictEqual(kv.get('foo'), undefined); |
| 12 | kv.put('foo', 123); |
| 13 | assert.strictEqual(kv.get('foo'), 123); |
| 14 | |
| 15 | assert.strictEqual(kv.get('bar'), undefined); |
| 16 | kv.put('bar', 'abc'); |
| 17 | assert.strictEqual(kv.get('bar'), 'abc'); |
| 18 | |
| 19 | // Iterates in alphabetical order. |
| 20 | assert.deepEqual( |
| 21 | [...kv.list()], |
| 22 | [ |
| 23 | ['bar', 'abc'], |
| 24 | ['foo', 123], |
| 25 | ] |
| 26 | ); |
| 27 | |
| 28 | assert.deepEqual( |
| 29 | [...kv.list({ reverse: true })], |
| 30 | [ |
| 31 | ['foo', 123], |
| 32 | ['bar', 'abc'], |
| 33 | ] |
| 34 | ); |
| 35 | |
| 36 | // A new call to kv.list() invalidates any previous iterator. |
| 37 | { |
| 38 | let cursor1 = kv.list(); |
| 39 | let cursor2 = kv.list(); |
| 40 | |
| 41 | assert.throws(() => [...cursor1], { |
| 42 | name: 'Error', |
| 43 | message: |
| 44 | 'kv.list() iterator was invalidated because a new call to kv.list() was started. ' + |
| 45 | 'Only one kv.list() iterator can exist at a time.', |
| 46 | }); |
| 47 | |
| 48 | assert.deepEqual( |
| 49 | [...cursor2], |
| 50 | [ |
| 51 | ['bar', 'abc'], |
| 52 | ['foo', 123], |
| 53 | ] |
| 54 | ); |
| 55 | } |
| 56 | |
| 57 | kv.put('baz', false); |
| 58 | |
| 59 | // Try a prefix. |
| 60 | assert.deepEqual( |
| 61 | [...kv.list({ prefix: 'ba' })], |
| 62 | [ |
| 63 | ['bar', 'abc'], |
| 64 | ['baz', false], |
| 65 | ] |
| 66 | ); |
| 67 | |
| 68 | // Try a limit. |
| 69 | assert.deepEqual([...kv.list({ limit: 1 })], [['bar', 'abc']]); |
| 70 | |
| 71 | // Try a reverse limit. |
| 72 | assert.deepEqual([...kv.list({ limit: 1, reverse: true })], [['foo', 123]]); |
| 73 | |
| 74 | // Try a range. |
| 75 | assert.deepEqual( |
| 76 | [...kv.list({ start: 'b', end: 'c' })], |
| 77 | [ |
| 78 | ['bar', 'abc'], |
| 79 | ['baz', false], |
| 80 | ] |
| 81 | ); |
| 82 | |
| 83 | // End is exclusive. |
| 84 | assert.deepEqual( |
| 85 | [...kv.list({ start: 'b', end: 'baz' })], |
| 86 | [['bar', 'abc']] |
| 87 | ); |
| 88 | |
| 89 | // Start is inclusive. |
| 90 | assert.deepEqual( |
| 91 | [...kv.list({ start: 'bar', end: 'c' })], |
| 92 | [ |
| 93 | ['bar', 'abc'], |
| 94 | ['baz', false], |
| 95 | ] |
| 96 | ); |
| 97 | |
| 98 | // Except when it's not. |
| 99 | assert.deepEqual( |
| 100 | [...kv.list({ startAfter: 'bar', end: 'c' })], |
| 101 | [['baz', false]] |
| 102 | ); |
| 103 | |
| 104 | // Test delete. |
| 105 | assert.strictEqual(kv.delete('qux'), false); |
| 106 | assert.strictEqual(kv.delete('bar'), true); |
| 107 | assert.deepEqual( |
| 108 | [...kv.list()], |
| 109 | [ |
| 110 | ['baz', false], |
| 111 | ['foo', 123], |
| 112 | ] |
| 113 | ); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | export let testAutoRollBackOnCriticalError = { |
| 118 | async test(ctrl, env, ctx) { |
| 119 | await ctx.exports.MyActor.getByName('foo').run(); |
| 120 | }, |
| 121 | }; |