Skip to content

Commit a5226de

Browse files
committed
test(lds): add tests for LdsOptions.types and .params
1 parent b51377a commit a5226de

3 files changed

Lines changed: 106 additions & 8 deletions

File tree

packages/lds/__tests__/helpers.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as pg from "pg";
2-
import PgLogicalDecoding from "../src/pg-logical-decoding";
2+
import PgLogicalDecoding, { LdsOptions } from "../src/pg-logical-decoding";
33

44
export const DATABASE_URL = process.env.LDS_TEST_DATABASE_URL || "lds_test";
55
export { PoolClient } from "pg";
@@ -46,12 +46,14 @@ export async function withLdAndClient<T = void>(
4646
}
4747

4848
export async function withLd<T = void>(
49-
callback: (ld: PgLogicalDecoding) => Promise<T>
49+
callback: (ld: PgLogicalDecoding) => Promise<T>,
50+
options?: LdsOptions
5051
): Promise<T> {
5152
const slotName = "get_ld";
5253
const ld = new PgLogicalDecoding(DATABASE_URL, {
5354
slotName,
5455
temporary: true,
56+
...options,
5557
});
5658
await ld.createSlot();
5759
try {

packages/lds/__tests__/index.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ test("gets expected data, cleans up, doesn't receive data after cleanup", async
2828
await sub.close();
2929
expect(mockCallback).toHaveBeenCalledTimes(4);
3030
// Now run a new mutation, and expect the mockCallback not to have been called
31-
await withClient(DATABASE_URL, pgClient =>
32-
pgClient.query(
31+
await withClient(DATABASE_URL, async pgClient => {
32+
await pgClient.query(
3333
"insert into app_public.foo(name) values ('temp') returning id"
34-
)
35-
);
34+
);
35+
await sleep(100);
36+
});
3637
expect(mockCallback).toHaveBeenCalledTimes(4);
3738

3839
const {

packages/lds/__tests__/pg-logical-decoding.test.ts

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
import PgLogicalDecoding from "../src/pg-logical-decoding";
2-
import { tryDropSlot, DATABASE_URL, query, withLdAndClient } from "./helpers";
1+
import * as assert from "assert";
2+
import * as pg from "pg";
3+
import PgLogicalDecoding, { LdsOptions } from "../src/pg-logical-decoding";
4+
import {
5+
tryDropSlot,
6+
DATABASE_URL,
7+
query,
8+
withLdAndClient,
9+
withLd,
10+
} from "./helpers";
311

412
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
513

@@ -271,3 +279,90 @@ test("multiple notifications", () =>
271279
const changes3 = await ld.getChanges();
272280
expect(changes3.length).toEqual(0);
273281
}));
282+
283+
describe("parse results for", () => {
284+
async function getUpdate(options: LdsOptions) {
285+
const {
286+
rows: [{ id }],
287+
} = await query(
288+
`insert into app_public.foo(name) values ('john doe') returning id;`
289+
);
290+
return withLd(async ld => {
291+
await query(
292+
`update app_public.foo set name = 'jane doe' where id = $1;`,
293+
[id]
294+
);
295+
const rows = await ld.getChanges();
296+
const change = rows[0].data.change[0];
297+
assert.strictEqual(change.kind, "update" as const);
298+
return {
299+
id,
300+
keys: ld.changeToPk(change),
301+
data: ld.changeToRecord(change),
302+
change,
303+
};
304+
}, options);
305+
}
306+
307+
test("options without `types` should contain raw wal2json output", async () => {
308+
const { id, keys, data } = await getUpdate({});
309+
expect(keys).toEqual([id]);
310+
expect(data).toEqual({
311+
id,
312+
name: "jane doe",
313+
created_at: expect.any(String), // .stringMatching(isoDateRegex)
314+
updated_at: expect.any(String), // .stringMatching(isoDateRegex)
315+
});
316+
});
317+
test("options with `types` set to pg-types should parse output", async () => {
318+
const getTypeParser = jest.fn(pg.types.getTypeParser); // like jest.spyOn(pg.types, "getTypeParser") but not globally shared
319+
const { id, keys, data } = await getUpdate({
320+
types: { getTypeParser },
321+
});
322+
expect(keys).toEqual([id]);
323+
expect(data.name).toEqual("jane doe");
324+
expect(data.created_at).toEqual(expect.any(Date));
325+
expect(data.updated_at).toEqual(expect.any(Date));
326+
expect(getTypeParser).toHaveBeenCalledTimes(5);
327+
expect(getTypeParser.mock.calls).toEqual([
328+
// in changeToPk (id)
329+
[pg.types.builtins.INT4, "text"],
330+
// in changeToRecord (id, name, created_at, updated_at)
331+
[pg.types.builtins.INT4, "text"],
332+
[pg.types.builtins.TEXT, "text"],
333+
[pg.types.builtins.TIMESTAMPTZ, "text"],
334+
[pg.types.builtins.TIMESTAMPTZ, "text"],
335+
]);
336+
});
337+
test("options with `include-type-oids` overwritten should not have been parsed by `types`", async () => {
338+
const getTypeParser = jest.fn();
339+
const { id, keys } = await getUpdate({
340+
types: { getTypeParser },
341+
params: { "include-type-oids": "f" },
342+
});
343+
expect(keys).toEqual([String(id)]); // `numeric-data-types-as-string` still enabled
344+
expect(getTypeParser).not.toHaveBeenCalled();
345+
});
346+
test("options with `types` set to pg-types should ignore numbers in output", async () => {
347+
const { id, keys, data } = await getUpdate({
348+
types: pg.types,
349+
params: { "numeric-data-types-as-string": "f" },
350+
});
351+
expect(keys).toEqual([id]);
352+
expect(data.name).toEqual("jane doe");
353+
expect(data.created_at).toEqual(expect.any(Date));
354+
});
355+
test("options with `include-pk` and `include-types` set the change should have the respective properties", async () => {
356+
const { change } = await getUpdate({
357+
params: { "include-pk": "t", "include-types": "t" },
358+
});
359+
expect(change.columntypes).toEqual([
360+
"integer",
361+
"text",
362+
"timestamp with time zone",
363+
"timestamp with time zone",
364+
]);
365+
expect(change).toHaveProperty("pk");
366+
expect((change as any).pk.pknames).toEqual(["id"]);
367+
});
368+
});

0 commit comments

Comments
 (0)