UNPKG

pure-orm

Version:

A SQL Toolkit based on pure business objects passed to and from stateful data access objects

377 lines (376 loc) 18.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable max-len */ const pgp_1 = require("./pgp"); const core_1 = require("../core"); const entities_1 = require("../../test-utils/order/entities"); class SimpleModel { constructor(props) { this.id = props.id; } } class SimpleCollection { constructor({ models }) { this.models = models; } } const simpleEntities = [ { tableName: 'widget', columns: ['id'], Model: SimpleModel, Collection: SimpleCollection } ]; const makeRow = (id) => ({ 'widget#id': id }); const makeOrderRow = (id, email) => ({ 'order#id': id, 'order#email': email, 'order#browser_ip': null, 'order#browser_user_agent': null, 'order#kujo_imported_date': null, 'order#created_date': null, 'order#cancel_reason': null, 'order#cancelled_date': null, 'order#closed_date': null, 'order#processed_date': null, 'order#updated_date': null, 'order#note': null, 'order#subtotal_price': null, 'order#taxes_included': null, 'order#total_discounts': null, 'order#total_price': null, 'order#total_tax': null, 'order#total_weight': null, 'order#order_status_url': null, 'order#utm_source_id': null, 'order#utm_medium_id': null, 'order#utm_campaign': null, 'order#utm_content': null, 'order#utm_term': null }); /* -------------------------------------------------------------------------*/ /* one ---------------------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('one', () => { test('calls db.many and returns a single model', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn().mockResolvedValue([makeRow(1)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.one('SELECT ...', { id: 1 }); expect(db.many).toHaveBeenCalledWith('SELECT ...', { id: 1 }); expect(result.id).toEqual(1); })); test('throws when db.many returns empty array', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn().mockResolvedValue([]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield expect(pgp.one('SELECT ...')).rejects.toThrow(); })); test('throws when result nests into more than one model', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield expect(pgp.one('SELECT ...')).rejects.toThrow(); })); test('passes values to db.many', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn().mockResolvedValue([makeRow(5)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield pgp.one('SELECT ... WHERE id = $1', [5]); expect(db.many).toHaveBeenCalledWith('SELECT ... WHERE id = $1', [5]); })); }); /* -------------------------------------------------------------------------*/ /* oneOrNone ---------------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('oneOrNone', () => { test('returns a model when one result exists', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([makeRow(1)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.oneOrNone('SELECT ...', { id: 1 }); expect(db.any).toHaveBeenCalledWith('SELECT ...', { id: 1 }); expect(result.id).toEqual(1); })); test('returns undefined when no results', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.oneOrNone('SELECT ...'); expect(result).toBeUndefined(); })); test('throws when more than one result', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield expect(pgp.oneOrNone('SELECT ...')).rejects.toThrow(); })); }); /* -------------------------------------------------------------------------*/ /* many --------------------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('many', () => { test('returns a collection with at least one model', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([makeRow(1), makeRow(2)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.many('SELECT ...'); expect(db.any).toHaveBeenCalledWith('SELECT ...', undefined); expect(result.models.length).toEqual(2); expect(result.models[0].id).toEqual(1); expect(result.models[1].id).toEqual(2); })); test('works with a single result row', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([makeRow(42)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.many('SELECT ...'); expect(result.models.length).toEqual(1); expect(result.models[0].id).toEqual(42); })); test('throws when no results', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield expect(pgp.many('SELECT ...')).rejects.toThrow(); })); test('passes values to db.any', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { any: jest.fn().mockResolvedValue([makeRow(1)]) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield pgp.many('SELECT ... WHERE x = $1', [10]); expect(db.any).toHaveBeenCalledWith('SELECT ... WHERE x = $1', [10]); })); }); /* -------------------------------------------------------------------------*/ /* any ---------------------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('any', () => { test('returns a collection when results exist', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { result: jest.fn().mockResolvedValue({ rows: [makeRow(1), makeRow(2)], fields: [{ name: 'widget#id' }] }) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.any('SELECT ...'); expect(db.result).toHaveBeenCalledWith('SELECT ...', undefined); expect(result.models.length).toEqual(2); })); test('returns empty collection when no results', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { result: jest.fn().mockResolvedValue({ rows: [], fields: [{ name: 'widget#id' }] }) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.any('SELECT ...'); expect(result.models).toBeDefined(); expect(result.models.length).toEqual(0); })); test('extracts table name from first field name', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: entities_1.entities }); const db = { result: jest.fn().mockResolvedValue({ rows: [makeOrderRow(1, 'a@b.com')], fields: [{ name: 'order#id' }] }) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.any('SELECT ...'); expect(result.models.length).toEqual(1); expect(result.models[0].id).toEqual(1); })); test('passes values to db.result', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { result: jest.fn().mockResolvedValue({ rows: [], fields: [{ name: 'widget#id' }] }) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield pgp.any('SELECT ... WHERE x = $1', [5]); expect(db.result).toHaveBeenCalledWith('SELECT ... WHERE x = $1', [5]); })); }); /* -------------------------------------------------------------------------*/ /* none --------------------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('none', () => { test('calls db.none and resolves to null', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { none: jest.fn().mockResolvedValue(undefined) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const result = yield pgp.none('DELETE ...'); expect(db.none).toHaveBeenCalledWith('DELETE ...', undefined); expect(result).toBeNull(); })); test('passes values to db.none', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { none: jest.fn().mockResolvedValue(undefined) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield pgp.none('DELETE ... WHERE id = $(id)', { id: 7 }); expect(db.none).toHaveBeenCalledWith('DELETE ... WHERE id = $(id)', { id: 7 }); })); }); /* -------------------------------------------------------------------------*/ /* Error handling ----------------------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('error handling', () => { test('defaultErrorHandler rethrows the error', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('db error'); const db = { many: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); yield expect(pgp.one('SELECT ...')).rejects.toThrow('db error'); })); test('defaultErrorHandler calls logError for non-QueryResultError', () => __awaiter(void 0, void 0, void 0, function* () { const logError = jest.fn((err) => { throw err; }); const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('unexpected error'); const db = { many: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db, logError }); yield expect(pgp.one('SELECT ...')).rejects.toThrow('unexpected error'); expect(logError).toHaveBeenCalledWith(err); })); test('defaultErrorHandler does not call logError for QueryResultError', () => __awaiter(void 0, void 0, void 0, function* () { const logError = jest.fn((err) => { throw err; }); const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('no data'); err.name = 'QueryResultError'; const db = { many: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db, logError }); yield expect(pgp.one('SELECT ...')).rejects.toThrow('no data'); expect(logError).not.toHaveBeenCalled(); })); test('custom errorHandler overrides defaultErrorHandler', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('db failure'); const db = { many: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const customHandler = jest.fn((_err) => { throw new Error('custom handled'); }); yield expect(pgp.one('SELECT ...', undefined, customHandler)).rejects.toThrow('custom handled'); expect(customHandler).toHaveBeenCalledWith(err); })); test('custom errorHandler works for oneOrNone', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('fail'); const db = { any: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const customHandler = jest.fn((_err) => { throw new Error('caught'); }); yield expect(pgp.oneOrNone('SELECT ...', undefined, customHandler)).rejects.toThrow('caught'); expect(customHandler).toHaveBeenCalledWith(err); })); test('custom errorHandler works for many', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('fail'); const db = { any: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const customHandler = jest.fn((_err) => { throw new Error('caught'); }); yield expect(pgp.many('SELECT ...', undefined, customHandler)).rejects.toThrow('caught'); })); test('custom errorHandler works for any', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('fail'); const db = { result: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const customHandler = jest.fn((_err) => { throw new Error('caught'); }); yield expect(pgp.any('SELECT ...', undefined, customHandler)).rejects.toThrow('caught'); })); test('custom errorHandler works for none', () => __awaiter(void 0, void 0, void 0, function* () { const core = (0, core_1.createCore)({ entities: simpleEntities }); const err = new Error('fail'); const db = { none: jest.fn().mockRejectedValue(err) }; const pgp = (0, pgp_1.createForPGP)({ core, db }); const customHandler = jest.fn((_err) => { throw new Error('caught'); }); yield expect(pgp.none('DELETE ...', undefined, customHandler)).rejects.toThrow('caught'); })); }); /* -------------------------------------------------------------------------*/ /* Returned object structure -----------------------------------------------*/ /* -------------------------------------------------------------------------*/ describe('createForPGP return structure', () => { test('includes all core methods', () => { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn(), any: jest.fn(), result: jest.fn(), none: jest.fn() }; const pgp = (0, pgp_1.createForPGP)({ core, db }); expect(typeof pgp.createFromDatabase).toBe('function'); expect(typeof pgp.createOneFromDatabase).toBe('function'); expect(typeof pgp.createOneOrNoneFromDatabase).toBe('function'); expect(typeof pgp.createManyFromDatabase).toBe('function'); expect(typeof pgp.createAnyFromDatabase).toBe('function'); expect(typeof pgp.getEntityByModel).toBe('function'); expect(typeof pgp.getEntityByTableName).toBe('function'); expect(pgp.tables).toBeDefined(); }); test('includes all query methods', () => { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn(), any: jest.fn(), result: jest.fn(), none: jest.fn() }; const pgp = (0, pgp_1.createForPGP)({ core, db }); expect(typeof pgp.one).toBe('function'); expect(typeof pgp.oneOrNone).toBe('function'); expect(typeof pgp.many).toBe('function'); expect(typeof pgp.any).toBe('function'); expect(typeof pgp.none).toBe('function'); }); test('exposes db reference', () => { const core = (0, core_1.createCore)({ entities: simpleEntities }); const db = { many: jest.fn(), any: jest.fn(), result: jest.fn(), none: jest.fn() }; const pgp = (0, pgp_1.createForPGP)({ core, db }); expect(pgp.db).toBe(db); }); });