Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
fix: correct pointer values
Browse files Browse the repository at this point in the history
fix: don't stringify primitives prematurely
tests: improved tests for new async stuff
  • Loading branch information
helmturner committed Jan 4, 2024
1 parent 88db45d commit f367de1
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 192 deletions.
4 changes: 2 additions & 2 deletions src/async/asyncTypes2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const ChunkTypes = {
ERROR: "ERROR",
HEAD: "HEAD",
LEAF: "LEAF",
REFERENCE: "REFERENCE",
REF: "REF",
TAIL: "TAIL",
} as const;

Expand Down Expand Up @@ -114,7 +114,7 @@ export type TsonAsyncHeadTuple = [
];

export type TsonAsyncReferenceTuple = [
ChunkType: ChunkTypes["REFERENCE"],
ChunkType: ChunkTypes["REF"],
Header: TsonAsyncTupleHeader,
OriginalNodeId: `${TsonNonce}${number}`,
];
Expand Down
222 changes: 172 additions & 50 deletions src/async/handlers/tsonPromise2.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import { expect, test } from "vitest";

import { TsonType } from "../../index.js";
import { createPromise } from "../../internals/testUtils.js";
import { ChunkTypes, TsonStatus } from "../asyncTypes2.js";
import { createPromise, expectSequence } from "../../internals/testUtils.js";
import { ChunkTypes, TsonAsyncTuple, TsonStatus } from "../asyncTypes2.js";
import { createTsonSerializeAsync } from "../serializeAsync2.js";
import { tsonPromise } from "./tsonPromise2.js";

const nonce = "__tson";
const anyId = expect.stringMatching(`^${nonce}[0-9]+$`);
const idOf = (id: TsonAsyncTuple | number | string) => {
if (Array.isArray(id)) {
return id[1][0];
}

return `${nonce}${id}`;
};

const tsonError: TsonType<Error, { message: string }> = {
deserialize: (v) => {
const err = new Error(v.message);
Expand All @@ -19,8 +29,6 @@ const tsonError: TsonType<Error, { message: string }> = {
};

test("serialize promise", async () => {
const nonce = "__tson";

const serialize = createTsonSerializeAsync({
nonce: () => nonce,
types: [tsonPromise],
Expand All @@ -29,73 +37,102 @@ test("serialize promise", async () => {
const promise = Promise.resolve(42);
const iterator = serialize(promise);

const values = [];
const chunks: TsonAsyncTuple[] = [];
for await (const value of iterator) {
values.push(value);
chunks.push(value);
}

const promiseId = `${nonce}0`;
const arrayId = `${nonce}1`;
const heads = chunks.filter((chunk) => chunk[0] === ChunkTypes.HEAD);
const tails = chunks.filter((chunk) => chunk[0] === ChunkTypes.TAIL);
const leaves = chunks.filter((chunk) => chunk[0] === ChunkTypes.LEAF);

expect(values).toEqual([
[ChunkTypes.HEAD, [promiseId, nonce, null], tsonPromise.key],
[ChunkTypes.HEAD, [arrayId, promiseId, null]],
[ChunkTypes.LEAF, [`${nonce}2`, arrayId, 0], 0],
[ChunkTypes.TAIL, [`${nonce}3`, promiseId, null], TsonStatus.OK],
[ChunkTypes.LEAF, [`${nonce}4`, arrayId, 1], 42],
[ChunkTypes.TAIL, [`${nonce}5`, arrayId, null], TsonStatus.OK],
]);
expect(chunks.length).toBe(6);
expect(heads).toHaveLength(2);
expect(tails).toHaveLength(2);
expect(leaves).toHaveLength(2);

expectSequence(chunks)
.toHaveAll(heads)
.beforeAll([...leaves, ...tails]);

heads.forEach((_, i) => {
expectSequence(chunks).toHave(heads[i]!).beforeAll([tails[i]!, leaves[i]!]);
expectSequence(chunks).toHave(tails[i]!).afterAll([heads[i]!, leaves[i]!]);
});
});

test("serialize promise that returns a promise", async () => {
const nonce = "__tson";
const serialize = createTsonSerializeAsync({
nonce: () => nonce,
types: [tsonPromise],
});

const expected = 42;

const obj = {
promise: createPromise(() => {
return {
anotherPromise: createPromise(() => {
return 42;
return expected;
}),
};
}),
};

const iterator = serialize(obj);
const values = [];
const chunks: TsonAsyncTuple[] = [];

for await (const value of iterator) {
values.push(value);
chunks.push(value);
}

expect(values).toEqual([
/*
TODO: The parent IDs are wrong here. They're not correct in the implementation,
TODO: either, and I don't know what they should be yet.
*/
[ChunkTypes.HEAD, [`${nonce}0`, `${nonce}`, null]],
[ChunkTypes.HEAD, [`${nonce}1`, `${nonce}0`, "promise"], "Promise"],
[ChunkTypes.TAIL, [`${nonce}2`, `${nonce}0`, null], 200],
[ChunkTypes.HEAD, [`${nonce}3`, `${nonce}1`, null]],
[ChunkTypes.TAIL, [`${nonce}4`, `${nonce}1`, null], 200],
[ChunkTypes.LEAF, [`${nonce}5`, `${nonce}3`, 0], 0],
[ChunkTypes.HEAD, [`${nonce}6`, `${nonce}3`, 1]],
[ChunkTypes.HEAD, [`${nonce}7`, `${nonce}6`, "anotherPromise"], "Promise"],
[ChunkTypes.TAIL, [`${nonce}8`, `${nonce}6`, null], 200],
[ChunkTypes.TAIL, [`${nonce}9`, `${nonce}7`, null], 200],
[ChunkTypes.HEAD, [`${nonce}10`, `${nonce}6`, null]],
[ChunkTypes.TAIL, [`${nonce}11`, `${nonce}9`, null], 200],
[ChunkTypes.LEAF, [`${nonce}12`, `${nonce}12`, 0], 0],
[ChunkTypes.LEAF, [`${nonce}13`, `${nonce}11`, 1], 42],
[ChunkTypes.TAIL, [`${nonce}14`, `${nonce}11`, null], 200],
const heads = chunks.filter((chunk) => chunk[0] === ChunkTypes.HEAD);
const tails = chunks.filter((chunk) => chunk[0] === ChunkTypes.TAIL);
const leaves = chunks.filter((chunk) => chunk[0] === ChunkTypes.LEAF);

expect(chunks).toHaveLength(15);
expect(heads).toHaveLength(6);
expect(leaves).toHaveLength(3);
expect(tails).toHaveLength(6);

heads.forEach((_, i) => {
expect(tails.filter((v) => v[1][1] === heads[i]![1][0])).toHaveLength(1);
expectSequence(chunks)
.toHave(heads[i]!)
.beforeAll(
[...tails, ...leaves].filter((v) => v[1][1] === heads[i]![1][0]),
);
expectSequence(chunks)
.toHave(tails[i]!)
.afterAll(
[...heads, ...leaves].filter((v) => v[1][1] === tails[i]![1][0]),
);
});

expect(heads[0]![1][0]).toBe(idOf(0));

expect(heads).toHaveLength(6);
expect(leaves).toHaveLength(3);
expect(tails).toHaveLength(6);

expect(heads[0]).toStrictEqual([ChunkTypes.HEAD, [idOf(0), nonce, null]]);
expect(heads[1]).toStrictEqual([
ChunkTypes.HEAD,
[anyId, idOf(0), "promise"],
tsonPromise.key,
]);

expect(heads[2]).toStrictEqual([ChunkTypes.HEAD, [anyId, anyId, null]]);
expect(heads[3]).toStrictEqual([ChunkTypes.HEAD, [anyId, anyId, 1]]);
expect(heads[4]).toStrictEqual([
ChunkTypes.HEAD,
[anyId, anyId, "anotherPromise"],
tsonPromise.key,
]);
expect(heads[5]).toStrictEqual([ChunkTypes.HEAD, [anyId, anyId, null]]);
});

test("promise that rejects", async () => {
const nonce = "__tson";
const serialize = createTsonSerializeAsync({
nonce: () => nonce,
types: [tsonPromise, tsonError],
Expand All @@ -104,19 +141,104 @@ test("promise that rejects", async () => {
const promise = Promise.reject(new Error("foo"));
const iterator = serialize(promise);

const values = [];
const chunks: TsonAsyncTuple[] = [];
const expected = { message: "foo" };

for await (const value of iterator) {
values.push(value);
chunks.push(value);
}

expect(values).toEqual([
[ChunkTypes.HEAD, [`${nonce}0`, `${nonce}`, null], "Promise"],
[ChunkTypes.HEAD, [`${nonce}1`, `${nonce}0`, null]],
[ChunkTypes.LEAF, [`${nonce}2`, `${nonce}1`, 0], 1],
[ChunkTypes.TAIL, [`${nonce}3`, `${nonce}0`, null], 200],
[ChunkTypes.LEAF, [`${nonce}5`, `${nonce}1`, 1], expected, "Error"],
[ChunkTypes.TAIL, [`${nonce}6`, `${nonce}1`, null], 200],
expect(chunks.length).toBe(6);

const heads = chunks.filter((chunk) => chunk[0] === ChunkTypes.HEAD);
const tails = chunks.filter((chunk) => chunk[0] === ChunkTypes.TAIL);
const leaves = chunks.filter((chunk) => chunk[0] === ChunkTypes.LEAF);

expectSequence(chunks)
.toHaveAll(heads)
.beforeAll([...leaves, ...tails]);

heads.forEach((_, i) => {
expect(tails.filter((v) => v[1][1] === heads[i]![1][0])).toHaveLength(1);
expectSequence(chunks)
.toHave(heads[i]!)
.beforeAll(
[...tails, ...leaves].filter((v) => v[1][1] === heads[i]![1][0]),
);
expectSequence(chunks)
.toHave(tails[i]!)
.afterAll(
[...heads, ...leaves].filter((v) => v[1][1] === tails[i]![1][0]),
);
});

expect(heads[0]![1][0]).toBe(idOf(0));

expect(heads).toHaveLength(2);
expect(tails).toHaveLength(2);
expect(leaves).toHaveLength(2);

expect(heads[0]).toStrictEqual([
ChunkTypes.HEAD,
[idOf(0), nonce, null],
tsonPromise.key,
]);

expect(heads[1]).toEqual([ChunkTypes.HEAD, [anyId, idOf(0), null]]);
expect(leaves[0]).toEqual([ChunkTypes.LEAF, [anyId, anyId, 0], 1]);
expect(leaves[1]).toEqual([
ChunkTypes.LEAF,
[anyId, anyId, 1],
expected,
tsonError.key,
]);
});

test("racing promises", async () => {
const serialize = createTsonSerializeAsync({
nonce: () => nonce,
types: [tsonPromise],
});

const iterator = serialize({
promise: createPromise(() => {
return {
promise1: createPromise(() => {
return 42;
}, Math.random() * 100),
promise2: createPromise(() => {
return 43;
}, Math.random() * 100),
};
}),
});

const chunks: TsonAsyncTuple[] = [];

for await (const value of iterator) {
chunks.push(value);
}

const heads = chunks.filter((chunk) => chunk[0] === ChunkTypes.HEAD);
const leaves = chunks.filter((chunk) => chunk[0] === ChunkTypes.LEAF);
const tails = chunks.filter((chunk) => chunk[0] === ChunkTypes.TAIL);

expect(chunks).toHaveLength(21);
expect(heads).toHaveLength(8);
expect(leaves).toHaveLength(5);
expect(tails).toHaveLength(8);

heads.forEach((_, i) => {
expect(tails.filter((v) => v[1][1] === heads[i]![1][0])).toHaveLength(1);
expectSequence(chunks)
.toHave(heads[i]!)
.beforeAll(
[...tails, ...leaves].filter((v) => v[1][1] === heads[i]![1][0]),
);
expectSequence(chunks)
.toHave(tails[i]!)
.afterAll(
[...heads, ...leaves].filter((v) => v[1][1] === tails[i]![1][0]),
);
});
});
Loading

0 comments on commit f367de1

Please sign in to comment.