pure-orm
Version:
A SQL Toolkit based on pure business objects passed to and from stateful data access objects
553 lines (552 loc) • 25.5 kB
JavaScript
"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 index_1 = require("./index");
const entities_1 = require("../test-utils/order/entities");
const order_1 = require("../test-utils/order/models/order");
const mockPgpDb = (overrides = {}) => (Object.assign({ $config: { pgp: true }, many: jest.fn(), any: jest.fn(), result: jest.fn(), none: jest.fn() }, overrides));
/* -------------------------------------------------------------------------*/
/* Helper Utility Functions ------------------------------------------------*/
/* -------------------------------------------------------------------------*/
describe('getSqlInsertParts', () => {
test('generates correct insert parts for a model with defined values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'test@test.com',
subtotalPrice: 100,
utmSourceId: 10
});
expect(orm.getSqlInsertParts(order)).toEqual({
columns: '"id", "email", "subtotal_price", "utm_source_id"',
values: [1, 'test@test.com', 100, 10],
valuesVar: ['$1', '$2', '$3', '$4']
});
});
test('filters out undefined properties', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 5 });
const result = orm.getSqlInsertParts(order);
expect(result.columns).toEqual('"id"');
expect(result.values).toEqual([5]);
expect(result.valuesVar).toEqual(['$1']);
});
test('includes all defined properties', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'a@b.com',
browserIp: '1.2.3.4',
subtotalPrice: 50,
totalPrice: 55
});
const result = orm.getSqlInsertParts(order);
expect(result.values).toContain(1);
expect(result.values).toContain('a@b.com');
expect(result.values).toContain(50);
expect(result.values).toContain(55);
expect(result.values.length).toEqual(result.valuesVar.length);
});
});
describe('getSqlUpdateParts', () => {
test('generates correct update parts', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'test@test.com',
subtotalPrice: 100,
utmSourceId: 10
});
expect(orm.getSqlUpdateParts(order)).toEqual({
clause: '"id" = $1, "email" = $2, "subtotal_price" = $3, "utm_source_id" = $4',
idVar: '$5',
values: [1, 'test@test.com', 100, 10, 1]
});
});
test('appends the id value at the end of values array', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 42, email: 'x@y.com' });
const result = orm.getSqlUpdateParts(order);
expect(result.values[result.values.length - 1]).toEqual(42);
});
test('custom "on" parameter uses that property for the id value', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1, email: 'x@y.com' });
const result = orm.getSqlUpdateParts(order, 'email');
expect(result.values[result.values.length - 1]).toEqual('x@y.com');
});
test('filters out undefined properties from clause', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 7 });
const result = orm.getSqlUpdateParts(order);
expect(result.clause).toEqual('"id" = $1');
expect(result.idVar).toEqual('$2');
expect(result.values).toEqual([7, 7]);
});
});
describe('getMatchingParts', () => {
test('generates correct WHERE clause and values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'test@test.com',
subtotalPrice: 100,
utmSourceId: 10
});
expect(orm.getMatchingParts(order)).toEqual({
values: [1, 'test@test.com', 100, 10],
whereClause: '"order"."id" = $1 AND "order"."email" = $2 AND "order"."subtotal_price" = $3 AND "order"."utm_source_id" = $4'
});
});
test('filters out null and undefined values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
const result = orm.getMatchingParts(order);
expect(result.whereClause).toEqual('"order"."id" = $1');
expect(result.values).toEqual([1]);
});
});
describe('getMatchingPartsObject', () => {
test('generates correct WHERE clause and object values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'test@test.com',
subtotalPrice: 100,
utmSourceId: 10
});
expect(orm.getMatchingPartsObject(order)).toEqual({
values: {
1: 1,
2: 'test@test.com',
3: 100,
4: 10
},
whereClause: '"order"."id" = $(1) AND "order"."email" = $(2) AND "order"."subtotal_price" = $(3) AND "order"."utm_source_id" = $(4)'
});
});
test('uses $() syntax for parameterized values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 5 });
const result = orm.getMatchingPartsObject(order);
expect(result.whereClause).toContain('$(1)');
expect(result.values).toEqual({ 1: 5 });
});
});
describe('getValueBySqlColumn', () => {
test('retrieves values by SQL column name', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({
id: 1,
email: 'test@test.com',
subtotalPrice: 100,
utmSourceId: 10
});
expect(orm.getValueBySqlColumn(order, 'id')).toEqual(1);
expect(orm.getValueBySqlColumn(order, 'email')).toEqual('test@test.com');
expect(orm.getValueBySqlColumn(order, 'subtotal_price')).toEqual(100);
expect(orm.getValueBySqlColumn(order, 'utm_source_id')).toEqual(10);
});
test('returns undefined for columns with no value set', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
expect(orm.getValueBySqlColumn(order, 'email')).toBeUndefined();
});
});
describe('getSqlColumnForPropertyName', () => {
test('maps property names to SQL column names', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
expect(orm.getSqlColumnForPropertyName(order, 'id')).toEqual('id');
expect(orm.getSqlColumnForPropertyName(order, 'utmSourceId')).toEqual('utm_source_id');
expect(orm.getSqlColumnForPropertyName(order, 'browserIP')).toEqual('browser_ip');
});
test('returns undefined for non-existent property name', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
expect(orm.getSqlColumnForPropertyName(order, 'nonExistent')).toBeUndefined();
});
});
describe('getNewWith', () => {
test('creates a new model instance with specified SQL columns and values', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
const newOrder = orm.getNewWith(order, ['id', 'email'], [99, 'new@test.com']);
expect(newOrder).toBeInstanceOf(order_1.Order);
expect(newOrder.id).toEqual(99);
expect(newOrder.email).toEqual('new@test.com');
});
test('creates model with single column', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
const newOrder = orm.getNewWith(order, ['id'], [42]);
expect(newOrder).toBeInstanceOf(order_1.Order);
expect(newOrder.id).toEqual(42);
});
test('maps SQL column names to property names', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
const order = new order_1.Order({ id: 1 });
const newOrder = orm.getNewWith(order, ['id', 'subtotal_price', 'utm_source_id'], [1, 200, 5]);
expect(newOrder.id).toEqual(1);
expect(newOrder.subtotalPrice).toEqual(200);
expect(newOrder.utmSourceId).toEqual(5);
});
});
/* -------------------------------------------------------------------------*/
/* Unsupported driver error ------------------------------------------------*/
/* -------------------------------------------------------------------------*/
describe('unsupported database driver', () => {
test('throws for an unrecognized driver', () => {
expect(() => (0, index_1.create)({
entities: entities_1.entities,
db: { $config: {} }
})).toThrow('database driver is not yet supported');
});
});
/* -------------------------------------------------------------------------*/
/* CRUD operations (mocked db) ---------------------------------------------*/
/* -------------------------------------------------------------------------*/
describe('CRUD operations', () => {
const orderSelectClause = '"order".id as "order#id", "order".email as "order#email", "order".browser_ip as "order#browser_ip", "order".browser_user_agent as "order#browser_user_agent", "order".kujo_imported_date as "order#kujo_imported_date", "order".created_date as "order#created_date", "order".cancel_reason as "order#cancel_reason", "order".cancelled_date as "order#cancelled_date", "order".closed_date as "order#closed_date", "order".processed_date as "order#processed_date", "order".updated_date as "order#updated_date", "order".note as "order#note", "order".subtotal_price as "order#subtotal_price", "order".taxes_included as "order#taxes_included", "order".total_discounts as "order#total_discounts", "order".total_price as "order#total_price", "order".total_tax as "order#total_tax", "order".total_weight as "order#total_weight", "order".order_status_url as "order#order_status_url", "order".utm_source_id as "order#utm_source_id", "order".utm_medium_id as "order#utm_medium_id", "order".utm_campaign as "order#utm_campaign", "order".utm_content as "order#utm_content", "order".utm_term as "order#utm_term"';
const makeRow = (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
});
describe('create (INSERT)', () => {
test('builds INSERT query and returns created model', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
many: jest.fn().mockResolvedValue([makeRow(1, 'new@test.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1, email: 'new@test.com' });
const result = yield orm.create(order);
expect(db.many).toHaveBeenCalledTimes(1);
const [query, values] = db.many.mock.calls[0];
expect(query).toContain('INSERT INTO "order"');
expect(query).toContain('"id", "email"');
expect(query).toContain('$1,$2');
expect(query).toContain('RETURNING');
expect(values).toEqual([1, 'new@test.com']);
expect(result.id).toEqual(1);
expect(result.email).toEqual('new@test.com');
}));
test('only includes defined columns in INSERT', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
many: jest.fn().mockResolvedValue([makeRow(5, null)])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 5 });
yield orm.create(order);
const [query, values] = db.many.mock.calls[0];
expect(query).toContain('"id"');
expect(query).not.toContain('"email"');
expect(values).toEqual([5]);
}));
});
describe('update (UPDATE)', () => {
test('builds UPDATE query and returns updated model', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
many: jest.fn().mockResolvedValue([makeRow(1, 'updated@test.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1, email: 'updated@test.com' });
const result = yield orm.update(order);
expect(db.many).toHaveBeenCalledTimes(1);
const [query, values] = db.many.mock.calls[0];
expect(query).toContain('UPDATE "order"');
expect(query).toContain('SET');
expect(query).toContain('WHERE');
expect(query).toContain('RETURNING');
expect(values).toContain(1);
expect(values).toContain('updated@test.com');
expect(result.email).toEqual('updated@test.com');
}));
test('uses custom "on" column for WHERE clause', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
many: jest.fn().mockResolvedValue([makeRow(1, 'x@y.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1, email: 'x@y.com' });
yield orm.update(order, { on: 'email' });
const [query, values] = db.many.mock.calls[0];
expect(query).toContain('WHERE');
expect(query).toContain('"order".email');
expect(values[values.length - 1]).toEqual('x@y.com');
}));
});
describe('delete (DELETE)', () => {
test('builds DELETE query with id', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
none: jest.fn().mockResolvedValue(null)
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 42 });
yield orm.delete(order);
expect(db.none).toHaveBeenCalledTimes(1);
const [query, values] = db.none.mock.calls[0];
expect(query).toContain('DELETE FROM "order"');
expect(query).toContain('"order".id = $(id)');
expect(values).toEqual({ id: 42 });
}));
});
describe('deleteMatching', () => {
test('builds DELETE query with WHERE clause from model properties', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
none: jest.fn().mockResolvedValue(null)
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 10, email: 'del@test.com' });
yield orm.deleteMatching(order);
expect(db.none).toHaveBeenCalledTimes(1);
const [query, values] = db.none.mock.calls[0];
expect(query).toContain('DELETE FROM "order"');
expect(query).toContain('WHERE');
expect(query).toContain('"order"."id" = $1');
expect(query).toContain('"order"."email" = $2');
expect(values).toEqual([10, 'del@test.com']);
}));
});
describe('getMatching', () => {
test('builds SELECT query with WHERE clause and returns one model', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
many: jest.fn().mockResolvedValue([makeRow(1, 'found@test.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1 });
const result = yield orm.getMatching(order);
expect(db.many).toHaveBeenCalledTimes(1);
const [query, values] = db.many.mock.calls[0];
expect(query).toContain('SELECT');
expect(query).toContain('FROM "order"');
expect(query).toContain('WHERE');
expect(query).toContain('"order"."id" = $1');
expect(values).toEqual([1]);
expect(result.id).toEqual(1);
expect(result.email).toEqual('found@test.com');
}));
});
describe('getOneOrNoneMatching', () => {
test('returns a model when one exists', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
any: jest.fn().mockResolvedValue([makeRow(1, 'one@test.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1 });
const result = yield orm.getOneOrNoneMatching(order);
expect(db.any).toHaveBeenCalledTimes(1);
expect(result).toBeDefined();
expect(result.id).toEqual(1);
}));
test('returns undefined when none exist', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
any: jest.fn().mockResolvedValue([])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 999 });
const result = yield orm.getOneOrNoneMatching(order);
expect(result).toBeUndefined();
}));
});
describe('getAnyMatching', () => {
test('returns collection when results exist', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
result: jest.fn().mockResolvedValue({
rows: [makeRow(1, 'a@test.com'), makeRow(2, 'b@test.com')],
fields: [{ name: 'order#id' }]
})
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1 });
const result = yield orm.getAnyMatching(order);
expect(db.result).toHaveBeenCalledTimes(1);
expect(result.models.length).toEqual(2);
}));
test('returns empty collection when no results', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
result: jest.fn().mockResolvedValue({
rows: [],
fields: [{ name: 'order#id' }]
})
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 999 });
const result = yield orm.getAnyMatching(order);
expect(result.models.length).toEqual(0);
}));
});
describe('getAllMatching', () => {
test('returns collection when results exist', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
any: jest.fn().mockResolvedValue([makeRow(1, 'a@test.com')])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 1 });
const result = yield orm.getAllMatching(order);
expect(db.any).toHaveBeenCalledTimes(1);
expect(result.models.length).toEqual(1);
}));
test('throws when no results exist', () => __awaiter(void 0, void 0, void 0, function* () {
const db = mockPgpDb({
any: jest.fn().mockResolvedValue([])
});
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
const order = new order_1.Order({ id: 999 });
yield expect(orm.getAllMatching(order)).rejects.toThrow();
}));
});
});
/* -------------------------------------------------------------------------*/
/* ORM exposes core methods ------------------------------------------------*/
/* -------------------------------------------------------------------------*/
describe('ORM exposes core methods', () => {
test('exposes createFromDatabase', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.createFromDatabase).toBe('function');
});
test('exposes createOneFromDatabase', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.createOneFromDatabase).toBe('function');
});
test('exposes createOneOrNoneFromDatabase', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.createOneOrNoneFromDatabase).toBe('function');
});
test('exposes createManyFromDatabase', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.createManyFromDatabase).toBe('function');
});
test('exposes createAnyFromDatabase', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.createAnyFromDatabase).toBe('function');
});
test('exposes tables', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(orm.tables).toBeDefined();
expect(Object.keys(orm.tables).length).toEqual(5);
});
test('exposes getEntityByModel', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.getEntityByModel).toBe('function');
const order = new order_1.Order({ id: 1 });
expect(orm.getEntityByModel(order).tableName).toEqual('order');
});
test('exposes getEntityByTableName', () => {
const orm = (0, index_1.create)({
entities: entities_1.entities,
db: mockPgpDb()
});
expect(typeof orm.getEntityByTableName).toBe('function');
expect(orm.getEntityByTableName('order').tableName).toEqual('order');
});
test('exposes db reference', () => {
const db = mockPgpDb();
const orm = (0, index_1.create)({ entities: entities_1.entities, db });
expect(orm.db).toBe(db);
});
});