From d18bfd010769c876e660a61105c4fb1f657f4e05 Mon Sep 17 00:00:00 2001 From: sbruens Date: Tue, 16 Jan 2024 12:24:07 -0500 Subject: [PATCH] Expand test scenarios for key generation to `createAccessKey`. --- src/shadowbox/server/manager_service.spec.ts | 437 +++++++++---------- 1 file changed, 205 insertions(+), 232 deletions(-) diff --git a/src/shadowbox/server/manager_service.spec.ts b/src/shadowbox/server/manager_service.spec.ts index 7c195636..c5023c4c 100644 --- a/src/shadowbox/server/manager_service.spec.ts +++ b/src/shadowbox/server/manager_service.spec.ts @@ -306,251 +306,224 @@ describe('ShadowsocksManagerService', () => { }); }); - describe('createNewAccessKey', () => { - it('generates a unique ID', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); + describe('creating new access key', () => { + let repo: ServerAccessKeyRepository; + let service: ShadowsocksManagerService; - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.id).toEqual('0'); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {}}, res, done); + beforeEach(() => { + repo = getAccessKeyRepository(); + service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); }); - it('rejects requests with ID parameter set', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {id: 'foobar'}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); + describe('handling the access key identifier', () => { + describe("with 'createNewAccessKey'", () => { + it('generates a unique ID', (done) => { + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.id).toEqual('0'); + responseProcessed = true; // required for afterEach to pass. + }, + }; + service.createNewAccessKey({params: {}}, res, done); + }); + it('rejects requests with ID parameter set', (done) => { + const res = {send: (_httpCode, _data) => {}}; + service.createNewAccessKey({params: {id: 'foobar'}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); }); - }); - it('verify default method', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - // Verify that response returns a key with the expected properties. - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES); - expect(data.method).toEqual('chacha20-ietf-poly1305'); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {}}, res, done); - }); - it('non-default method gets set', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - // Verify that response returns a key with the expected properties. - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES); - expect(data.method).toEqual('aes-256-gcm'); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {method: 'aes-256-gcm'}}, res, done); - }); - it('use default name is params is not defined', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.name).toEqual(''); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {}}, res, done); - }); - it('rejects non-string name', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {name: Number('9876')}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('defined name is equal to stored', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const ACCESSKEY_NAME = 'accesskeyname'; - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.name).toEqual(ACCESSKEY_NAME); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {name: ACCESSKEY_NAME}}, res, done); - }); - it('limit can be undefined', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.limit).toBeUndefined(); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {}}, res, done); - }); - it('rejects non-numeric limits', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const ACCESSKEY_LIMIT = {bytes: '9876'}; - - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {limit: ACCESSKEY_LIMIT}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('defined limit is equal to stored', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const ACCESSKEY_LIMIT = {bytes: 9876}; - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.dataLimit).toEqual(ACCESSKEY_LIMIT); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createNewAccessKey({params: {limit: ACCESSKEY_LIMIT}}, res, done); - }); - it('method must be of type string', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {method: Number('9876')}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('method must be valid', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {method: 'abcdef'}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('Create returns a 500 when the repository throws an exception', (done) => { - const repo = getAccessKeyRepository(); - spyOn(repo, 'createNewAccessKey').and.throwError('cannot write to disk'); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createNewAccessKey({params: {method: 'aes-192-gcm'}}, res, (error) => { - expect(error.statusCode).toEqual(500); - responseProcessed = true; // required for afterEach to pass. - done(); + describe("with 'createAccessKey'", () => { + it('rejects requests without ID parameter set', (done) => { + const res = {send: (_httpCode, _data) => {}}; + service.createAccessKey({params: {}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('rejects non-string ID', (done) => { + const res = {send: (_httpCode, _data) => {}}; + service.createAccessKey({params: {id: Number('9876')}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('creates key with provided ID', (done) => { + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.id).toEqual('myKeyId'); + responseProcessed = true; // required for afterEach to pass. + }, + }; + service.createAccessKey({params: {id: 'myKeyId'}}, res, done); + }); }); }); - it('generates a new password when no password is provided', async (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); + const conditions = [ + {methodName: 'createNewAccessKey', accessKeyId: undefined}, + {methodName: 'createAccessKey', accessKeyId: 'myKeyId'}, + ]; - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.password).toBeDefined(); - responseProcessed = true; // required for afterEach to pass. - }, - }; - await service.createNewAccessKey({params: {}}, res, done); - }); + for (const {methodName, accessKeyId} of conditions) { + describe(`with '${methodName}'`, () => { + let serviceMethod: (req, res, next) => Promise; - it('uses the provided password when one is provided', async (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); + beforeEach(() => { + serviceMethod = service[methodName].bind(service); + }); - const PASSWORD = '8iu8V8EeoFVpwQvQeS9wiD'; - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.password).toEqual(PASSWORD); - responseProcessed = true; // required for afterEach to pass. - }, - }; - await service.createNewAccessKey({params: {password: PASSWORD}}, res, done); - }); + it('verify default method', (done) => { + // Verify that response returns a key with the expected properties. + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES); + expect(data.method).toEqual('chacha20-ietf-poly1305'); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId}}, res, done); + }); + it('non-default method gets set', (done) => { + // Verify that response returns a key with the expected properties. + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES); + expect(data.method).toEqual('aes-256-gcm'); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId, method: 'aes-256-gcm'}}, res, done); + }); + it('use default name is params is not defined', (done) => { + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.name).toEqual(''); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId}}, res, done); + }); + it('rejects non-string name', (done) => { + const res = {send: (_httpCode, _data) => {}}; + serviceMethod({params: {id: accessKeyId, name: Number('9876')}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('defined name is equal to stored', (done) => { + const ACCESSKEY_NAME = 'accesskeyname'; + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.name).toEqual(ACCESSKEY_NAME); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId, name: ACCESSKEY_NAME}}, res, done); + }); + it('limit can be undefined', (done) => { + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.limit).toBeUndefined(); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId}}, res, done); + }); + it('rejects non-numeric limits', (done) => { + const ACCESSKEY_LIMIT = {bytes: '9876'}; - it('rejects a password that is not a string', async (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - const PASSWORD = Number.MAX_SAFE_INTEGER; - const res = {send: SEND_NOTHING}; - await service.createNewAccessKey({params: {password: PASSWORD}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); + const res = {send: (_httpCode, _data) => {}}; + serviceMethod({params: {id: accessKeyId, limit: ACCESSKEY_LIMIT}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('defined limit is equal to stored', (done) => { + const ACCESSKEY_LIMIT = {bytes: 9876}; + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.dataLimit).toEqual(ACCESSKEY_LIMIT); + responseProcessed = true; // required for afterEach to pass. + }, + }; + serviceMethod({params: {id: accessKeyId, limit: ACCESSKEY_LIMIT}}, res, done); + }); + it('method must be of type string', (done) => { + const res = {send: (_httpCode, _data) => {}}; + serviceMethod({params: {id: accessKeyId, method: Number('9876')}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('method must be valid', (done) => { + const res = {send: (_httpCode, _data) => {}}; + serviceMethod({params: {id: accessKeyId, method: 'abcdef'}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + it('Create returns a 500 when the repository throws an exception', (done) => { + spyOn(repo, 'createNewAccessKey').and.throwError('cannot write to disk'); + const res = {send: (_httpCode, _data) => {}}; + serviceMethod({params: {id: accessKeyId, method: 'aes-192-gcm'}}, res, (error) => { + expect(error.statusCode).toEqual(500); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); + + it('generates a new password when no password is provided', async (done) => { + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.password).toBeDefined(); + responseProcessed = true; // required for afterEach to pass. + }, + }; + await serviceMethod({params: {id: accessKeyId}}, res, done); + }); + + it('uses the provided password when one is provided', async (done) => { + const PASSWORD = '8iu8V8EeoFVpwQvQeS9wiD'; + const res = { + send: (httpCode, data) => { + expect(httpCode).toEqual(201); + expect(data.password).toEqual(PASSWORD); + responseProcessed = true; // required for afterEach to pass. + }, + }; + await serviceMethod({params: {id: accessKeyId, password: PASSWORD}}, res, done); + }); + + it('rejects a password that is not a string', async (done) => { + const PASSWORD = Number.MAX_SAFE_INTEGER; + const res = {send: SEND_NOTHING}; + await serviceMethod({params: {id: accessKeyId, password: PASSWORD}}, res, (error) => { + expect(error.statusCode).toEqual(400); + responseProcessed = true; // required for afterEach to pass. + done(); + }); + }); }); - }); - }); - describe('createAccessKey', () => { - it('rejects requests without ID parameter set', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createAccessKey({params: {}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('rejects non-string ID', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = {send: (_httpCode, _data) => {}}; - service.createAccessKey({params: {id: Number('9876')}}, res, (error) => { - expect(error.statusCode).toEqual(400); - responseProcessed = true; // required for afterEach to pass. - done(); - }); - }); - it('creates key with provided ID', (done) => { - const repo = getAccessKeyRepository(); - const service = new ShadowsocksManagerServiceBuilder().accessKeys(repo).build(); - - const res = { - send: (httpCode, data) => { - expect(httpCode).toEqual(201); - expect(data.id).toEqual('myKeyId'); - responseProcessed = true; // required for afterEach to pass. - }, - }; - service.createAccessKey({params: {id: 'myKeyId'}}, res, done); - }); + } }); describe('setPortForNewAccessKeys', () => { it('changes ports for new access keys', async (done) => {