@peerbit/indexer-tests 3.0.5 → 3.0.7

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.
@@ -0,0 +1,3 @@
1
+ import { type Indices } from "@peerbit/indexer-interface";
2
+ export declare const tieParityTests: (createIndicies: (directory?: string) => Indices | Promise<Indices>) => Mocha.Suite;
3
+ //# sourceMappingURL=tie-parity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tie-parity.d.ts","sourceRoot":"","sources":["../../src/tie-parity.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,OAAO,EAKZ,MAAM,4BAA4B,CAAC;AAiEpC,eAAO,MAAM,cAAc,GAC1B,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gBAgElE,CAAC"}
@@ -0,0 +1,214 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { field, variant } from "@dao-xyz/borsh";
36
+ import { Sort, SortDirection, getIdProperty, id, } from "@peerbit/indexer-interface";
37
+ import { expect } from "chai";
38
+ // Cross-backend SORT TIE-BREAK parity: @peerbit/indexer-simple (HashmapIndex) and
39
+ // @peerbit/indexer-sqlite3 must break ties on equal sort keys IDENTICALLY, in the
40
+ // document primary-key id's natural typed order:
41
+ // - string ids -> UTF-8 byte order (sqlite TEXT / BINARY collation)
42
+ // - integer ids -> NUMERIC order (sqlite INTEGER: 2 < 10, not "10" < "2")
43
+ // - Uint8Array ids -> raw-byte memcmp (sqlite BLOB: 0x00 < 0xff, not base64)
44
+ // and REVERSE that id order when the (single) sort field is DESC.
45
+ //
46
+ // Every document shares sortKey=1 so the whole result set is one tie group, which
47
+ // forces the id tie-break for every row. Simple historically ordered ties by
48
+ // INSERTION order (direction-independent), matching neither sqlite nor a stable
49
+ // content order; this suite locks simple == sqlite.
50
+ let StringIdDoc = (() => {
51
+ let _classDecorators = [variant("tie_str")];
52
+ let _classDescriptor;
53
+ let _classExtraInitializers = [];
54
+ let _classThis;
55
+ let _id_decorators;
56
+ let _id_initializers = [];
57
+ let _id_extraInitializers = [];
58
+ let _sortKey_decorators;
59
+ let _sortKey_initializers = [];
60
+ let _sortKey_extraInitializers = [];
61
+ var StringIdDoc = class {
62
+ static { _classThis = this; }
63
+ static {
64
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
65
+ _id_decorators = [id({ type: "string" })];
66
+ _sortKey_decorators = [field({ type: "u32" })];
67
+ __esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
68
+ __esDecorate(null, null, _sortKey_decorators, { kind: "field", name: "sortKey", static: false, private: false, access: { has: obj => "sortKey" in obj, get: obj => obj.sortKey, set: (obj, value) => { obj.sortKey = value; } }, metadata: _metadata }, _sortKey_initializers, _sortKey_extraInitializers);
69
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
70
+ StringIdDoc = _classThis = _classDescriptor.value;
71
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
72
+ __runInitializers(_classThis, _classExtraInitializers);
73
+ }
74
+ id = __runInitializers(this, _id_initializers, void 0);
75
+ sortKey = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _sortKey_initializers, void 0));
76
+ constructor(id) {
77
+ __runInitializers(this, _sortKey_extraInitializers);
78
+ this.id = id;
79
+ this.sortKey = 1;
80
+ }
81
+ };
82
+ return StringIdDoc = _classThis;
83
+ })();
84
+ let BigIntIdDoc = (() => {
85
+ let _classDecorators = [variant("tie_bigint")];
86
+ let _classDescriptor;
87
+ let _classExtraInitializers = [];
88
+ let _classThis;
89
+ let _id_decorators;
90
+ let _id_initializers = [];
91
+ let _id_extraInitializers = [];
92
+ let _sortKey_decorators;
93
+ let _sortKey_initializers = [];
94
+ let _sortKey_extraInitializers = [];
95
+ var BigIntIdDoc = class {
96
+ static { _classThis = this; }
97
+ static {
98
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
99
+ _id_decorators = [id({ type: "u64" })];
100
+ _sortKey_decorators = [field({ type: "u32" })];
101
+ __esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
102
+ __esDecorate(null, null, _sortKey_decorators, { kind: "field", name: "sortKey", static: false, private: false, access: { has: obj => "sortKey" in obj, get: obj => obj.sortKey, set: (obj, value) => { obj.sortKey = value; } }, metadata: _metadata }, _sortKey_initializers, _sortKey_extraInitializers);
103
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
104
+ BigIntIdDoc = _classThis = _classDescriptor.value;
105
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
106
+ __runInitializers(_classThis, _classExtraInitializers);
107
+ }
108
+ id = __runInitializers(this, _id_initializers, void 0);
109
+ sortKey = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _sortKey_initializers, void 0));
110
+ constructor(id) {
111
+ __runInitializers(this, _sortKey_extraInitializers);
112
+ this.id = id;
113
+ this.sortKey = 1;
114
+ }
115
+ };
116
+ return BigIntIdDoc = _classThis;
117
+ })();
118
+ let BytesIdDoc = (() => {
119
+ let _classDecorators = [variant("tie_bytes")];
120
+ let _classDescriptor;
121
+ let _classExtraInitializers = [];
122
+ let _classThis;
123
+ let _id_decorators;
124
+ let _id_initializers = [];
125
+ let _id_extraInitializers = [];
126
+ let _sortKey_decorators;
127
+ let _sortKey_initializers = [];
128
+ let _sortKey_extraInitializers = [];
129
+ var BytesIdDoc = class {
130
+ static { _classThis = this; }
131
+ static {
132
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
133
+ _id_decorators = [id({ type: Uint8Array })];
134
+ _sortKey_decorators = [field({ type: "u32" })];
135
+ __esDecorate(null, null, _id_decorators, { kind: "field", name: "id", static: false, private: false, access: { has: obj => "id" in obj, get: obj => obj.id, set: (obj, value) => { obj.id = value; } }, metadata: _metadata }, _id_initializers, _id_extraInitializers);
136
+ __esDecorate(null, null, _sortKey_decorators, { kind: "field", name: "sortKey", static: false, private: false, access: { has: obj => "sortKey" in obj, get: obj => obj.sortKey, set: (obj, value) => { obj.sortKey = value; } }, metadata: _metadata }, _sortKey_initializers, _sortKey_extraInitializers);
137
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
138
+ BytesIdDoc = _classThis = _classDescriptor.value;
139
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
140
+ __runInitializers(_classThis, _classExtraInitializers);
141
+ }
142
+ id = __runInitializers(this, _id_initializers, void 0);
143
+ sortKey = (__runInitializers(this, _id_extraInitializers), __runInitializers(this, _sortKey_initializers, void 0));
144
+ constructor(id) {
145
+ __runInitializers(this, _sortKey_extraInitializers);
146
+ this.id = id;
147
+ this.sortKey = 1;
148
+ }
149
+ };
150
+ return BytesIdDoc = _classThis;
151
+ })();
152
+ const fmt = (value) => value instanceof Uint8Array
153
+ ? Array.from(value)
154
+ .map((x) => x.toString(16).padStart(2, "0"))
155
+ .join("")
156
+ : String(value);
157
+ export const tieParityTests = (createIndicies) => {
158
+ return describe("sort tie-break parity", () => {
159
+ let indices;
160
+ afterEach(async () => {
161
+ await indices?.stop?.();
162
+ await indices?.drop?.();
163
+ indices = undefined;
164
+ });
165
+ const collect = async (schema, docs) => {
166
+ indices = await createIndicies();
167
+ await indices.start();
168
+ const store = await indices.init({
169
+ schema,
170
+ indexBy: getIdProperty(schema) || ["id"],
171
+ });
172
+ for (const doc of docs) {
173
+ await store.put(doc);
174
+ }
175
+ const order = async (direction) => {
176
+ const results = await store
177
+ .iterate({
178
+ query: [],
179
+ sort: [new Sort({ key: "sortKey", direction })],
180
+ })
181
+ .all();
182
+ return results.map((r) => fmt(r.value.id));
183
+ };
184
+ const asc = await order(SortDirection.ASC);
185
+ const desc = await order(SortDirection.DESC);
186
+ await store.stop?.();
187
+ await indices.stop?.();
188
+ await indices.drop?.();
189
+ indices = undefined;
190
+ return { asc, desc };
191
+ };
192
+ it("orders string-id ties by UTF-8 bytes, both directions", async () => {
193
+ const docs = ["c", "a", "b", "aa", "A", "Z"].map((s) => new StringIdDoc(s));
194
+ const { asc, desc } = await collect(StringIdDoc, docs);
195
+ // UTF-8 byte order: uppercase (A, Z) before lowercase (a, aa, b, c).
196
+ expect(asc).to.deep.equal(["A", "Z", "a", "aa", "b", "c"]);
197
+ expect(desc).to.deep.equal(["c", "b", "aa", "a", "Z", "A"]);
198
+ });
199
+ it("orders bigint-id ties numerically, both directions", async () => {
200
+ const docs = [10n, 2n, 21n, 9n, 100n, 300n].map((n) => new BigIntIdDoc(n));
201
+ const { asc, desc } = await collect(BigIntIdDoc, docs);
202
+ expect(asc).to.deep.equal(["2", "9", "10", "21", "100", "300"]);
203
+ expect(desc).to.deep.equal(["300", "100", "21", "10", "9", "2"]);
204
+ });
205
+ it("orders Uint8Array-id ties by raw bytes, both directions", async () => {
206
+ const docs = [[0xff], [0x00], [0x0a], [0x3e], [0xf8], [0xfb]].map((b) => new BytesIdDoc(new Uint8Array(b)));
207
+ const { asc, desc } = await collect(BytesIdDoc, docs);
208
+ // Raw-byte memcmp: 0x00 < 0x0a < 0x3e < 0xf8 < 0xfb < 0xff.
209
+ expect(asc).to.deep.equal(["00", "0a", "3e", "f8", "fb", "ff"]);
210
+ expect(desc).to.deep.equal(["ff", "fb", "f8", "3e", "0a", "00"]);
211
+ });
212
+ });
213
+ };
214
+ //# sourceMappingURL=tie-parity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tie-parity.js","sourceRoot":"","sources":["../../src/tie-parity.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/indexer-tests",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "Tests document store index engines",
5
5
  "sideEffects": false,
6
6
  "type": "module",
@@ -52,17 +52,17 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "author": "dao.xyz",
55
+ "author": "Peerbit contributors",
56
56
  "license": "MIT",
57
57
  "dependencies": {
58
58
  "@dao-xyz/borsh": "^6.0.0",
59
+ "benchmark": "^2.1.4",
59
60
  "libsodium-wrappers": "0.7.15",
60
61
  "uint8arrays": "^5.1.0",
61
- "benchmark": "^2.1.4",
62
- "uuid": "^10.0.0",
63
- "@peerbit/crypto": "3.1.2",
64
- "@peerbit/indexer-interface": "3.0.5",
65
- "@peerbit/time": "3.0.0"
62
+ "uuid": "^11.1.1",
63
+ "@peerbit/crypto": "3.1.3",
64
+ "@peerbit/time": "3.0.1",
65
+ "@peerbit/indexer-interface": "3.0.7"
66
66
  },
67
67
  "scripts": {
68
68
  "clean": "aegir clean",
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { tests } from "./tests.js";
2
+ export { tieParityTests } from "./tie-parity.js";
2
3
  export { benchmarks } from "./benchmarks.js";
package/src/tests.ts CHANGED
@@ -4048,6 +4048,273 @@ export const tests = (
4048
4048
  expect(await iterator.pending()).to.eq(0);
4049
4049
  });
4050
4050
 
4051
+ it("all after partial next returns only remaining sorted results", async () => {
4052
+ await put(0);
4053
+ await put(1);
4054
+ await put(2);
4055
+
4056
+ const iterator = store.iterate({
4057
+ query: [],
4058
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4059
+ });
4060
+
4061
+ expect((await iterator.next(1)).map((x) => x.value.name)).to.deep.equal(
4062
+ ["0"],
4063
+ );
4064
+ expect((await iterator.all()).map((x) => x.value.name)).to.deep.equal([
4065
+ "1",
4066
+ "2",
4067
+ ]);
4068
+ expect(iterator.done()).to.be.true;
4069
+ });
4070
+
4071
+ it("deleting an already yielded sorted result does not skip the next result", async () => {
4072
+ await put(0);
4073
+ await put(1);
4074
+ await put(2);
4075
+
4076
+ const iterator = store.iterate({
4077
+ query: [],
4078
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4079
+ });
4080
+
4081
+ expect((await iterator.next(1)).map((x) => x.value.name)).to.deep.equal(
4082
+ ["0"],
4083
+ );
4084
+ await store.del({
4085
+ query: new StringMatch({
4086
+ key: "id",
4087
+ value: "0",
4088
+ method: StringMatchMethod.exact,
4089
+ caseInsensitive: false,
4090
+ }),
4091
+ });
4092
+
4093
+ expect((await iterator.next(2)).map((x) => x.value.name)).to.deep.equal(
4094
+ ["1", "2"],
4095
+ );
4096
+ expect(iterator.done()).to.be.true;
4097
+ });
4098
+
4099
+ it("inserting before an already yielded sorted result does not duplicate it", async () => {
4100
+ await put(1);
4101
+ await put(2);
4102
+
4103
+ const iterator = store.iterate({
4104
+ query: [],
4105
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4106
+ });
4107
+
4108
+ expect((await iterator.next(1)).map((x) => x.value.name)).to.deep.equal(
4109
+ ["1"],
4110
+ );
4111
+ await put(0);
4112
+
4113
+ const rest = (await iterator.next(3)).map((x) => x.value.name);
4114
+ expect(new Set(rest).size).to.equal(rest.length);
4115
+ expect(rest).to.not.include("1");
4116
+ expect(rest).to.include("2");
4117
+ });
4118
+
4119
+ it("keeps a mutated iterator open while unseen sorted results remain", async () => {
4120
+ await put(0);
4121
+ await put(2);
4122
+
4123
+ const iterator = store.iterate({
4124
+ query: [],
4125
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4126
+ });
4127
+
4128
+ expect((await iterator.next(1)).map((x) => x.value.name)).to.deep.equal(
4129
+ ["0"],
4130
+ );
4131
+ await put(1);
4132
+
4133
+ if (properties.iteratorsMutable) {
4134
+ expect(
4135
+ (await iterator.next(1)).map((x) => x.value.name),
4136
+ ).to.deep.equal(["1"]);
4137
+ expect(iterator.done()).to.be.false;
4138
+ expect(await iterator.pending()).to.equal(1);
4139
+ expect(
4140
+ (await iterator.next(1)).map((x) => x.value.name),
4141
+ ).to.deep.equal(["2"]);
4142
+ } else {
4143
+ expect(
4144
+ (await iterator.next(1)).map((x) => x.value.name),
4145
+ ).to.deep.equal(["2"]);
4146
+ }
4147
+ expect(iterator.done()).to.be.true;
4148
+ });
4149
+
4150
+ it("next Infinity sees inserts after an initially empty page", async () => {
4151
+ const iterator = store.iterate({
4152
+ query: [],
4153
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4154
+ });
4155
+
4156
+ expect(await iterator.next(1)).to.deep.equal([]);
4157
+ await put(0);
4158
+
4159
+ const rest = (await iterator.next(Infinity)).map((x) => x.value.name);
4160
+ expect(rest).to.deep.equal(properties.iteratorsMutable ? ["0"] : []);
4161
+ });
4162
+
4163
+ it("keeps an explicitly closed iterator terminal across mutations", async () => {
4164
+ await put(0);
4165
+ const iterator = store.iterate({
4166
+ query: [],
4167
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4168
+ });
4169
+
4170
+ await iterator.close();
4171
+ await put(1);
4172
+
4173
+ expect(await iterator.next(1)).to.deep.equal([]);
4174
+ expect(await iterator.all()).to.deep.equal([]);
4175
+ expect(await iterator.pending()).to.equal(0);
4176
+ expect(iterator.done()).to.be.true;
4177
+ });
4178
+
4179
+ it("can continue a mutable iterator after all and a later insert", async () => {
4180
+ await put(0);
4181
+ const iterator = store.iterate({
4182
+ query: [],
4183
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4184
+ });
4185
+
4186
+ expect((await iterator.all()).map((x) => x.value.name)).to.deep.equal([
4187
+ "0",
4188
+ ]);
4189
+ await put(1);
4190
+
4191
+ expect((await iterator.next(1)).map((x) => x.value.name)).to.deep.equal(
4192
+ properties.iteratorsMutable ? ["1"] : [],
4193
+ );
4194
+ });
4195
+
4196
+ it("refreshes pending after an exhausted mutable iterator mutates", async () => {
4197
+ const iterator = store.iterate({
4198
+ query: [],
4199
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4200
+ });
4201
+
4202
+ expect(await iterator.next(1)).to.deep.equal([]);
4203
+ await put(0);
4204
+
4205
+ expect(await iterator.pending()).to.equal(
4206
+ properties.iteratorsMutable ? 1 : 0,
4207
+ );
4208
+ if (properties.iteratorsMutable) {
4209
+ expect(iterator.done()).to.be.false;
4210
+ expect(
4211
+ (await iterator.next(1)).map((x) => x.value.name),
4212
+ ).to.deep.equal(["0"]);
4213
+ }
4214
+ });
4215
+
4216
+ it("can mark live results delivered outside the iterator as yielded", async () => {
4217
+ if (!properties.iteratorsMutable) {
4218
+ return;
4219
+ }
4220
+
4221
+ await put(0);
4222
+ await put(1);
4223
+ const iterator = store.iterate({
4224
+ query: [],
4225
+ sort: [new Sort({ direction: SortDirection.ASC, key: "name" })],
4226
+ });
4227
+
4228
+ expect((await iterator.next(1)).map((x) => x.value.id)).to.deep.equal([
4229
+ "0",
4230
+ ]);
4231
+ await put(2);
4232
+ expect(iterator.markYielded).to.be.a("function");
4233
+ await iterator.markYielded!([toId("2")]);
4234
+
4235
+ expect(await iterator.pending()).to.equal(1);
4236
+ expect((await iterator.next(2)).map((x) => x.value.id)).to.deep.equal([
4237
+ "1",
4238
+ ]);
4239
+ expect(await iterator.pending()).to.equal(0);
4240
+ expect(iterator.done()).to.be.true;
4241
+ });
4242
+
4243
+ it("tracks filtered updates without duplicates", async () => {
4244
+ if (!properties.iteratorsMutable) {
4245
+ return;
4246
+ }
4247
+
4248
+ await put(0, { name: "match" });
4249
+ await put(1, { name: "miss" });
4250
+ await put(2, { name: "match" });
4251
+ const iterator = store.iterate({
4252
+ query: new StringMatch({
4253
+ key: "name",
4254
+ value: "match",
4255
+ method: StringMatchMethod.exact,
4256
+ caseInsensitive: false,
4257
+ }),
4258
+ sort: [new Sort({ direction: SortDirection.ASC, key: "number" })],
4259
+ });
4260
+
4261
+ expect((await iterator.next(1)).map((x) => x.value.id)).to.deep.equal([
4262
+ "0",
4263
+ ]);
4264
+ await put(1, { name: "match" });
4265
+ await put(2, { name: "miss" });
4266
+ await put(0, { name: "match" });
4267
+ await put(3, { name: "match" });
4268
+
4269
+ expect((await iterator.all()).map((x) => x.value.id)).to.deep.equal([
4270
+ "1",
4271
+ "3",
4272
+ ]);
4273
+ });
4274
+
4275
+ it("rescans beyond 128 yielded rows after a mutation", async () => {
4276
+ if (!properties.iteratorsMutable) {
4277
+ return;
4278
+ }
4279
+
4280
+ for (let i = 0; i < 140; i++) {
4281
+ await put(i);
4282
+ }
4283
+ const iterator = store.iterate({
4284
+ query: [],
4285
+ sort: [new Sort({ direction: SortDirection.ASC, key: "number" })],
4286
+ });
4287
+ expect(await iterator.next(130)).to.have.length(130);
4288
+
4289
+ await store.del({
4290
+ query: new StringMatch({
4291
+ key: "id",
4292
+ value: "0",
4293
+ method: StringMatchMethod.exact,
4294
+ caseInsensitive: false,
4295
+ }),
4296
+ });
4297
+ await put(200);
4298
+
4299
+ expect((await iterator.next(1)).map((x) => x.value.id)).to.deep.equal([
4300
+ "130",
4301
+ ]);
4302
+ const remaining = (await iterator.all()).map((x) => x.value.id);
4303
+ expect(remaining).to.deep.equal([
4304
+ "131",
4305
+ "132",
4306
+ "133",
4307
+ "134",
4308
+ "135",
4309
+ "136",
4310
+ "137",
4311
+ "138",
4312
+ "139",
4313
+ "200",
4314
+ ]);
4315
+ expect(new Set(remaining).size).to.equal(remaining.length);
4316
+ });
4317
+
4051
4318
  describe("close", () => {
4052
4319
  it("by invoking close()", async () => {
4053
4320
  await put(0);
@@ -0,0 +1,138 @@
1
+ import { field, variant } from "@dao-xyz/borsh";
2
+ import {
3
+ type Indices,
4
+ Sort,
5
+ SortDirection,
6
+ getIdProperty,
7
+ id,
8
+ } from "@peerbit/indexer-interface";
9
+ import { expect } from "chai";
10
+
11
+ // Cross-backend SORT TIE-BREAK parity: @peerbit/indexer-simple (HashmapIndex) and
12
+ // @peerbit/indexer-sqlite3 must break ties on equal sort keys IDENTICALLY, in the
13
+ // document primary-key id's natural typed order:
14
+ // - string ids -> UTF-8 byte order (sqlite TEXT / BINARY collation)
15
+ // - integer ids -> NUMERIC order (sqlite INTEGER: 2 < 10, not "10" < "2")
16
+ // - Uint8Array ids -> raw-byte memcmp (sqlite BLOB: 0x00 < 0xff, not base64)
17
+ // and REVERSE that id order when the (single) sort field is DESC.
18
+ //
19
+ // Every document shares sortKey=1 so the whole result set is one tie group, which
20
+ // forces the id tie-break for every row. Simple historically ordered ties by
21
+ // INSERTION order (direction-independent), matching neither sqlite nor a stable
22
+ // content order; this suite locks simple == sqlite.
23
+
24
+ @variant("tie_str")
25
+ class StringIdDoc {
26
+ @id({ type: "string" })
27
+ id: string;
28
+
29
+ @field({ type: "u32" })
30
+ sortKey: number;
31
+
32
+ constructor(id: string) {
33
+ this.id = id;
34
+ this.sortKey = 1;
35
+ }
36
+ }
37
+
38
+ @variant("tie_bigint")
39
+ class BigIntIdDoc {
40
+ @id({ type: "u64" })
41
+ id: bigint;
42
+
43
+ @field({ type: "u32" })
44
+ sortKey: number;
45
+
46
+ constructor(id: bigint) {
47
+ this.id = id;
48
+ this.sortKey = 1;
49
+ }
50
+ }
51
+
52
+ @variant("tie_bytes")
53
+ class BytesIdDoc {
54
+ @id({ type: Uint8Array })
55
+ id: Uint8Array;
56
+
57
+ @field({ type: "u32" })
58
+ sortKey: number;
59
+
60
+ constructor(id: Uint8Array) {
61
+ this.id = id;
62
+ this.sortKey = 1;
63
+ }
64
+ }
65
+
66
+ const fmt = (value: string | number | bigint | Uint8Array): string =>
67
+ value instanceof Uint8Array
68
+ ? Array.from(value)
69
+ .map((x) => x.toString(16).padStart(2, "0"))
70
+ .join("")
71
+ : String(value);
72
+
73
+ export const tieParityTests = (
74
+ createIndicies: (directory?: string) => Indices | Promise<Indices>,
75
+ ) => {
76
+ return describe("sort tie-break parity", () => {
77
+ let indices: Indices | undefined;
78
+
79
+ afterEach(async () => {
80
+ await indices?.stop?.();
81
+ await indices?.drop?.();
82
+ indices = undefined;
83
+ });
84
+
85
+ const collect = async (schema: any, docs: any[]) => {
86
+ indices = await createIndicies();
87
+ await indices.start();
88
+ const store = await indices.init({
89
+ schema,
90
+ indexBy: getIdProperty(schema) || ["id"],
91
+ });
92
+ for (const doc of docs) {
93
+ await store.put(doc);
94
+ }
95
+ const order = async (direction: SortDirection) => {
96
+ const results = await store
97
+ .iterate({
98
+ query: [],
99
+ sort: [new Sort({ key: "sortKey", direction })],
100
+ })
101
+ .all();
102
+ return results.map((r) => fmt(r.value.id));
103
+ };
104
+ const asc = await order(SortDirection.ASC);
105
+ const desc = await order(SortDirection.DESC);
106
+ await store.stop?.();
107
+ await indices.stop?.();
108
+ await indices.drop?.();
109
+ indices = undefined;
110
+ return { asc, desc };
111
+ };
112
+
113
+ it("orders string-id ties by UTF-8 bytes, both directions", async () => {
114
+ const docs = ["c", "a", "b", "aa", "A", "Z"].map((s) => new StringIdDoc(s));
115
+ const { asc, desc } = await collect(StringIdDoc, docs);
116
+ // UTF-8 byte order: uppercase (A, Z) before lowercase (a, aa, b, c).
117
+ expect(asc).to.deep.equal(["A", "Z", "a", "aa", "b", "c"]);
118
+ expect(desc).to.deep.equal(["c", "b", "aa", "a", "Z", "A"]);
119
+ });
120
+
121
+ it("orders bigint-id ties numerically, both directions", async () => {
122
+ const docs = [10n, 2n, 21n, 9n, 100n, 300n].map((n) => new BigIntIdDoc(n));
123
+ const { asc, desc } = await collect(BigIntIdDoc, docs);
124
+ expect(asc).to.deep.equal(["2", "9", "10", "21", "100", "300"]);
125
+ expect(desc).to.deep.equal(["300", "100", "21", "10", "9", "2"]);
126
+ });
127
+
128
+ it("orders Uint8Array-id ties by raw bytes, both directions", async () => {
129
+ const docs = [[0xff], [0x00], [0x0a], [0x3e], [0xf8], [0xfb]].map(
130
+ (b) => new BytesIdDoc(new Uint8Array(b)),
131
+ );
132
+ const { asc, desc } = await collect(BytesIdDoc, docs);
133
+ // Raw-byte memcmp: 0x00 < 0x0a < 0x3e < 0xf8 < 0xfb < 0xff.
134
+ expect(asc).to.deep.equal(["00", "0a", "3e", "f8", "fb", "ff"]);
135
+ expect(desc).to.deep.equal(["ff", "fb", "f8", "3e", "0a", "00"]);
136
+ });
137
+ });
138
+ };