Ghost/ghost/adapter-manager/test/AdapterManager.test.js
Naz 37dd187fe6
Added adapter caching based on features
refs https://github.com/TryGhost/Toolbox/issues/384

- Adapter cache was not able to store multiple object instances derived from same Base class. This created a need to create boilerplate "shell" classes inheriting from the Base class, e.g.: ImageSizeCacheSyncInMemory etc.
- Having feature-based adapter instance caching in the adapter manager allows to simplify configuration and reuse the "base class" instead of creating artificial "shell" classes.
- For example with this change both image sizes and settings caches will create separate cache instances deriving from default "Memory" class. Less code, less configuration!
2022-09-06 17:51:57 +08:00

177 lines
6.0 KiB
JavaScript

const sinon = require('sinon');
const should = require('should');
const AdapterManager = require('../');
class BaseMailAdapter {
constructor() {
this.requiredFns = ['someMethod'];
}
}
class IncompleteMailAdapter extends BaseMailAdapter {}
class CustomMailAdapter extends BaseMailAdapter {
someMethod() {}
}
class DefaultMailAdapter extends BaseMailAdapter {
someMethod() {}
}
describe('AdapterManager', function () {
it('getAdapter throws if called without correct parameters', function () {
const pathsToAdapters = [
'first/path'
];
const loadAdapterFromPath = sinon.stub();
const adapterManager = new AdapterManager({
loadAdapterFromPath,
pathsToAdapters
});
adapterManager.registerAdapter('mail', BaseMailAdapter);
try {
adapterManager.getAdapter('mail');
should.fail(null, null, 'Should not have created');
} catch (err) {
should.exist(err);
should.equal(err.errorType, 'IncorrectUsageError');
should.equal(err.message, 'getAdapter must be called with a adapterName and a adapterClassName.');
}
});
it('Loads registered adapters in the order defined by the paths', function () {
const pathsToAdapters = [
'first/path',
'second/path',
'third/path'
];
const loadAdapterFromPath = sinon.stub();
loadAdapterFromPath.withArgs('first/path/mail/incomplete')
.returns(IncompleteMailAdapter);
loadAdapterFromPath.withArgs('second/path/mail/custom')
.returns(CustomMailAdapter);
loadAdapterFromPath.withArgs('third/path/mail/default')
.returns(DefaultMailAdapter);
loadAdapterFromPath.withArgs('first/path/mail/broken')
.throwsException('SHIT_GOT_REAL');
const adapterManager = new AdapterManager({
loadAdapterFromPath,
pathsToAdapters
});
adapterManager.registerAdapter('mail', BaseMailAdapter);
try {
const customAdapter = adapterManager.getAdapter('mail', 'custom', {});
should.ok(customAdapter instanceof BaseMailAdapter);
should.ok(customAdapter instanceof CustomMailAdapter);
} catch (err) {
should.fail(err, null, 'Should not have errored');
}
try {
const incompleteAdapter = adapterManager.getAdapter('mail', 'incomplete', {});
should.fail(incompleteAdapter, null, 'Should not have created');
} catch (err) {
should.exist(err);
should.equal(err.errorType, 'IncorrectUsageError');
should.equal(err.message, 'mail adapter incomplete is missing the someMethod method.');
}
try {
const defaultAdapter = adapterManager.getAdapter('mail', 'default', {});
should.ok(defaultAdapter instanceof BaseMailAdapter);
should.ok(defaultAdapter instanceof DefaultMailAdapter);
} catch (err) {
should.fail(err, null, 'Should not have errored');
}
try {
const brokenAdapter = adapterManager.getAdapter('mail', 'broken', {});
should.fail(brokenAdapter, null, 'Should not have created');
} catch (err) {
should.exist(err);
should.equal(err.errorType, 'IncorrectUsageError');
}
});
it('Reads adapter type from the adapter name divided with a colon (adapter:feature syntax)', function () {
const pathsToAdapters = [
'/path'
];
const loadAdapterFromPath = sinon.stub();
loadAdapterFromPath.withArgs('/path/mail/custom')
.returns(CustomMailAdapter);
loadAdapterFromPath.withArgs('/path/mail/default')
.returns(DefaultMailAdapter);
const adapterManager = new AdapterManager({
loadAdapterFromPath,
pathsToAdapters
});
adapterManager.registerAdapter('mail', BaseMailAdapter);
try {
const customAdapter = adapterManager.getAdapter('mail:newsletters', 'custom');
should.ok(customAdapter instanceof BaseMailAdapter);
should.ok(customAdapter instanceof CustomMailAdapter);
} catch (err) {
should.fail(err, null, 'Should not have errored');
}
});
it('Creates separate class instances for adapters requested for different features', function () {
const pathsToAdapters = [
'/path'
];
const loadAdapterFromPath = sinon.stub();
loadAdapterFromPath.withArgs('/path/mail/custom')
.returns(CustomMailAdapter);
loadAdapterFromPath.withArgs('/path/mail/default')
.returns(DefaultMailAdapter);
const adapterManager = new AdapterManager({
loadAdapterFromPath,
pathsToAdapters
});
adapterManager.registerAdapter('mail', BaseMailAdapter);
let mailNewslettersAdapter;
try {
mailNewslettersAdapter = adapterManager.getAdapter('mail:newsletters', 'custom');
should.ok(mailNewslettersAdapter instanceof BaseMailAdapter);
should.ok(mailNewslettersAdapter instanceof CustomMailAdapter);
} catch (err) {
should.fail(err, null, 'Should not have errored');
}
let mailNotificationsAdapter;
try {
mailNotificationsAdapter = adapterManager.getAdapter('mail:notifications', 'custom');
should.ok(mailNotificationsAdapter instanceof BaseMailAdapter);
should.ok(mailNotificationsAdapter instanceof CustomMailAdapter);
} catch (err) {
should.fail(err, null, 'Should not have errored');
}
should.notEqual(mailNewslettersAdapter, mailNotificationsAdapter);
const secondMailNewslettersAdapter = adapterManager.getAdapter('mail:newsletters', 'custom');
should.equal(mailNewslettersAdapter, secondMailNewslettersAdapter);
});
});