|
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"; |
3 | 11 |
|
4 | 12 | const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); |
5 | 13 |
|
@@ -271,3 +279,90 @@ test("multiple notifications", () => |
271 | 279 | const changes3 = await ld.getChanges(); |
272 | 280 | expect(changes3.length).toEqual(0); |
273 | 281 | })); |
| 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