aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-06-23 22:16:03 +0200
committerFlorian Dold <florian.dold@gmail.com>2019-06-23 22:16:03 +0200
commit859a9e72e1e0431d729e429865d6272e2fb03ff7 (patch)
tree85c36750a947733d7fc93947557f2635799b5b0e
parenta4e4125cca8644703d7cff527a39c1a5a9842eba (diff)
idb: more tests working
-rw-r--r--packages/idb-bridge/src/BridgeIDBCursor.ts48
-rw-r--r--packages/idb-bridge/src/BridgeIDBCursorWithValue.ts11
-rw-r--r--packages/idb-bridge/src/BridgeIDBIndex.ts1
-rw-r--r--packages/idb-bridge/src/BridgeIDBTransaction.ts3
-rw-r--r--packages/idb-bridge/src/MemoryBackend.test.ts65
-rw-r--r--packages/idb-bridge/src/MemoryBackend.ts103
-rw-r--r--packages/idb-bridge/src/backend-interface.ts4
-rw-r--r--packages/idb-bridge/src/util/cmp.ts2
-rw-r--r--packages/idb-bridge/src/util/valueToKey.ts88
9 files changed, 206 insertions, 119 deletions
diff --git a/packages/idb-bridge/src/BridgeIDBCursor.ts b/packages/idb-bridge/src/BridgeIDBCursor.ts
index 8321e2a1d..ed5aa3e88 100644
--- a/packages/idb-bridge/src/BridgeIDBCursor.ts
+++ b/packages/idb-bridge/src/BridgeIDBCursor.ts
@@ -52,9 +52,9 @@ class BridgeIDBCursor {
private _gotValue: boolean = false;
private _range: CursorRange;
- private _position = undefined; // Key of previously returned record
+ private _indexPosition = undefined; // Key of previously returned record
private _objectStorePosition = undefined;
- private _keyOnly: boolean = false;
+ private _keyOnly: boolean;
private _source: CursorSource;
private _direction: BridgeIDBCursorDirection;
@@ -63,6 +63,8 @@ class BridgeIDBCursor {
private _indexName: string | undefined;
private _objectStoreName: string;
+ protected _value: Value = undefined;
+
constructor(
source: CursorSource,
objectStoreName: string,
@@ -128,7 +130,7 @@ class BridgeIDBCursor {
const recordGetRequest: RecordGetRequest = {
direction: this.direction,
indexName: this._indexName,
- lastIndexPosition: this._position,
+ lastIndexPosition: this._indexPosition,
lastObjectStorePosition: this._objectStorePosition,
limit: 1,
range: this._range,
@@ -140,15 +142,38 @@ class BridgeIDBCursor {
const { btx } = this.source._confirmActiveTransaction();
- let response = await this._backend.getRecords(
- btx,
- recordGetRequest,
- );
+ let response = await this._backend.getRecords(btx, recordGetRequest);
if (response.count === 0) {
+ console.log("cursor is returning empty result");
return null;
}
+ if (response.count !== 1) {
+ throw Error("invariant failed");
+ }
+
+ console.log("request is:", JSON.stringify(recordGetRequest));
+ console.log("get response is:", JSON.stringify(response));
+
+ if (this._indexName !== undefined) {
+ this._key = response.indexKeys![0];
+ } else {
+ this._key = response.primaryKeys![0];
+ }
+
+ this._primaryKey = response.primaryKeys![0];
+
+ if (!this._keyOnly) {
+ this._value = response.values![0];
+ }
+
+ this._gotValue = true;
+ this._objectStorePosition = structuredClone(response.primaryKeys![0]);
+ if (response.indexKeys !== undefined && response.indexKeys.length > 0) {
+ this._indexPosition = structuredClone(response.indexKeys[0]);
+ }
+
return this;
}
@@ -171,6 +196,7 @@ class BridgeIDBCursor {
if (this._effectiveObjectStore._deleted) {
throw new InvalidStateError();
}
+
if (
!(this.source instanceof BridgeIDBObjectStore) &&
this.source._deleted
@@ -232,8 +258,12 @@ class BridgeIDBCursor {
if (key !== undefined) {
key = valueToKey(key);
+ let lastKey =
+ this._indexName === undefined
+ ? this._objectStorePosition
+ : this._indexPosition;
- const cmpResult = compareKeys(key, this._position);
+ const cmpResult = compareKeys(key, lastKey);
if (
(cmpResult <= 0 &&
@@ -250,7 +280,7 @@ class BridgeIDBCursor {
}
const operation = async () => {
- this._iterate(key);
+ return this._iterate(key);
};
transaction._execRequestAsync({
diff --git a/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
index 2739a6f14..b2f23147f 100644
--- a/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
+++ b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
@@ -22,9 +22,12 @@ import {
Value,
} from "./util/types";
-class FDBCursorWithValue extends BridgeIDBCursor {
- public value: Value = undefined;
+class BridgeIDBCursorWithValue extends BridgeIDBCursor {
+ get value(): Value {
+ return this._value;
+ }
+
constructor(
source: CursorSource,
objectStoreName: string,
@@ -33,7 +36,7 @@ class FDBCursorWithValue extends BridgeIDBCursor {
direction: BridgeIDBCursorDirection,
request?: any,
) {
- super(source, objectStoreName, indexName, range, direction, request, true);
+ super(source, objectStoreName, indexName, range, direction, request, false);
}
public toString() {
@@ -41,4 +44,4 @@ class FDBCursorWithValue extends BridgeIDBCursor {
}
}
-export default FDBCursorWithValue;
+export default BridgeIDBCursorWithValue;
diff --git a/packages/idb-bridge/src/BridgeIDBIndex.ts b/packages/idb-bridge/src/BridgeIDBIndex.ts
index 6001bc051..8179be83f 100644
--- a/packages/idb-bridge/src/BridgeIDBIndex.ts
+++ b/packages/idb-bridge/src/BridgeIDBIndex.ts
@@ -19,7 +19,6 @@ import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
import BridgeIDBRequest from "./BridgeIDBRequest";
-import enforceRange from "./util/enforceRange";
import {
ConstraintError,
InvalidStateError,
diff --git a/packages/idb-bridge/src/BridgeIDBTransaction.ts b/packages/idb-bridge/src/BridgeIDBTransaction.ts
index 09f324dfa..a9f0201d3 100644
--- a/packages/idb-bridge/src/BridgeIDBTransaction.ts
+++ b/packages/idb-bridge/src/BridgeIDBTransaction.ts
@@ -215,6 +215,9 @@ class BridgeIDBTransaction extends FakeEventTarget {
let event;
try {
const result = await operation();
+ if (BridgeIDBFactory.enableTracing) {
+ console.log("TRACE: tx operation finished with success");
+ }
request.readyState = "done";
request.result = result;
request.error = undefined;
diff --git a/packages/idb-bridge/src/MemoryBackend.test.ts b/packages/idb-bridge/src/MemoryBackend.test.ts
index 213bff750..c882b9760 100644
--- a/packages/idb-bridge/src/MemoryBackend.test.ts
+++ b/packages/idb-bridge/src/MemoryBackend.test.ts
@@ -4,7 +4,8 @@ import BridgeIDBFactory from "./BridgeIDBFactory";
import BridgeIDBRequest from "./BridgeIDBRequest";
import BridgeIDBDatabase from "./BridgeIDBDatabase";
import BridgeIDBTransaction from "./BridgeIDBTransaction";
-
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBCursorWithValue from "./BridgeIDBCursorWithValue";
function promiseFromRequest(request: BridgeIDBRequest): Promise<any> {
return new Promise((resolve, reject) => {
@@ -17,11 +18,13 @@ function promiseFromRequest(request: BridgeIDBRequest): Promise<any> {
});
}
-function promiseFromTransaction(transaction: BridgeIDBTransaction): Promise<any> {
+function promiseFromTransaction(
+ transaction: BridgeIDBTransaction,
+): Promise<any> {
return new Promise((resolve, reject) => {
console.log("attaching event handlers");
transaction.oncomplete = () => {
- console.log("oncomplete was called from promise")
+ console.log("oncomplete was called from promise");
resolve();
};
transaction.onerror = () => {
@@ -51,7 +54,6 @@ test("Spec: Example 1 Part 1", async t => {
t.pass();
});
-
test("Spec: Example 1 Part 2", async t => {
const backend = new MemoryBackend();
const idb = new BridgeIDBFactory(backend);
@@ -70,21 +72,20 @@ test("Spec: Example 1 Part 2", async t => {
const tx = db.transaction("books", "readwrite");
tx.oncomplete = () => {
- console.log("oncomplete called")
+ console.log("oncomplete called");
};
const store = tx.objectStore("books");
-
- store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
- store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
- store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
+
+ store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+ store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
+ store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
await promiseFromTransaction(tx);
t.pass();
});
-
test("Spec: Example 1 Part 3", async t => {
const backend = new MemoryBackend();
const idb = new BridgeIDBFactory(backend);
@@ -102,15 +103,12 @@ test("Spec: Example 1 Part 3", async t => {
t.is(db.name, "library");
const tx = db.transaction("books", "readwrite");
- tx.oncomplete = () => {
- console.log("oncomplete called")
- };
const store = tx.objectStore("books");
-
- store.put({title: "Quarry Memories", author: "Fred", isbn: 123456});
- store.put({title: "Water Buffaloes", author: "Fred", isbn: 234567});
- store.put({title: "Bedrock Nights", author: "Barney", isbn: 345678});
+
+ store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
+ store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+ store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
await promiseFromTransaction(tx);
@@ -122,5 +120,38 @@ test("Spec: Example 1 Part 3", async t => {
t.is(result2.author, "Barney");
+ const tx3 = db.transaction(["books"], "readonly");
+ const store3 = tx3.objectStore("books");
+ const index3 = store3.index("by_author");
+ const request3 = index3.openCursor(BridgeIDBKeyRange.only("Fred"));
+
+ await promiseFromRequest(request3);
+
+ let cursor: BridgeIDBCursorWithValue;
+ cursor = request3.result as BridgeIDBCursorWithValue;
+ t.is(cursor.value.author, "Fred");
+ t.is(cursor.value.isbn, 123456);
+
+ cursor.continue();
+
+ await promiseFromRequest(request3);
+
+ cursor = request3.result as BridgeIDBCursorWithValue;
+ t.is(cursor.value.author, "Fred");
+ t.is(cursor.value.isbn, 234567);
+
+ await promiseFromTransaction(tx3);
+
+ const tx4 = db.transaction("books", "readonly");
+ const store4 = tx4.objectStore("books");
+ const request4 = store4.openCursor();
+
+ await promiseFromRequest(request4);
+
+ cursor = request4.result;
+ t.is(cursor.value.isbn, 123456);
+
+ db.close();
+
t.pass();
});
diff --git a/packages/idb-bridge/src/MemoryBackend.ts b/packages/idb-bridge/src/MemoryBackend.ts
index 831974882..75e583a89 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -132,8 +132,11 @@ function nextStoreKey<T>(
return res[1].primaryKey;
}
-
-function furthestKey(forward: boolean, key1: Key | undefined, key2: Key | undefined) {
+function furthestKey(
+ forward: boolean,
+ key1: Key | undefined,
+ key2: Key | undefined,
+) {
if (key1 === undefined) {
return key2;
}
@@ -668,6 +671,7 @@ export class MemoryBackend implements Backend {
): Promise<RecordGetResponse> {
if (this.enableTracing) {
console.log(`TRACING: getRecords`);
+ console.log("query", req);
}
const myConn = this.connectionsByTransaction[btx.transactionCookie];
if (!myConn) {
@@ -687,15 +691,15 @@ export class MemoryBackend implements Backend {
let range;
if (req.range == null || req.range === undefined) {
- range = new BridgeIDBKeyRange(null, null, true, true);
+ range = new BridgeIDBKeyRange(undefined, undefined, true, true);
} else {
range = req.range;
}
let numResults = 0;
let indexKeys: Key[] = [];
- let primaryKeys = [];
- let values = [];
+ let primaryKeys: Key[] = [];
+ let values: Value[] = [];
const forward: boolean =
req.direction === "next" || req.direction === "nextunique";
@@ -797,8 +801,6 @@ export class MemoryBackend implements Backend {
primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
}
- // FIXME: filter out duplicates
-
while (1) {
if (req.limit != 0 && numResults == req.limit) {
break;
@@ -821,6 +823,16 @@ export class MemoryBackend implements Backend {
break;
}
}
+ if (
+ unique &&
+ indexKeys.length > 0 &&
+ compareKeys(indexEntry.indexKey, indexKeys[indexKeys.length - 1]) ===
+ 0
+ ) {
+ // We only return the first result if subsequent index keys are the same.
+ continue;
+ }
+ indexKeys.push(indexEntry.indexKey);
primaryKeys.push(indexEntry.primaryKeys[primkeySubPos]);
numResults++;
primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
@@ -850,48 +862,53 @@ export class MemoryBackend implements Backend {
storePos = furthestKey(forward, req.advancePrimaryKey, storePos);
- // Advance store position if we are either still at the last returned
- // store key, or if we are currently not on a key.
- const storeEntry = storeData.get(storePos);
- if (
- !storeEntry ||
- (req.lastObjectStorePosition !== undefined &&
- compareKeys(req.lastObjectStorePosition, storeEntry.primaryKey))
- ) {
- storePos = storeData.nextHigherKey(storePos);
+ if (storePos !== null && storePos !== undefined) {
+ // Advance store position if we are either still at the last returned
+ // store key, or if we are currently not on a key.
+ const storeEntry = storeData.get(storePos);
+ if (
+ !storeEntry ||
+ (req.lastObjectStorePosition !== undefined &&
+ compareKeys(req.lastObjectStorePosition, storeEntry.primaryKey))
+ ) {
+ storePos = storeData.nextHigherKey(storePos);
+ }
+ } else {
+ storePos = forward ? storeData.minKey() : storeData.maxKey();
+ console.log("setting starting store store pos to", storePos);
}
- if (req.lastObjectStorePosition)
- while (1) {
- if (req.limit != 0 && numResults == req.limit) {
- break;
- }
- if (storePos === null || storePos === undefined) {
- break;
- }
- if (!range.includes(storePos)) {
- break;
- }
+ while (1) {
+ if (req.limit != 0 && numResults == req.limit) {
+ break;
+ }
+ if (storePos === null || storePos === undefined) {
+ break;
+ }
+ if (!range.includes(storePos)) {
+ break;
+ }
- const res = storeData.get(storePos);
+ const res = storeData.get(storePos);
- if (!res) {
- break;
- }
+ if (res === undefined) {
+ break;
+ }
- if (req.resultLevel >= ResultLevel.OnlyKeys) {
- primaryKeys.push(res.primaryKey);
- }
+ if (req.resultLevel >= ResultLevel.OnlyKeys) {
+ primaryKeys.push(structuredClone(storePos));
+ }
- if (req.resultLevel >= ResultLevel.Full) {
- values.push(res.value);
- }
- numResults++;
- storePos = nextStoreKey(forward, storeData, storePos);
+ if (req.resultLevel >= ResultLevel.Full) {
+ values.push(res);
}
+
+ numResults++;
+ storePos = nextStoreKey(forward, storeData, storePos);
+ }
}
if (this.enableTracing) {
- console.log(`TRACING: getRecords got ${numResults} results`)
+ console.log(`TRACING: getRecords got ${numResults} results`);
}
return {
count: numResults,
@@ -962,7 +979,7 @@ export class MemoryBackend implements Backend {
}
}
- insertIntoIndex(
+ private insertIntoIndex(
index: Index,
primaryKey: Key,
value: Value,
@@ -987,7 +1004,9 @@ export class MemoryBackend implements Backend {
} else {
const newIndexRecord = {
indexKey: indexKey,
- primaryKeys: [primaryKey].concat(existingRecord.primaryKeys),
+ primaryKeys: [primaryKey]
+ .concat(existingRecord.primaryKeys)
+ .sort(compareKeys),
};
index.modifiedData = indexData.with(indexKey, newIndexRecord, true);
}
diff --git a/packages/idb-bridge/src/backend-interface.ts b/packages/idb-bridge/src/backend-interface.ts
index c963b1896..ab093d9cb 100644
--- a/packages/idb-bridge/src/backend-interface.ts
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -36,9 +36,9 @@ export interface DatabaseTransaction {
}
export enum ResultLevel {
- Full,
- OnlyKeys,
OnlyCount,
+ OnlyKeys,
+ Full,
}
export interface RecordGetRequest {
diff --git a/packages/idb-bridge/src/util/cmp.ts b/packages/idb-bridge/src/util/cmp.ts
index 9d0dc99a2..078c0a9bd 100644
--- a/packages/idb-bridge/src/util/cmp.ts
+++ b/packages/idb-bridge/src/util/cmp.ts
@@ -39,6 +39,8 @@ const getType = (x: any) => {
// https://w3c.github.io/IndexedDB/#compare-two-keys
const compareKeys = (first: any, second: any): -1 | 0 | 1 => {
+ console.log("comparing keys", first, second);
+
if (second === undefined) {
throw new TypeError();
}
diff --git a/packages/idb-bridge/src/util/valueToKey.ts b/packages/idb-bridge/src/util/valueToKey.ts
index 3a9e36786..85c8c409f 100644
--- a/packages/idb-bridge/src/util/valueToKey.ts
+++ b/packages/idb-bridge/src/util/valueToKey.ts
@@ -14,57 +14,57 @@
permissions and limitations under the License.
*/
-
import { DataError } from "./errors";
import { Key } from "./types";
// https://w3c.github.io/IndexedDB/#convert-a-value-to-a-input
function valueToKey(input: any, seen?: Set<object>): Key | Key[] {
- if (typeof input === "number") {
- if (isNaN(input)) {
- throw new DataError();
- }
- return input;
- } else if (input instanceof Date) {
- const ms = input.valueOf();
- if (isNaN(ms)) {
- throw new DataError();
- }
- return new Date(ms);
- } else if (typeof input === "string") {
- return input;
- } else if (
- input instanceof ArrayBuffer ||
- (typeof ArrayBuffer !== "undefined" &&
- ArrayBuffer.isView &&
- ArrayBuffer.isView(input))
- ) {
- if (input instanceof ArrayBuffer) {
- return new Uint8Array(input).buffer;
- }
- return new Uint8Array(input.buffer).buffer;
- } else if (Array.isArray(input)) {
- if (seen === undefined) {
- seen = new Set();
- } else if (seen.has(input)) {
- throw new DataError();
- }
- seen.add(input);
+ if (typeof input === "number") {
+ if (isNaN(input)) {
+ throw new DataError();
+ }
+ return input;
+ } else if (input instanceof Date) {
+ const ms = input.valueOf();
+ if (isNaN(ms)) {
+ throw new DataError();
+ }
+ return new Date(ms);
+ } else if (typeof input === "string") {
+ return input;
+ } else if (
+ input instanceof ArrayBuffer ||
+ (typeof ArrayBuffer !== "undefined" &&
+ ArrayBuffer.isView &&
+ ArrayBuffer.isView(input))
+ ) {
+ if (input instanceof ArrayBuffer) {
+ return new Uint8Array(input).buffer;
+ }
+ return new Uint8Array(input.buffer).buffer;
+ } else if (Array.isArray(input)) {
+ if (seen === undefined) {
+ seen = new Set();
+ } else if (seen.has(input)) {
+ throw new DataError();
+ }
+ seen.add(input);
- const keys = [];
- for (let i = 0; i < input.length; i++) {
- const hop = input.hasOwnProperty(i);
- if (!hop) {
- throw new DataError();
- }
- const entry = input[i];
- const key = valueToKey(entry, seen);
- keys.push(key);
- }
- return keys;
- } else {
+ const keys = [];
+ for (let i = 0; i < input.length; i++) {
+ const hop = input.hasOwnProperty(i);
+ if (!hop) {
throw new DataError();
+ }
+ const entry = input[i];
+ const key = valueToKey(entry, seen);
+ keys.push(key);
}
-};
+ return keys;
+ } else {
+
+ throw new DataError();
+ }
+}
export default valueToKey;