Skip to content

Commit

Permalink
merged tests; added tests for stopping an application
Browse files Browse the repository at this point in the history
  • Loading branch information
wesone committed May 7, 2021
2 parents 4bad9fb + 85d87da commit b2fed52
Show file tree
Hide file tree
Showing 18 changed files with 631 additions and 142 deletions.
3 changes: 2 additions & 1 deletion src/adapters/eventstore-mysql/Adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Adapter extends EventStoreAdapterInterface
throw Error('EventStore-MySQL needs a config.');
if(!this.config.host || !this.config.host.length)
throw Error('EventStore-MySQL needs a host.');
if(!this.config.port || !this.config.host.port)
if(!this.config.port)
this.config.port = 3306;
if(!this.config.database || !this.config.database.length)
throw Error('EventStore-MySQL needs a database name.');
Expand All @@ -120,6 +120,7 @@ class Adapter extends EventStoreAdapterInterface
// see https://github.com/sidorares/node-mysql2/issues/1239
//========MySQL 8.0.22 (and higher) fix========
const originalExecute = this.db.execute.bind(this.db);
/* istanbul ignore next */
this.db.execute = function(...args){
const [query, substitutions, ...rest] = args;
for(const key in substitutions) // array or object
Expand Down
63 changes: 63 additions & 0 deletions test/_resources/testApp/aggregates/user.commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const {
USER_CREATED,
USER_UPDATED,
USER_REJECTED,
USER_MAIL_CHANGE_REVERTED
} = require('../events/users');

const {
ConflictError,
BadRequestError,
ForbiddenError
} = require('../errors');

module.exports = {
create: async (command, state/* , context */) => {
if(state.registered)
throw new ConflictError('User already registered');
if(!command.payload.email)
throw new BadRequestError('Please provide an email address');
if(!command.payload.name)
throw new BadRequestError('Please provide a name');

return {
type: USER_CREATED,
payload: {
email: command.payload.email,
name: command.payload.name
}
};
},
update: async (command, state/* , context */) => {
if(!state.registered || state.removed)
throw new ForbiddenError();
if(!command.payload.name && !command.payload.email)
throw new BadRequestError('Please specify a new name and/or email address');
if(command.payload.name === state.name)
throw new BadRequestError(`Name is already set to '${state.name}'`);
if(command.payload.email === state.email)
throw new BadRequestError(`Email address is already set to '${state.email}'`);

return {
type: USER_UPDATED,
payload: {
name: command.payload.name ?? state.name,
email: command.payload.email ?? state.email
}
};
},
reject: async (command/* , state, context */) => {
return {
type: USER_REJECTED,
payload: command.payload
};
},
restoreEmailAddress: async (command, state/*, context */) => {
if(!state.registered || state.removed)
throw new ForbiddenError();
return {
type: USER_MAIL_CHANGE_REVERTED,
payload: {},
};
}
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
const {
USER_CREATED,
USER_UPDATED,
USER_REJECTED
} = require('../events/users');

module.exports = {
init: () => ({}),
[USER_CREATED]: (state, event) => ({
...state
[USER_CREATED]: (state, {payload}) => ({
...state,
...payload,
registered: true
}),
[USER_UPDATED]: (state, {payload}) => ({
...state,
...payload
}),
[USER_REJECTED]: state => ({
...state,
removed: true
})
};
88 changes: 88 additions & 0 deletions test/_resources/testApp/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const Blackrik = require('blackrik');

module.exports = {
aggregates: [
{
name: 'user',
commands: require('./aggregates/user.commands'),
projection: require('./aggregates/user.projection')
}
],
readModels: [
{
name: 'user',
projection: require('./readModels/user.projection'),
resolvers: require('./readModels/user.resolvers'),
adapter: 'default'
}
],
sagas: [
{
name: 'user',
source: require('./sagas/user'),
adapter: 'default'
}
],
adapter: 'default',
readModelStoreAdapters: {
default: {
module: Blackrik.ADAPTERS.READMODELSTORE.MySQL,
args: {
host: 'localhost',
database: 'readmodelstore',
user: 'root',
password: '1234'
}
}
},
eventStoreAdapter: {
module: Blackrik.ADAPTERS.EVENTSTORE.MySQL,
args: {
host: 'localhost',
database: 'eventstore',
user: 'root',
password: '1234'
}
},
eventBusAdapter: {
module: Blackrik.ADAPTERS.EVENTBUS.Kafka,
args: {
brokers: ['localhost:9092']
}
// module: Blackrik.ADAPTERS.EVENTBUS.Local,
// args: {}
},
server: {
config: {
port: 3000,
skipDefaultMiddlewares: false
},
middlewares: [
// a middleware for all routes
(req, res, next) => (req.test = 21) && next(),
// a middleware for /test only
[
'/test',
(req, res, next) => (req.test = 42) && next()
]
],
routes: [
{
method: 'GET',
path: '/test',
callback: (req, res) => {
console.log('CALLED /test; req.test is', req.test);
res.json({middlewareValue: req.test});
}
},
{
method: 'GET',
path: '/test2',
callback: (req, res) => {
console.log('CALLED /test2; req.test is', req.test);
res.json({middlewareValue: req.test});
}
}
]
}
};
59 changes: 59 additions & 0 deletions test/_resources/testApp/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class BaseError extends Error
{
constructor(message = 'Internal Error', status = 500)
{
super(message);
this.status = status;
}
}
module.exports.BaseError = BaseError;

class BadRequestError extends BaseError
{
constructor(message = 'Bad Request')
{
super(message);
this.status = 400;
}
}
module.exports.BadRequestError = BadRequestError;

class UnauthorizedError extends BaseError
{
constructor(message = 'Unauthorized')
{
super(message);
this.status = 401;
}
}
module.exports.UnauthorizedError = UnauthorizedError;

class ForbiddenError extends BaseError
{
constructor(message = 'Forbidden')
{
super(message);
this.status = 403;
}
}
module.exports.ForbiddenError = ForbiddenError;

class NotFoundError extends BaseError
{
constructor(message = 'Not Found')
{
super(message);
this.status = 404;
}
}
module.exports.NotFoundError = NotFoundError;

class ConflictError extends BaseError
{
constructor(message = 'Conflict')
{
super(message);
this.status = 409;
}
}
module.exports.ConflictError = ConflictError;
6 changes: 6 additions & 0 deletions test/_resources/testApp/events/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
USER_CREATED: 'USER_CREATED',
USER_UPDATED: 'USER_UPDATED',
USER_REJECTED: 'USER_REJECTED',
USER_MAIL_CHANGE_REVERTED: 'USER_MAIL_CHANGE_REVERTED'
};
43 changes: 43 additions & 0 deletions test/_resources/testApp/readModels/user.projection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const {
USER_CREATED,
USER_UPDATED,
USER_REJECTED
} = require('../events/users');

const tableName = 'Users';

module.exports = {
init: async store => {
await store.defineTable(tableName, {
id: {
type: 'uuid',
primaryKey: true,
},
email: 'String',
name: 'String',
createdAt: 'Date',
updatedAt: 'Date'
});
},
[USER_CREATED]: async (store, event) => {
const createdAt = new Date(event.timestamp);
await store.insert(tableName, {
id: event.aggregateId,
email: event.payload.email,
name: event.payload.name,
createdAt,
updatedAt: createdAt
});
},
[USER_UPDATED]: async (store, event) => {
await store.update(tableName, {
id: event.aggregateId
}, {
name: event.payload.name,
updatedAt: new Date(event.timestamp)
});
},
[USER_REJECTED]: async (store, {aggregateId: id}) => {
await store.delete(tableName, {id});
}
};
9 changes: 9 additions & 0 deletions test/_resources/testApp/readModels/user.resolvers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const tableName = 'Users';

module.exports = {
get: async (store, {id, position}/* , context */) => {
if(!id)
return null;
return await store.findOne(tableName, {id}, {position});
}
};
37 changes: 37 additions & 0 deletions test/_resources/testApp/sagas/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const {USER_CREATED} = require('../events/users');

const tableName = 'RegisteredUsers';

module.exports = {
handlers: {
init: async store => {
await store.defineTable(tableName, {
email: {
type: 'String',
primaryKey: true,
}
});
return {
noopSideEffectsOnReplay: true
};
},
[USER_CREATED]: async (store, {aggregateId, payload: {email, name}}, sideEffects) => {
if(await store.findOne(tableName, {email}))
return await sideEffects.executeCommand({
aggregateName: 'user',
aggregateId,
type: 'reject',
payload: {
reason: 'email address already taken'
}
});
await store.insert(tableName, {email});
await sideEffects.sendRegistrationMail(email, name);
}
},
sideEffects: {
sendRegistrationMail: async (email, name) => {
console.log(`Sending an email to "${name} <${email}>"...`);
}
}
};

0 comments on commit b2fed52

Please sign in to comment.