Use spies for CloudCode logger tests (#5042)

This commit is contained in:
Florent Vilmart
2018-09-08 18:46:26 -04:00
committed by GitHub
parent 617e8405ff
commit cfa22d0a62

View File

@@ -9,17 +9,24 @@ const loremFile = __dirname + '/support/lorem.txt';
describe('Cloud Code Logger', () => { describe('Cloud Code Logger', () => {
let user; let user;
let spy;
beforeEach(done => { beforeEach(async () => {
Parse.User.enableUnsafeCurrentUser(); Parse.User.enableUnsafeCurrentUser();
return reconfigureServer({ return reconfigureServer({
// useful to flip to false for fine tuning :). // useful to flip to false for fine tuning :).
silent: true, silent: true,
}).then(() => { })
.then(() => {
return Parse.User.signUp('tester', 'abc') return Parse.User.signUp('tester', 'abc')
.catch(() => {})
.then(loggedInUser => (user = loggedInUser)) .then(loggedInUser => (user = loggedInUser))
.then(() => Parse.User.logIn(user.get('username'), 'abc')) .then(() => Parse.User.logIn(user.get('username'), 'abc'));
.then(() => done()); })
.then(() => {
spy = spyOn(
Config.get('test').loggerController.adapter,
'log'
).and.callThrough();
}); });
}); });
@@ -27,9 +34,10 @@ describe('Cloud Code Logger', () => {
// see helpers.js:afterEach // see helpers.js:afterEach
it('should expose log to functions', () => { it('should expose log to functions', () => {
const config = Config.get('test'); const spy = spyOn(
const spy = spyOn(config.loggerController, 'log').and.callThrough(); Config.get('test').loggerController,
'log'
).and.callThrough();
Parse.Cloud.define('loggerTest', req => { Parse.Cloud.define('loggerTest', req => {
req.log.info('logTest', 'info log', { info: 'some log' }); req.log.info('logTest', 'info log', { info: 'some log' });
req.log.error('logTest', 'error log', { error: 'there was an error' }); req.log.error('logTest', 'error log', { error: 'there was an error' });
@@ -61,26 +69,21 @@ describe('Cloud Code Logger', () => {
}); });
it('trigger should obfuscate password', done => { it('trigger should obfuscate password', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave(Parse.User, req => { Parse.Cloud.beforeSave(Parse.User, req => {
return req.object; return req.object;
}); });
Parse.User.signUp('tester123', 'abc') Parse.User.signUp('tester123', 'abc')
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => {
.then(res => { const entry = spy.calls.mostRecent().args;
const entry = res[0]; expect(entry[1]).not.toMatch(/password":"abc/);
expect(entry.message).not.toMatch(/password":"abc/); expect(entry[1]).toMatch(/\*\*\*\*\*\*\*\*/);
expect(entry.message).toMatch(/\*\*\*\*\*\*\*\*/);
done(); done();
}) })
.then(null, e => done.fail(e)); .then(null, e => done.fail(e));
}); });
it('should expose log to trigger', done => { it('should expose log to trigger', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave('MyObject', req => { Parse.Cloud.beforeSave('MyObject', req => {
req.log.info('beforeSave MyObject', 'info log', { info: 'some log' }); req.log.info('beforeSave MyObject', 'info log', { info: 'some log' });
req.log.error('beforeSave MyObject', 'error log', { req.log.error('beforeSave MyObject', 'error log', {
@@ -90,29 +93,27 @@ describe('Cloud Code Logger', () => {
}); });
const obj = new Parse.Object('MyObject'); const obj = new Parse.Object('MyObject');
obj obj.save().then(() => {
.save() const lastCalls = spy.calls.all().reverse();
.then(() => { const cloudTriggerMessage = lastCalls[0].args;
return logController.getLogs({ from: Date.now() - 500, size: 1000 }); const errorMessage = lastCalls[1].args;
}) const infoMessage = lastCalls[2].args;
.then(res => { expect(cloudTriggerMessage[0]).toBe('info');
expect(res.length).not.toBe(0); expect(cloudTriggerMessage[2].triggerType).toEqual('beforeSave');
const lastLogs = res.slice(0, 3); expect(cloudTriggerMessage[1]).toMatch(
const cloudTriggerMessage = lastLogs[0];
const errorMessage = lastLogs[1];
const infoMessage = lastLogs[2];
expect(cloudTriggerMessage.level).toBe('info');
expect(cloudTriggerMessage.triggerType).toEqual('beforeSave');
expect(cloudTriggerMessage.message).toMatch(
/beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {}/ /beforeSave triggered for MyObject for user [^ ]*\n {2}Input: {}\n {2}Result: {}/
); );
expect(cloudTriggerMessage.user).toBe(user.id); expect(cloudTriggerMessage[2].user).toBe(user.id);
expect(errorMessage.level).toBe('error'); expect(errorMessage[0]).toBe('error');
expect(errorMessage.error).toBe('there was an error'); expect(errorMessage[3].error).toBe('there was an error');
expect(errorMessage.message).toBe('beforeSave MyObject error log'); expect(errorMessage[1] + ' ' + errorMessage[2]).toBe(
expect(infoMessage.level).toBe('info'); 'beforeSave MyObject error log'
expect(infoMessage.info).toBe('some log'); );
expect(infoMessage.message).toBe('beforeSave MyObject info log'); expect(infoMessage[0]).toBe('info');
expect(infoMessage[3].info).toBe('some log');
expect(infoMessage[1] + ' ' + infoMessage[2]).toBe(
'beforeSave MyObject info log'
);
done(); done();
}); });
}); });
@@ -125,18 +126,16 @@ describe('Cloud Code Logger', () => {
}); });
it('should truncate input and result of long lines', done => { it('should truncate input and result of long lines', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
const longString = fs.readFileSync(loremFile, 'utf8'); const longString = fs.readFileSync(loremFile, 'utf8');
Parse.Cloud.define('aFunction', req => { Parse.Cloud.define('aFunction', req => {
return req.params; return req.params;
}); });
Parse.Cloud.run('aFunction', { longString }) Parse.Cloud.run('aFunction', { longString })
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => {
.then(logs => { const log = spy.calls.mostRecent().args;
const log = logs[0]; expect(log[0]).toEqual('info');
expect(log.level).toEqual('info'); expect(log[1]).toMatch(
expect(log.message).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {.*?\(truncated\)$/m
); );
done(); done();
@@ -145,14 +144,12 @@ describe('Cloud Code Logger', () => {
}); });
it('should log an afterSave', done => { it('should log an afterSave', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.afterSave('MyObject', () => {}); Parse.Cloud.afterSave('MyObject', () => {});
new Parse.Object('MyObject') new Parse.Object('MyObject')
.save() .save()
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => {
.then(logs => { const log = spy.calls.mostRecent().args;
const log = logs[0]; expect(log[2].triggerType).toEqual('afterSave');
expect(log.triggerType).toEqual('afterSave');
done(); done();
}) })
// catch errors - not that the error is actually useful :( // catch errors - not that the error is actually useful :(
@@ -160,7 +157,6 @@ describe('Cloud Code Logger', () => {
}); });
it('should log a denied beforeSave', done => { it('should log a denied beforeSave', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.beforeSave('MyObject', () => { Parse.Cloud.beforeSave('MyObject', () => {
throw 'uh oh!'; throw 'uh oh!';
}); });
@@ -171,28 +167,27 @@ describe('Cloud Code Logger', () => {
() => done.fail('this is not supposed to succeed'), () => done.fail('this is not supposed to succeed'),
() => new Promise(resolve => setTimeout(resolve, 100)) () => new Promise(resolve => setTimeout(resolve, 100))
) )
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => {
.then(logs => { const logs = spy.calls.all().reverse();
const log = logs[1]; // 0 is the 'uh oh!' from rejection... const log = logs[1].args; // 0 is the 'uh oh!' from rejection...
expect(log.level).toEqual('error'); expect(log[0]).toEqual('error');
expect(log.error).toEqual({ code: 141, message: 'uh oh!' }); const error = log[2].error;
expect(error instanceof Parse.Error).toBeTruthy();
expect(error.code).toBe(141);
expect(error.message).toBe('uh oh!');
done(); done();
}); });
}); });
it('should log cloud function success', done => { it('should log cloud function success', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('aFunction', () => { Parse.Cloud.define('aFunction', () => {
return 'it worked!'; return 'it worked!';
}); });
Parse.Cloud.run('aFunction', { foo: 'bar' }) Parse.Cloud.run('aFunction', { foo: 'bar' }).then(() => {
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) const log = spy.calls.mostRecent().args;
.then(logs => { expect(log[0]).toEqual('info');
const log = logs[0]; expect(log[1]).toMatch(
expect(log.level).toEqual('info');
expect(log.message).toMatch(
/Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/ /Ran cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Result: "it worked!/
); );
done(); done();
@@ -200,25 +195,23 @@ describe('Cloud Code Logger', () => {
}); });
it('should log cloud function failure', done => { it('should log cloud function failure', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('aFunction', () => { Parse.Cloud.define('aFunction', () => {
throw 'it failed!'; throw 'it failed!';
}); });
Parse.Cloud.run('aFunction', { foo: 'bar' }) Parse.Cloud.run('aFunction', { foo: 'bar' })
.then(null, () => .catch(() => {})
logController.getLogs({ from: Date.now() - 500, size: 1000 }) .then(() => {
) const logs = spy.calls.all().reverse();
.then(logs => { expect(logs[0].args[1]).toBe('it failed!');
expect(logs[0].message).toBe('it failed!'); const log = logs[1].args;
const log = logs[1]; expect(log[0]).toEqual('error');
expect(log.level).toEqual('error'); expect(log[1]).toMatch(
expect(log.message).toMatch( /Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error:/
/Failed running cloud function aFunction for user [^ ]* with:\n {2}Input: {"foo":"bar"}\n {2}Error: {"code":141,"message":"it failed!"}/
); );
done(); done();
}); })
.catch(done.fail);
}); });
xit('should log a changed beforeSave indicating a change', done => { xit('should log a changed beforeSave indicating a change', done => {
@@ -247,17 +240,14 @@ describe('Cloud Code Logger', () => {
}).pend('needs more work.....'); }).pend('needs more work.....');
it('cloud function should obfuscate password', done => { it('cloud function should obfuscate password', done => {
const logController = new LoggerController(new WinstonLoggerAdapter());
Parse.Cloud.define('testFunction', () => { Parse.Cloud.define('testFunction', () => {
return 'verify code success'; return 'verify code success';
}); });
Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' }) Parse.Cloud.run('testFunction', { username: 'hawk', password: '123456' })
.then(() => logController.getLogs({ from: Date.now() - 500, size: 1000 })) .then(() => {
.then(res => { const entry = spy.calls.mostRecent().args;
const entry = res[0]; expect(entry[2].params.password).toMatch(/\*\*\*\*\*\*\*\*/);
expect(entry.params.password).toMatch(/\*\*\*\*\*\*\*\*/);
done(); done();
}) })
.then(null, e => done.fail(e)); .then(null, e => done.fail(e));