Expand test scenarios for key generation to createAccessKey.

This commit is contained in:
sbruens 2024-01-16 12:24:07 -05:00
parent f9f70c2045
commit d18bfd0107

View file

@ -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<void>;
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) => {