@getanthill/datastore
Version:
Event-Sourced Datastore
1,337 lines (1,107 loc) • 34.9 kB
text/typescript
import Datastore from './Datastore';
import * as utils from './utils';
import setup from '../../test/setup';
import thingsModelConfig from '../templates/examples/things.json';
describe('sdk/utils (integration)', () => {
let app;
let ds;
let datastores;
let uuid;
let modelNames: string[];
let res: {
entity: any;
query: any;
query_iteration: any;
batch_id: number;
index: number;
}[] = [];
function handler(entity, query, queryIteration, batchId, index) {
res.push({
entity,
query,
query_iteration: queryIteration,
batch_id: batchId,
index,
});
}
beforeAll(async () => {
[, , , , , ds as Datastore, , app] = await setup.startApi({
features: { api: { admin: true, updateSpecOnModelsChange: true } },
});
datastores = new Map([['default', ds]]);
});
beforeEach(async () => {
uuid = setup.uuid();
modelNames = [`users_${uuid}`, `accounts_${uuid}`];
await Promise.all(
modelNames.map((modelName) =>
ds.createModel({
...thingsModelConfig,
name: modelName,
correlation_field: 'correlation_id',
description: 'Users information',
}),
),
);
res = [];
});
afterAll(async () => {
jest.restoreAllMocks();
await setup.teardownDb(app.services.mongodb);
await setup.stopApi(app);
});
describe('#walkMulti', () => {
let uuid;
beforeEach(() => {
uuid = setup.uuid();
});
it('walk over entities in order (2 entities)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'entities',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res).toEqual([
expect.objectContaining({ entity: entityA, query: queryA }),
expect.objectContaining({ entity: entityB, query: queryB }),
]);
});
it('walk over entities in order (chunkSize = 1)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'entities',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler, {
chunk_size: 1,
});
expect(res).toEqual([
expect.objectContaining({ entity: entityA, query: queryA }),
expect.objectContaining({ entity: entityB, query: queryB }),
]);
});
it('walk over entities in order (3 entities)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'John',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'entities',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryA, queryB], 1, handler);
expect(res).toEqual([
expect.objectContaining({ entity: entityA, query: queryA }),
expect.objectContaining({ entity: entityB, query: queryB }),
expect.objectContaining({ entity: entityC, query: queryA }),
]);
});
it('walk over events in order (2 events)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
]);
});
it('walk over events in order (3 events)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
expect.objectContaining({ entity: eventsA[1], query: queryA }),
]);
});
it('walk over events in order without a pageSize defined (3 events)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], undefined, handler);
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
expect.objectContaining({ entity: eventsA[1], query: queryA }),
]);
});
it('walk over events in order with a pageSize of 2 (3 events)', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'John',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'John',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 2, handler);
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
expect.objectContaining({ entity: eventsA[1], query: queryA }),
]);
});
it('(backward) walk over entities in order with exact same `created_at` timestamp', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const _walkNext = ds.walkNext;
function mockWalkNext(
model: string,
query: object,
source: string,
page: number,
pageSize: number,
opts: {
current_version: number;
version_ordered?: boolean;
cursor_last_id?: string;
cursor_last_correlation_id: string;
headers?: any;
},
) {
opts.cursor_last_correlation_id = '';
return _walkNext.call(ds, model, query, source, page, pageSize, opts);
}
ds.walkNext = mockWalkNext.bind(ds);
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryA], 1, handler);
expect(res).toHaveLength(2);
ds.walkNext = _walkNext;
});
it('(backward) walk over entities in order with exact same `created_at` timestamp', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const _walkNext = ds.walkNext;
function mockWalkNext(
model: string,
query: object,
source: string,
page: number,
pageSize: number,
opts: {
current_version: number;
version_ordered?: boolean;
cursor_last_id?: string;
cursor_last_correlation_id: string;
headers?: any;
},
) {
opts.cursor_last_correlation_id = '';
return _walkNext.call(ds, model, query, source, page, pageSize, opts);
}
ds.walkNext = mockWalkNext.bind(ds);
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { _sort: { created_at: -1 } },
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryA], 1, handler);
expect(res).toHaveLength(2);
ds.walkNext = _walkNext;
});
it('(backward) walk over events in order with exact same `created_at` timestamp', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const _walkNext = ds.walkNext;
function mockWalkNext(
model: string,
query: object,
source: string,
page: number,
pageSize: number,
opts: {
current_version: number;
version_ordered?: boolean;
cursor_last_id?: string;
cursor_last_correlation_id: string;
headers?: any;
},
) {
opts.cursor_last_correlation_id = '';
return _walkNext.call(ds, model, query, source, page, pageSize, opts);
}
ds.walkNext = mockWalkNext.bind(ds);
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryA], 1, handler);
expect(res).toHaveLength(2);
ds.walkNext = _walkNext;
});
it('walk over entities in order with exact same `created_at` timestamp', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityB } = await ds.create(
modelNames[1],
{ firstname: 'Bernard' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { _sort: { created_at: -1 } },
source: 'entities',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: { _sort: { created_at: -1 } },
source: 'entities',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res).toHaveLength(3);
});
it('walk over events in order with exact same `created_at` timestamp', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityB } = await ds.create(
modelNames[1],
{ firstname: 'Bernard' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res).toHaveLength(3);
});
it('walk over events in order with exact same `created_at` timestamp and fields filtering', async () => {
const createdAt = new Date();
const { data: entityA } = await ds.create(
modelNames[0],
{ firstname: 'Alice' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityB } = await ds.create(
modelNames[1],
{ firstname: 'Bernard' },
{ 'created-at': createdAt.toISOString() },
);
const { data: entityC } = await ds.create(
modelNames[0],
{ firstname: 'Eve' },
{ 'created-at': createdAt.toISOString() },
);
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { _fields: { firstname: 1 } },
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: { _fields: { firstname: 1 } },
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 1, handler);
expect(res.map((r) => r.entity.firstname)).toEqual([
'Bernard',
'Alice',
'Eve',
]);
});
it('walk over events in temporal order and not version order', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'Alice',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'Bernard',
});
await ds.update(modelNames[0], entityA.correlation_id, {
firstname: 'Alizzz',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'Eve',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 2, handler, {
version_ordered: false,
});
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
expect.objectContaining({ entity: eventsA[1], query: queryA }),
expect.objectContaining({ entity: eventsA[2], query: queryA }),
]);
});
it('walk over events in temporal order and not version order with a handle in order', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'Alice',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'Bernard',
});
await ds.update(modelNames[0], entityA.correlation_id, {
firstname: 'Alizzz',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'Eve',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 2, handler, {
version_ordered: false,
handle_in_order: true,
});
expect(res).toEqual([
expect.objectContaining({
entity: eventsA[0],
query: queryA,
batch_id: 0,
index: 0,
}),
expect.objectContaining({
entity: eventsB[0],
query: queryB,
batch_id: 1,
index: 0,
}),
expect.objectContaining({
entity: eventsA[1],
query: queryA,
batch_id: 0,
index: 0,
}),
expect.objectContaining({
entity: eventsA[2],
query: queryA,
batch_id: 1,
index: 0,
}),
]);
});
it('walk over events in temporal order and not version order with a handle in parallel', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'Alice',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'Bernard',
});
await ds.update(modelNames[0], entityA.correlation_id, {
firstname: 'Alizzz',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'Eve',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 4, handler, {
version_ordered: false,
handle_in_parallel: true,
});
expect(res).toEqual([
expect.objectContaining({
entity: eventsA[0],
query: queryA,
batch_id: 0,
index: 0,
}),
expect.objectContaining({
entity: eventsB[0],
query: queryB,
batch_id: 0,
index: 1,
}),
expect.objectContaining({
entity: eventsA[1],
query: queryA,
batch_id: 0,
index: 2,
}),
expect.objectContaining({
entity: eventsA[2],
query: queryA,
batch_id: 0,
index: 3,
}),
]);
});
it('walk over events in order of versions', async () => {
const { data: entityA } = await ds.create(modelNames[0], {
firstname: 'Alice',
});
const { data: entityB } = await ds.create(modelNames[1], {
firstname: 'Bernard',
});
await ds.update(modelNames[0], entityA.correlation_id, {
firstname: 'Alizzz',
});
const { data: entityC } = await ds.create(modelNames[0], {
firstname: 'Eve',
});
const { data: eventsA } = await ds.allEvents(modelNames[0], {});
const { data: eventsB } = await ds.allEvents(modelNames[1], {});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: {},
source: 'events',
};
// @ts-ignore
await utils.walkMulti(datastores, [queryB, queryA], 2, handler, {
version_ordered: true,
});
expect(res).toEqual([
expect.objectContaining({ entity: eventsA[0], query: queryA }),
expect.objectContaining({ entity: eventsB[0], query: queryB }),
expect.objectContaining({ entity: eventsA[2], query: queryA }),
expect.objectContaining({ entity: eventsA[1], query: queryA }),
]);
});
});
describe('#walkMulti (with mutation)', () => {
let uuid;
beforeEach(() => {
uuid = setup.uuid();
});
it('walk over entities without any mutation', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result) => {
// Noop
res.push(result);
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
});
it('walk over entities with total mutation', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result) => {
const { data: mutatedResult } = await ds.update(
modelNames[0],
result.correlation_id,
{ mutated_attribute: 'consumed' },
);
res.push(result);
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
});
it('walk over entities with partial mutation', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
const mutatedEntities: unknown[] = [];
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result) => {
res.push(result);
if (result.firstname === 'alice') {
return;
}
const { data: mutatedResult } = await ds.update(
modelNames[0],
result.correlation_id,
{ mutated_attribute: 'consumed' },
);
mutatedEntities.push(mutatedResult);
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
expect(mutatedEntities).toHaveLength(1);
expect(mutatedEntities).toMatchObject([
{ mutated_attribute: 'consumed', firstname: 'bernard', version: 1 },
]);
});
it('walk over entities with mutation with sort order on another field', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created', _sort: { firstname: -1 } },
source: 'entities' as 'entities',
};
const mutatedEntities: unknown[] = [];
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result) => {
res.push(result);
const { data: mutatedResult } = await ds.update(
modelNames[0],
result.correlation_id,
{ mutated_attribute: 'consumed' },
);
mutatedEntities.push(mutatedResult);
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
});
it('walk over entities with high number of entities', async () => {
for (let i = 0; i < 50; i++) {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: `alice-${i}`,
});
}
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result) => {
res.push(result);
await ds.update(modelNames[0], result.correlation_id, {
mutated_attribute: 'consumed',
});
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(50);
});
it('walk over entities with high number of entities and page size of 5', async () => {
for (let i = 0; i < 50; i++) {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: `alice-${i}`,
});
}
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
5,
async (result) => {
res.push(result);
await ds.update(modelNames[0], result.correlation_id, {
mutated_attribute: 'consumed',
});
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(50);
});
it('walk over entities with high number of entities and page size of 7 for 50 entities', async () => {
for (let i = 0; i < 50; i++) {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: `alice-${i}`,
});
}
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
7,
async (result) => {
res.push(result);
await ds.update(modelNames[0], result.correlation_id, {
mutated_attribute: 'consumed',
});
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(50);
});
it('walk over entities from multiple triggers with mutation', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[1], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
const queryB = {
datastore: 'default',
model: modelNames[1],
query: { mutated_attribute: 'created' },
source: 'entities' as 'entities',
};
const mutatedEntities: unknown[] = [];
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA, queryB],
1,
async (result, query) => {
res.push(result);
const { data: mutatedResult } = await ds.update(
query.model,
result.correlation_id,
{ mutated_attribute: 'consumed' },
);
mutatedEntities.push(mutatedResult);
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
});
it('walk over entities changing the order', async () => {
const { data: alice } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'alice',
});
const { data: bernard } = await ds.create(modelNames[0], {
mutated_attribute: 'created',
firstname: 'bernard',
});
const queryA = {
datastore: 'default',
model: modelNames[0],
query: { _sort: { firstname: 1 } },
source: 'entities' as 'entities',
};
const mutatedEntities: unknown[] = [];
// @ts-ignore
await utils.walkMulti(
datastores,
[queryA],
1,
async (result, query) => {
res.push(result);
if (result.firstname === 'alice') {
const { data: mutatedResult } = await ds.update(
query.model,
result.correlation_id,
{ firstname: 'eve' },
);
mutatedEntities.push(mutatedResult);
}
},
{ is_mutating: true },
);
// Then
expect(res).toHaveLength(2);
expect(res).toEqual([alice, bernard]);
});
});
describe('#walkMulti (errors)', () => {
let uuid;
beforeEach(() => {
uuid = setup.uuid();
});
it('error on invokation with mutation and `source=events`', async () => {
let error;
try {
await utils.walkMulti(
datastores,
[
{
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events' as 'events',
},
],
1,
async () => {},
{ is_mutating: true },
);
} catch (err) {
error = err;
}
expect(error).toHaveProperty(
'message',
utils.ERRORS.INCOMPATIBLE_MUTATION_ON_EVENTS,
);
});
it('error on invokation with mutation and `source=events`', async () => {
let error;
try {
await utils.walkMulti(
datastores,
[
{
datastore: 'default',
model: modelNames[0],
query: {},
source: 'events' as 'events',
},
],
1,
async () => {},
{ handle_in_order: true, handle_in_parallel: true },
);
} catch (err) {
error = err;
}
expect(error).toHaveProperty(
'message',
utils.ERRORS.INCOMPATIBLE_MULTIPLE_HANDLE_OPTIONS,
);
});
});
describe('#getLastMatchingCorrelationIdIndex', () => {
it('returns -1 if no correlationId is defined in `before`', () => {
// Given
// When
const index = utils.getLastMatchingCorrelationIdIndex([], []);
// Then
expect(index).toEqual(-1);
});
it('returns -1 if the last correlationId from `before` is not found in `after`', () => {
// Given
// When
const index = utils.getLastMatchingCorrelationIdIndex(
['id-0', 'id-1'],
[],
);
// Then
expect(index).toEqual(-1);
});
it('returns 0 if the last correlationId from `before` is found at first place in `after`', () => {
// Given
// When
const index = utils.getLastMatchingCorrelationIdIndex(
['id-0', 'id-1'],
['id-1'],
);
// Then
expect(index).toEqual(0);
});
it('returns 1 if the last correlationId from `before` is found at second place in `after`', () => {
// Given
// When
const index = utils.getLastMatchingCorrelationIdIndex(
['id-0', 'id-1', 'id-2'], // id-0 would have been mutated
['id-1', 'id-2', 'id-3'],
);
// Then
expect(index).toEqual(1);
});
});
});