diff --git a/packages/client/package.json b/packages/client/package.json index a04ec904..e9d42e96 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -18,7 +18,8 @@ "build": "yarn build:node && yarn build:browser", "build:node": "tsc -b && rollup -c rollup/rollup.config.ts", "build:browser": "tsc -b && rollup -c rollup/rollup.config.browser.ts", - "test": "yarn test:integration && yarn test:browser", + "test": "yarn test:unit && yarn test:integration && yarn test:browser", + "test:unit": "jest test/unit", "test:integration": "jest test/integration", "test:browser": "scripts/test-browser.sh", "coverage": "jest --coverage test/integration" diff --git a/packages/client/src/workspace.ts b/packages/client/src/workspace.ts index 7836f79d..d06eb6c3 100644 --- a/packages/client/src/workspace.ts +++ b/packages/client/src/workspace.ts @@ -73,7 +73,7 @@ export default new Proxy( } ); -class ServiceDefinition { +export class ServiceDefinition { constructor(readonly bytecode: Uint8Array, readonly idl: Idl) {} public async deploy(...args: any[]): Promise { @@ -83,8 +83,51 @@ class ServiceDefinition { } catch (e) { await configGateway(); } + + // Place the bytecode into the DeployOptions. + this.injectBytecode(args); + + // Finally, perform the deploy. return deploy(...args); } + + /** + * Inject the bytecode into the deploy options. + * Deploy options were either provided or not. + */ + private injectBytecode(args: any[]): void { + // Deploy options provided, so inject the bytecode into them. + if (this.idl.constructor.inputs.length + 1 === args.length) { + // Replace the args' options with a cloned version. + const options = (() => { + let o = args.pop(); + + if (typeof o !== 'object') { + throw new WorkspaceError('Options argument must be an object'); + } + if (o.bytecode) { + throw new WorkspaceError( + 'Bytecode should not be provided when deploying a workspace service' + ); + } + + o = JSON.parse(JSON.stringify(o)); + args.push(o); + return o; + })(); + + // Inject the bytecode into the options. + options.bytecode = this.bytecode; + } + // Deploy options not provided, so create them and then add them + // to the arguments. + else if (this.idl.constructor.inputs.length === args.length) { + const options = { + bytecode: this.bytecode, + }; + args.push(options); + } + } } async function configGateway(): Promise { diff --git a/packages/client/test/unit/workspace.spec.ts b/packages/client/test/unit/workspace.spec.ts new file mode 100644 index 00000000..e0185109 --- /dev/null +++ b/packages/client/test/unit/workspace.spec.ts @@ -0,0 +1,69 @@ +import { ServiceDefinition } from '../../src/workspace'; + +describe('ServiceDefinition', () => { + describe('injectBytecode', () => { + const bytecode = new Uint8Array([1, 2, 3]); + const idl = { + constructor: { + inputs: [{ type: 'string' }], + }, + }; + const serviceDef = new ServiceDefinition(bytecode, idl); + + it('injects bytecode into the deploy options', async () => { + const deployOptions = { gasLimit: '0x00' }; + const args = ['hello', deployOptions]; + + // @ts-ignore + serviceDef.injectBytecode(args); + + const expectedArgs = [ + 'hello', + { + ...deployOptions, + bytecode, + }, + ]; + + // Bytecode should be properly injected. + expect(args).toEqual(expectedArgs); + // Original options should not have been mutated. + expect(deployOptions).toEqual({ gasLimit: '0x00' }); + }); + + it('creates deploy options when none exist', async () => { + const args = ['hello']; + + // @ts-ignore + serviceDef.injectBytecode(args); + + const expectedArgs = [ + 'hello', + { + bytecode, + }, + ]; + + expect(args).toEqual(expectedArgs); + }); + + it('errors if bytecode is provided', async () => { + const args = [ + 'hello', + { + bytecode: new Uint8Array([1, 2, 3]), + }, + ]; + + try { + // @ts-ignore + serviceDef.injectBytecode(args); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toEqual( + 'Bytecode should not be provided when deploying a workspace service' + ); + } + }); + }); +}); diff --git a/packages/service/src/deploy/index.ts b/packages/service/src/deploy/index.ts index d1c031a7..8895acc4 100644 --- a/packages/service/src/deploy/index.ts +++ b/packages/service/src/deploy/index.ts @@ -35,7 +35,6 @@ import { DeployError } from '../error'; * value?: string | number; * }; * - * * @returns a Service object to make rpc requests with. */ export default async function deploy(...args: any[]): Promise { @@ -63,7 +62,7 @@ export default async function deploy(...args: any[]): Promise { * DeployOptions type, throwing an error if the arguments are * malformed in any way. */ -export async function extractOptions(args: any[]): Promise { +async function extractOptions(args: any[]): Promise { if (args.length === 0) { throw new DeployError(args, 'No deploy arguments provided'); }