@getanthill/datastore
Version:
Event-Sourced Datastore
529 lines (464 loc) • 14.3 kB
text/typescript
import setup from '../../test/setup';
import PostgreSQLClient from './pg';
describe('services/pg', () => {
let pg;
let app;
beforeEach(async () => {
app = await setup.build();
await PostgreSQLClient.init(app.services.config.pg);
pg = new PostgreSQLClient(app.services.config.pg);
await pg.connect();
});
afterEach(async () => {
jest.resetAllMocks();
await pg.disconnect();
await setup.teardownDb(app.services.mongodb);
});
describe('#connect / #end', () => {
it('invokes the client connect on connect', async () => {
await pg.disconnect();
pg = new PostgreSQLClient(app.services.config.pg);
jest.spyOn(pg._client, 'connect').mockImplementation(() => null);
await pg.connect();
expect(pg._client.connect).toHaveBeenCalledTimes(1);
});
it('allows to invoke the connect several times safely', async () => {
jest.spyOn(pg._client, 'connect').mockImplementation(() => null);
await pg.connect();
await pg.connect();
expect(pg._client.connect).toHaveBeenCalledTimes(2);
});
it('invokes the client end on disconnect', async () => {
await pg.disconnect();
pg = new PostgreSQLClient(app.services.config.pg);
jest.spyOn(pg._client, 'end').mockImplementation(() => null);
await pg.disconnect();
expect(pg._client.end).toHaveBeenCalledTimes(1);
});
it('allows to invokes the client end several times', async () => {
jest.spyOn(pg._client, 'end');
await pg.disconnect();
await pg.disconnect();
expect(pg._client.end).toHaveBeenCalledTimes(2);
});
});
describe('#getSqlSchemaForModelDeletion', () => {
it('returns SQL to delete model tables', () => {
expect(
PostgreSQLClient.getSqlSchemaForModelDeletion({
name: 'users',
correlation_field: 'user_id',
}),
).toMatchSnapshot();
});
});
describe('#getSqlSchemaForModel', () => {
it('returns SQL to create model tables', () => {
expect(
PostgreSQLClient.getSqlSchemaForModel({
name: 'users',
correlation_field: 'user_id',
}),
).toMatchSnapshot();
});
it('returns SQL to create model tables with database source name', () => {
expect(
PostgreSQLClient.getSqlSchemaForModel(
{
name: 'users',
correlation_field: 'user_id',
},
'entities',
),
).toMatchSnapshot();
});
it('returns SQL to create model tables with comments', () => {
expect(
PostgreSQLClient.getSqlSchemaForModel({
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
}),
).toMatchSnapshot();
});
it('creates the tables effectively', async () => {
const sql = PostgreSQLClient.getSqlSchemaForModels(
[
{
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
},
],
true,
);
await pg.queryAll(sql);
const d = await pg.query(`SELECT
table_name,
column_name,
data_type
FROM
information_schema.columns
WHERE
table_name = 'users';`);
expect(d.rows).toEqual([
{
column_name: 'created_at',
data_type: 'timestamp with time zone',
table_name: 'users',
},
{
column_name: 'updated_at',
data_type: 'timestamp with time zone',
table_name: 'users',
},
{ column_name: 'version', data_type: 'bigint', table_name: 'users' },
{ column_name: 'entity', data_type: 'json', table_name: 'users' },
{ column_name: 'user_id', data_type: 'text', table_name: 'users' },
]);
});
});
describe('#getSqlSchemaFromModels', () => {
it('returns SQL to create all model tables', () => {
expect(
PostgreSQLClient.getSqlSchemaForModels([
{
name: 'users',
correlation_field: 'user_id',
},
{
name: 'accounts',
correlation_field: 'account_id',
},
]),
).toMatchSnapshot();
});
it('returns SQL to create all model tables with comments', () => {
expect(
PostgreSQLClient.getSqlSchemaForModels([
{
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
},
{
name: 'accounts',
correlation_field: 'account_id',
description: 'Accounts information',
},
]),
).toMatchSnapshot();
});
it('returns SQL to delete then create all model tables', () => {
expect(
PostgreSQLClient.getSqlSchemaForModels(
[
{
name: 'users',
correlation_field: 'user_id',
},
{
name: 'accounts',
correlation_field: 'account_id',
},
],
true,
),
).toMatchSnapshot();
});
});
describe('#insert', () => {
it('inserts an entity effectively', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res = await pg.insert(modelConfig, 'entities', {
user_id: 'user_id',
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
version: 0,
});
expect(res.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
entity: {
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
user_id: 'user_id',
version: 0,
},
updated_at: new Date('2021-01-01T00:00:00.000Z'),
user_id: 'user_id',
version: '0',
},
]);
});
it('updates an entity effectively', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res0 = await pg.insert(modelConfig, 'entities', {
user_id: 'user_id',
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
version: 0,
});
const res1 = await pg.insert(modelConfig, 'entities', {
user_id: 'user_id',
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-02T00:00:00.000Z',
version: 1,
});
expect(res1.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
entity: {
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-02T00:00:00.000Z',
user_id: 'user_id',
version: 1,
},
updated_at: new Date('2021-01-02T00:00:00.000Z'),
user_id: 'user_id',
version: '1',
},
]);
});
it('keeps encrypted data from entity on insert if requested', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
encrypted_fields: ['firstname'],
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res = await pg.insert(
modelConfig,
'entities',
{
user_id: 'user_id',
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
version: 0,
firstname: {
hash: 'hash',
encrypted: 'encrypted',
},
},
{
with_encrypted_data: true,
},
);
expect(res.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
entity: {
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
user_id: 'user_id',
version: 0,
firstname: {
hash: 'hash',
encrypted: 'encrypted',
},
},
updated_at: new Date('2021-01-01T00:00:00.000Z'),
user_id: 'user_id',
version: '0',
},
]);
});
it('removes encrypted data from entity on insert by default', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
encrypted_fields: ['firstname'],
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res = await pg.insert(modelConfig, 'entities', {
user_id: 'user_id',
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
version: 0,
firstname: {
hash: 'hash',
encrypted: 'encrypted',
},
});
expect(res.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
entity: {
created_at: '2021-01-01T00:00:00.000Z',
updated_at: '2021-01-01T00:00:00.000Z',
user_id: 'user_id',
version: 0,
firstname: {
hash: 'hash',
},
},
updated_at: new Date('2021-01-01T00:00:00.000Z'),
user_id: 'user_id',
version: '0',
},
]);
});
it('inserts an event effectively', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res = await pg.insert(modelConfig, 'events', {
user_id: 'user_id',
type: 'CREATED',
created_at: '2021-01-01T00:00:00.000Z',
version: 0,
});
expect(res.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users_events WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
event: {
created_at: '2021-01-01T00:00:00.000Z',
type: 'CREATED',
user_id: 'user_id',
version: 0,
},
type: 'CREATED',
user_id: 'user_id',
version: '0',
},
]);
});
it('updates an event on contraint violation', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res0 = await pg.insert(modelConfig, 'events', {
user_id: 'user_id',
type: 'CREATED',
created_at: '2021-01-01T00:00:00.000Z',
version: 0,
});
const res1 = await pg.insert(modelConfig, 'events', {
user_id: 'user_id',
type: 'CREATED',
created_at: '2021-01-02T00:00:00.000Z',
version: 0,
});
expect(res0.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users_events WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-02T00:00:00.000Z'),
event: {
created_at: '2021-01-02T00:00:00.000Z',
type: 'CREATED',
user_id: 'user_id',
version: 0,
},
type: 'CREATED',
user_id: 'user_id',
version: '0',
},
]);
});
it('inserts multiple events effectively', async () => {
const modelConfig = {
name: 'users',
correlation_field: 'user_id',
description: 'Users information',
};
await pg.queryAll(
PostgreSQLClient.getSqlSchemaForModels([modelConfig], true),
);
const res0 = await pg.insert(modelConfig, 'events', {
user_id: 'user_id',
type: 'CREATED',
created_at: '2021-01-01T00:00:00.000Z',
version: 0,
});
const res1 = await pg.insert(modelConfig, 'events', {
user_id: 'user_id',
type: 'CREATED',
created_at: '2021-01-02T00:00:00.000Z',
version: 1,
});
expect(res1.rowCount).toEqual(1);
const result = await pg.query(
`SELECT * FROM users_events WHERE user_id='user_id'`,
);
expect(result.rows).toEqual([
{
created_at: new Date('2021-01-01T00:00:00.000Z'),
event: {
created_at: '2021-01-01T00:00:00.000Z',
type: 'CREATED',
user_id: 'user_id',
version: 0,
},
type: 'CREATED',
user_id: 'user_id',
version: '0',
},
{
created_at: new Date('2021-01-02T00:00:00.000Z'),
event: {
created_at: '2021-01-02T00:00:00.000Z',
type: 'CREATED',
user_id: 'user_id',
version: 1,
},
type: 'CREATED',
user_id: 'user_id',
version: '1',
},
]);
});
});
});