From 8ec9151949ff2ad9d236bfa42eb31fd84b48cca3 Mon Sep 17 00:00:00 2001 From: Piotr Roslaniec Date: Wed, 30 Aug 2023 18:40:54 +0200 Subject: [PATCH] apply pr suggestions --- src/agents/contracts.ts | 3 +- src/conditions/base/index.ts | 9 +-- src/conditions/base/rpc.ts | 24 ++----- src/conditions/base/shared.ts | 18 ++++++ src/conditions/compound-condition.ts | 6 +- src/conditions/condition-expr.ts | 29 +-------- src/conditions/condition.ts | 64 +++++++++++-------- src/conditions/const.ts | 9 +++ src/conditions/index.ts | 5 +- src/conditions/zod.ts | 31 +++++++++ src/types.ts | 7 -- src/web3.ts | 8 +++ test/unit/conditions/base/condition.test.ts | 34 +--------- test/unit/conditions/base/contract.test.ts | 43 +++++++++---- test/unit/conditions/base/rpc.test.ts | 8 ++- test/unit/conditions/base/time.test.ts | 7 +- .../conditions/compound-condition.test.ts | 26 +++++--- test/unit/conditions/condition-expr.test.ts | 58 +++++++++++++---- test/unit/testVariables.ts | 2 +- 19 files changed, 226 insertions(+), 165 deletions(-) create mode 100644 src/conditions/base/shared.ts create mode 100644 src/conditions/zod.ts diff --git a/src/agents/contracts.ts b/src/agents/contracts.ts index 0ed03352f..a25d89115 100644 --- a/src/agents/contracts.ts +++ b/src/agents/contracts.ts @@ -1,4 +1,5 @@ -import { ChainId, ChecksumAddress } from '../types'; +import { ChecksumAddress } from '../types'; +import { ChainId } from '../web3'; type Contracts = { readonly SUBSCRIPTION_MANAGER: ChecksumAddress | undefined; diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts index a6a0abe36..b17914f34 100644 --- a/src/conditions/base/index.ts +++ b/src/conditions/base/index.ts @@ -35,9 +35,6 @@ export class TimeCondition extends Condition { } } -export { - contractConditionSchema, - type ContractConditionProps, -} from './contract'; -export { rpcConditionSchema, type RpcConditionProps } from './rpc'; -export { timeConditionSchema, type TimeConditionProps } from './time'; +export { type ContractConditionProps } from './contract'; +export { type RpcConditionProps } from './rpc'; +export { type TimeConditionProps } from './time'; diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index 2b16d7823..ea232d50f 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -1,29 +1,15 @@ import { z } from 'zod'; -import { ETH_ADDRESS_REGEXP, USER_ADDRESS_PARAM } from '../const'; +import { SUPPORTED_CHAIN_IDS } from '../const'; +import createUnionSchema from '../zod'; -export const returnValueTestSchema = z.object({ - index: z.number().optional(), - comparator: z.enum(['==', '>', '<', '>=', '<=', '!=']), - value: z.union([z.string(), z.number(), z.boolean()]), -}); - -export type ReturnValueTestProps = z.infer; - -const EthAddressOrUserAddressSchema = z.array( - z.union([z.string().regex(ETH_ADDRESS_REGEXP), z.literal(USER_ADDRESS_PARAM)]) -); +import { EthAddressOrUserAddressSchema, returnValueTestSchema } from './shared'; export const rpcConditionSchema = z.object({ conditionType: z.literal('rpc').default('rpc'), - chain: z.union([ - z.literal(137), - z.literal(80001), - z.literal(5), - z.literal(1), - ]), + chain: createUnionSchema(SUPPORTED_CHAIN_IDS), method: z.enum(['eth_getBalance', 'balanceOf']), - parameters: EthAddressOrUserAddressSchema, + parameters: z.array(EthAddressOrUserAddressSchema), returnValueTest: returnValueTestSchema, }); diff --git a/src/conditions/base/shared.ts b/src/conditions/base/shared.ts new file mode 100644 index 000000000..904329521 --- /dev/null +++ b/src/conditions/base/shared.ts @@ -0,0 +1,18 @@ +import { z } from 'zod'; + +import { ETH_ADDRESS_REGEXP, USER_ADDRESS_PARAM } from '../const'; + +export const returnValueTestSchema = z.object({ + index: z.number().optional(), + comparator: z.enum(['==', '>', '<', '>=', '<=', '!=']), + value: z.unknown(), +}); + +export type ReturnValueTestProps = z.infer; + +const EthAddressSchema = z.string().regex(ETH_ADDRESS_REGEXP); +const UserAddressSchema = z.literal(USER_ADDRESS_PARAM); +export const EthAddressOrUserAddressSchema = z.union([ + EthAddressSchema, + UserAddressSchema, +]); diff --git a/src/conditions/compound-condition.ts b/src/conditions/compound-condition.ts index 33c137a99..2c88e0bfe 100644 --- a/src/conditions/compound-condition.ts +++ b/src/conditions/compound-condition.ts @@ -1,8 +1,8 @@ import { z } from 'zod'; -import { contractConditionSchema } from './base'; -import { rpcConditionSchema } from './base'; -import { timeConditionSchema } from './base'; +import { contractConditionSchema } from './base/contract'; +import { rpcConditionSchema } from './base/rpc'; +import { timeConditionSchema } from './base/time'; export const compoundConditionSchema: z.ZodSchema = z.object({ conditionType: z.literal('compound').default('compound'), diff --git a/src/conditions/condition-expr.ts b/src/conditions/condition-expr.ts index 99358f84c..697687088 100644 --- a/src/conditions/condition-expr.ts +++ b/src/conditions/condition-expr.ts @@ -4,17 +4,7 @@ import { SemVer } from 'semver'; import { toBytes, toJSON } from '../utils'; -import { - CompoundCondition, - ContractCondition, - ContractConditionProps, - RpcCondition, - RpcConditionProps, - TimeCondition, - TimeConditionProps, -} from './base'; -import { CompoundConditionProps } from './compound-condition'; -import { Condition, ConditionProps } from './condition'; +import { Condition } from './condition'; import { ConditionContext, CustomContextParam } from './context'; export type ConditionExpressionJSON = { @@ -38,21 +28,6 @@ export class ConditionExpression { }; } - private static conditionFromObject(obj: ConditionProps): Condition { - switch (obj.conditionType) { - case 'rpc': - return new RpcCondition(obj as RpcConditionProps); - case 'time': - return new TimeCondition(obj as TimeConditionProps); - case 'contract': - return new ContractCondition(obj as ContractConditionProps); - case 'compound': - return new CompoundCondition(obj as CompoundConditionProps); - default: - throw new Error(`Invalid conditionType: ${obj.conditionType}`); - } - } - public static fromObj(obj: ConditionExpressionJSON): ConditionExpression { const receivedVersion = new SemVer(obj.version); const currentVersion = new SemVer(ConditionExpression.VERSION); @@ -70,7 +45,7 @@ export class ConditionExpression { ); } - const condition = this.conditionFromObject(obj.condition as ConditionProps); + const condition = Condition.fromObj(obj.condition); return new ConditionExpression(condition, obj.version); } diff --git a/src/conditions/condition.ts b/src/conditions/condition.ts index fe957bf32..b16444ef1 100644 --- a/src/conditions/condition.ts +++ b/src/conditions/condition.ts @@ -3,40 +3,40 @@ import { z } from 'zod'; import { objectEquals } from '../utils'; import { + CompoundCondition, + ContractCondition, ContractConditionProps, + RpcCondition, RpcConditionProps, + TimeCondition, TimeConditionProps, } from './base'; import { CompoundConditionProps } from './compound-condition'; import { USER_ADDRESS_PARAM } from './const'; -// Not using discriminated union because of inconsistent Zod types -// Some conditions have ZodEffect types because of .refine() calls -export type ConditionProps = - | RpcConditionProps - | TimeConditionProps - | ContractConditionProps - | CompoundConditionProps; +type ConditionSchema = z.ZodSchema; +export type ConditionProps = z.infer; export class Condition { constructor( - public readonly schema: z.ZodSchema, - public readonly value: - | RpcConditionProps - | TimeConditionProps - | ContractConditionProps - | CompoundConditionProps - ) {} + public readonly schema: ConditionSchema, + public readonly value: ConditionProps + ) { + const { data, error } = Condition.validate(schema, value); + if (error) { + throw new Error(`Invalid condition: ${JSON.stringify(error.issues)}`); + } + this.value = data; + } - public validate(override: Partial = {}): { + public static validate( + schema: ConditionSchema, + value: ConditionProps + ): { data?: ConditionProps; error?: z.ZodError; } { - const newValue = { - ...this.value, - ...override, - }; - const result = this.schema.safeParse(newValue); + const result = schema.safeParse(value); if (result.success) { return { data: result.data }; } @@ -48,18 +48,30 @@ export class Condition { } public toObj() { - const { data, error } = this.validate(this.value); + const { data, error } = Condition.validate(this.schema, this.value); if (error) { throw new Error(`Invalid condition: ${JSON.stringify(error.issues)}`); } return data; } - public static fromObj( - this: new (...args: unknown[]) => T, - obj: Record - ): T { - return new this(obj); + private static conditionFromObject(obj: ConditionProps): Condition { + switch (obj.conditionType) { + case 'rpc': + return new RpcCondition(obj as RpcConditionProps); + case 'time': + return new TimeCondition(obj as TimeConditionProps); + case 'contract': + return new ContractCondition(obj as ContractConditionProps); + case 'compound': + return new CompoundCondition(obj as CompoundConditionProps); + default: + throw new Error(`Invalid conditionType: ${obj.conditionType}`); + } + } + + public static fromObj(obj: ConditionProps): Condition { + return Condition.conditionFromObject(obj); } public equals(other: Condition) { diff --git a/src/conditions/const.ts b/src/conditions/const.ts index 3a8b941d2..9854c6a13 100644 --- a/src/conditions/const.ts +++ b/src/conditions/const.ts @@ -1,3 +1,12 @@ +import { ChainId } from '../web3'; + export const USER_ADDRESS_PARAM = ':userAddress'; export const ETH_ADDRESS_REGEXP = new RegExp('^0x[a-fA-F0-9]{40}$'); + +export const SUPPORTED_CHAIN_IDS = [ + ChainId.POLYGON, + ChainId.MUMBAI, + ChainId.GOERLI, + ChainId.MAINNET, +]; diff --git a/src/conditions/index.ts b/src/conditions/index.ts index 365b87a63..ac71ad556 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -8,7 +8,4 @@ export { } from './condition-expr'; export { ConditionContext, type CustomContextParam } from './context'; export { Condition, type ConditionProps } from './condition'; -export { - compoundConditionSchema, - type CompoundConditionProps, -} from './compound-condition'; +export { type CompoundConditionProps } from './compound-condition'; diff --git a/src/conditions/zod.ts b/src/conditions/zod.ts new file mode 100644 index 000000000..1829e1201 --- /dev/null +++ b/src/conditions/zod.ts @@ -0,0 +1,31 @@ +import { Primitive, z, ZodLiteral } from 'zod'; + +// Source: https://github.com/colinhacks/zod/issues/831#issuecomment-1063481764 +const createUnion = < + T extends Readonly<[Primitive, Primitive, ...Primitive[]]> +>( + values: T +) => { + const zodLiterals = values.map((value) => z.literal(value)) as unknown as [ + ZodLiteral, + ZodLiteral, + ...ZodLiteral[] + ]; + return z.union(zodLiterals); +}; + +function createUnionSchema(values: T) { + if (values.length === 0) { + return z.never(); + } + + if (values.length === 1) { + return z.literal(values[0]); + } + + return createUnion( + values as unknown as Readonly<[Primitive, Primitive, ...Primitive[]]> + ); +} + +export default createUnionSchema; diff --git a/src/types.ts b/src/types.ts index 2440dcc53..9befc497e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,3 @@ export type ChecksumAddress = string; export type HexEncodedBytes = string; export type Base64EncodedBytes = string; - -export enum ChainId { - POLYGON = 137, - MUMBAI = 80001, - GOERLI = 5, - MAINNET = 1, -} diff --git a/src/web3.ts b/src/web3.ts index 1b5dcd11a..a55508f8f 100644 --- a/src/web3.ts +++ b/src/web3.ts @@ -1,4 +1,12 @@ import { fromHexString } from './utils'; + +export enum ChainId { + POLYGON = 137, + MUMBAI = 80001, + GOERLI = 5, + MAINNET = 1, +} + export const toCanonicalAddress = (address: string): Uint8Array => { const ETH_ADDRESS_STRING_PREFIX = '0x'; const nonPrefixed = address.startsWith(ETH_ADDRESS_STRING_PREFIX) diff --git a/test/unit/conditions/base/condition.test.ts b/test/unit/conditions/base/condition.test.ts index baa0882a0..e91322af0 100644 --- a/test/unit/conditions/base/condition.test.ts +++ b/test/unit/conditions/base/condition.test.ts @@ -1,3 +1,4 @@ +import { Condition } from '../../../../src/conditions'; import { ContractCondition } from '../../../../src/conditions/base'; import { ERC721Balance, @@ -6,7 +7,6 @@ import { import { TEST_CHAIN_ID, TEST_CONTRACT_ADDR, - TEST_CONTRACT_ADDR_2, testContractConditionObj, } from '../../testVariables'; @@ -17,40 +17,10 @@ describe('validation', () => { }); it('accepts a correct schema', async () => { - const result = condition.validate(); + const result = Condition.validate(condition.schema, condition.value); expect(result.error).toBeUndefined(); expect(result.data.contractAddress).toEqual(TEST_CONTRACT_ADDR); }); - - it('accepts on a valid value override', async () => { - const validOverride = { - chain: TEST_CHAIN_ID, - contractAddress: TEST_CONTRACT_ADDR_2, - }; - const result = condition.validate(validOverride); - expect(result.error).toBeUndefined(); - expect(result.data).toMatchObject(validOverride); - }); - - it('rejects on an invalid value override', async () => { - const invalidOverride = { - chain: -1, - contractAddress: TEST_CONTRACT_ADDR, - }; - const result = condition.validate(invalidOverride); - expect(result.error).toBeDefined(); - expect(result.data).toBeUndefined(); - expect(result.error?.format()).toMatchObject({ - chain: { - _errors: [ - 'Invalid literal value, expected 137', - 'Invalid literal value, expected 80001', - 'Invalid literal value, expected 5', - 'Invalid literal value, expected 1', - ], - }, - }); - }); }); describe('serialization', () => { diff --git a/test/unit/conditions/base/contract.test.ts b/test/unit/conditions/base/contract.test.ts index 11ff45f29..002467210 100644 --- a/test/unit/conditions/base/contract.test.ts +++ b/test/unit/conditions/base/contract.test.ts @@ -6,14 +6,20 @@ import { ContractCondition, ContractConditionProps, } from '../../../../src/conditions/base'; -import { FunctionAbiProps } from '../../../../src/conditions/base/contract'; +import { + contractConditionSchema, + FunctionAbiProps, +} from '../../../../src/conditions/base/contract'; import { USER_ADDRESS_PARAM } from '../../../../src/conditions/const'; import { fakeProvider, fakeSigner } from '../../../utils'; import { testContractConditionObj, testFunctionAbi } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { - const result = new ContractCondition(testContractConditionObj).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + testContractConditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual(testContractConditionObj); @@ -25,7 +31,10 @@ describe('validation', () => { // Intentionally removing `contractAddress` contractAddress: undefined, } as unknown as ContractConditionProps; - const result = new ContractCondition(badContractCondition).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + badContractCondition + ); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); @@ -65,7 +74,10 @@ describe('accepts either standardContractType or functionAbi but not both or non standardContractType, functionAbi: undefined, } as typeof testContractConditionObj; - const result = new ContractCondition(conditionObj).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + conditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual(conditionObj); @@ -77,7 +89,10 @@ describe('accepts either standardContractType or functionAbi but not both or non functionAbi, standardContractType: undefined, } as typeof testContractConditionObj; - const result = new ContractCondition(conditionObj).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + conditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual(conditionObj); @@ -89,7 +104,10 @@ describe('accepts either standardContractType or functionAbi but not both or non standardContractType, functionAbi, } as typeof testContractConditionObj; - const result = new ContractCondition(conditionObj).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + conditionObj + ); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); @@ -106,7 +124,10 @@ describe('accepts either standardContractType or functionAbi but not both or non standardContractType: undefined, functionAbi: undefined, } as typeof testContractConditionObj; - const result = new ContractCondition(conditionObj).validate(); + const result = ContractCondition.validate( + contractConditionSchema, + conditionObj + ); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); @@ -176,12 +197,12 @@ describe('supports custom function abi', () => { }, }, ])('accepts well-formed functionAbi', ({ method, functionAbi }) => { - const result = new ContractCondition({ + const result = ContractCondition.validate(contractConditionSchema, { ...contractConditionObj, parameters: functionAbi.inputs.map((input) => `fake_parameter_${input}`), // functionAbi: functionAbi as FunctionAbiProps, method, - }).validate(); + }); expect(result.error).toBeUndefined(); expect(result.data).toBeDefined(); @@ -258,11 +279,11 @@ describe('supports custom function abi', () => { ])( 'rejects malformed functionAbi', ({ method, badField, expectedErrors, functionAbi }) => { - const result = new ContractCondition({ + const result = ContractCondition.validate(contractConditionSchema, { ...contractConditionObj, functionAbi: functionAbi as unknown as FunctionAbiProps, method, - }).validate(); + }); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); diff --git a/test/unit/conditions/base/rpc.test.ts b/test/unit/conditions/base/rpc.test.ts index a830d0f50..c4f658092 100644 --- a/test/unit/conditions/base/rpc.test.ts +++ b/test/unit/conditions/base/rpc.test.ts @@ -1,9 +1,13 @@ import { RpcCondition } from '../../../../src/conditions/base'; +import { rpcConditionSchema } from '../../../../src/conditions/base/rpc'; import { testRpcConditionObj } from '../../testVariables'; describe('validation', () => { it('accepts on a valid schema', () => { - const result = new RpcCondition(testRpcConditionObj).validate(); + const result = RpcCondition.validate( + rpcConditionSchema, + testRpcConditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual(testRpcConditionObj); @@ -16,7 +20,7 @@ describe('validation', () => { method: 'fake_invalid_method', } as unknown as typeof testRpcConditionObj; - const result = new RpcCondition(badRpcObj).validate(); + const result = RpcCondition.validate(rpcConditionSchema, badRpcObj); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); diff --git a/test/unit/conditions/base/time.test.ts b/test/unit/conditions/base/time.test.ts index e2d30cbed..0ace6466f 100644 --- a/test/unit/conditions/base/time.test.ts +++ b/test/unit/conditions/base/time.test.ts @@ -2,7 +2,8 @@ import { TimeCondition, TimeConditionProps, } from '../../../../src/conditions/base'; -import { ReturnValueTestProps } from '../../../../src/conditions/base/rpc'; +import { ReturnValueTestProps } from '../../../../src/conditions/base/shared'; +import { timeConditionSchema } from '../../../../src/conditions/base/time'; describe('validation', () => { const returnValueTest: ReturnValueTestProps = { @@ -18,7 +19,7 @@ describe('validation', () => { method: 'blocktime', chain: 1, }; - const result = new TimeCondition(conditionObj).validate(); + const result = TimeCondition.validate(timeConditionSchema, conditionObj); expect(result.error).toBeUndefined(); expect(result.data).toEqual(conditionObj); @@ -34,7 +35,7 @@ describe('validation', () => { }, chain: 5, } as unknown as TimeConditionProps; - const result = new TimeCondition(badObj).validate(); + const result = TimeCondition.validate(timeConditionSchema, badObj); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); diff --git a/test/unit/conditions/compound-condition.test.ts b/test/unit/conditions/compound-condition.test.ts index f558fd8c9..2f81a8bc8 100644 --- a/test/unit/conditions/compound-condition.test.ts +++ b/test/unit/conditions/compound-condition.test.ts @@ -1,4 +1,6 @@ +import { Condition } from '../../../src/conditions'; import { CompoundCondition } from '../../../src/conditions/base'; +import { compoundConditionSchema } from '../../../src/conditions/compound-condition'; import { testContractConditionObj, testRpcConditionObj, @@ -11,7 +13,7 @@ describe('validation', () => { operator: 'or', operands: [testContractConditionObj, testTimeConditionObj], }; - const result = new CompoundCondition(conditionObj).validate(); + const result = Condition.validate(compoundConditionSchema, conditionObj); expect(result.error).toBeUndefined(); expect(result.data).toEqual({ @@ -25,7 +27,10 @@ describe('validation', () => { operator: 'and', operands: [testContractConditionObj, testTimeConditionObj], }; - const result = new CompoundCondition(conditionObj).validate(); + const result = CompoundCondition.validate( + compoundConditionSchema, + conditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual({ @@ -35,10 +40,10 @@ describe('validation', () => { }); it('rejects an invalid operator', () => { - const result = new CompoundCondition({ + const result = CompoundCondition.validate(compoundConditionSchema, { operator: 'not-an-operator', operands: [testRpcConditionObj, testTimeConditionObj], - }).validate(); + }); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); @@ -52,10 +57,10 @@ describe('validation', () => { }); it('rejects invalid number of operands = 0', () => { - const result = new CompoundCondition({ + const result = CompoundCondition.validate(compoundConditionSchema, { operator: 'or', operands: [], - }).validate(); + }); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); @@ -67,10 +72,10 @@ describe('validation', () => { }); it('rejects invalid number of operands = 1', () => { - const result = new CompoundCondition({ + const result = CompoundCondition.validate(compoundConditionSchema, { operator: 'or', operands: [testRpcConditionObj], - }).validate(); + }); expect(result.error).toBeDefined(); expect(result.data).toBeUndefined(); expect(result.error?.format()).toMatchObject({ @@ -93,7 +98,10 @@ describe('validation', () => { }, ], }; - const result = new CompoundCondition(conditionObj).validate(); + const result = CompoundCondition.validate( + compoundConditionSchema, + conditionObj + ); expect(result.error).toBeUndefined(); expect(result.data).toEqual({ conditionType: 'compound', diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index bcc78e7a7..ff53730e1 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -7,6 +7,7 @@ import { ContractConditionProps, RpcCondition, TimeCondition, + TimeConditionProps, } from '../../../src/conditions/base'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { ERC721Balance } from '../../../src/conditions/predefined'; @@ -66,7 +67,7 @@ describe('condition set', () => { describe('equality', () => { const conditionExprCurrentVersion = new ConditionExpression(rpcCondition); - it('same version and condition', async () => { + it('same version and condition', () => { const conditionExprSameCurrentVersion = new ConditionExpression( rpcCondition, ConditionExpression.VERSION @@ -76,7 +77,7 @@ describe('condition set', () => { ).toBeTruthy(); }); - it('different minor/patch version but same condition', async () => { + it('different minor/patch version but same condition', () => { const conditionExprOlderMinorVersion = new ConditionExpression( rpcCondition, '0.1.0' @@ -96,7 +97,7 @@ describe('condition set', () => { ).not.toBeTruthy(); }); - it('minor/patch number greater than major; still older', async () => { + it('minor/patch number greater than major; still older', () => { const conditionExprOlderMinorVersion = new ConditionExpression( rpcCondition, '0.9.0' @@ -139,7 +140,7 @@ describe('condition set', () => { contractConditionWithAbi, timeCondition, compoundCondition, - ])('same version but different condition', async (condition) => { + ])('same version but different condition', (condition) => { const conditionExprSameVersionDifferentCondition = new ConditionExpression(condition); expect( @@ -149,7 +150,7 @@ describe('condition set', () => { ).not.toBeTruthy(); }); - it('same contract condition although using erc721 helper', async () => { + it('same contract condition although using erc721 helper', () => { const erc721ConditionExpr = new ConditionExpression( erc721BalanceCondition ); @@ -172,7 +173,7 @@ describe('condition set', () => { rpcCondition, timeCondition, compoundCondition, - ])('serializes to and from json', async (condition) => { + ])('serializes to and from json', (condition) => { const conditionExpr = new ConditionExpression(condition); const conditionExprJson = conditionExpr.toJson(); expect(conditionExprJson).toBeDefined(); @@ -187,7 +188,7 @@ describe('condition set', () => { expect(conditionExprFromJson.equals(conditionExprFromJson)).toBeTruthy(); }); - it('incompatible version', async () => { + it('incompatible version', () => { const currentVersion = new SemVer(ConditionExpression.VERSION); const invalidVersion = currentVersion.inc('major'); expect(() => { @@ -202,7 +203,7 @@ describe('condition set', () => { it.each(['version', 'x.y', 'x.y.z', '-1,0.0', '1.0.0.0.0.0.0'])( 'invalid versions', - async (invalidVersion) => { + (invalidVersion) => { expect(() => { ConditionExpression.fromObj({ version: invalidVersion, @@ -212,7 +213,36 @@ describe('condition set', () => { } ); - it('erc721 condition serialization', async () => { + it.each(['_invalid_condition_type_', undefined as unknown as string])( + 'rejects an invalid condition type', + (invalidConditionType) => { + const conditionObj = { + ...testTimeConditionObj, + conditionType: invalidConditionType, + } as unknown as TimeConditionProps; + expect(() => { + ConditionExpression.fromObj({ + version: ConditionExpression.VERSION, + condition: conditionObj, + }); + }).toThrow(`Invalid conditionType: ${invalidConditionType}`); + } + ); + + it('rejects a mismatched condition type', () => { + const conditionObj = { + ...testTimeConditionObj, + conditionType: 'rpc', + } as unknown as TimeConditionProps; + expect(() => { + ConditionExpression.fromObj({ + version: ConditionExpression.VERSION, + condition: conditionObj, + }); + }).toThrow(/^Invalid condition/); + }); + + it('erc721 condition serialization', () => { const conditionExpr = new ConditionExpression(erc721BalanceCondition); const erc721BalanceConditionObj = erc721BalanceCondition.toObj(); @@ -240,7 +270,7 @@ describe('condition set', () => { expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); }); - it('contract condition no abi serialization', async () => { + it('contract condition no abi serialization', () => { const conditionExpr = new ConditionExpression(contractConditionNoAbi); const conditionExprJson = conditionExpr.toJson(); @@ -272,7 +302,7 @@ describe('condition set', () => { expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); }); - it('contract condition with abi serialization', async () => { + it('contract condition with abi serialization', () => { const conditionExpr = new ConditionExpression(contractConditionWithAbi); const conditionExprJson = conditionExpr.toJson(); @@ -305,7 +335,7 @@ describe('condition set', () => { expect(conditionExprFromJson.condition).toBeInstanceOf(ContractCondition); }); - it('time condition serialization', async () => { + it('time condition serialization', () => { const conditionExpr = new ConditionExpression(timeCondition); const conditionExprJson = conditionExpr.toJson(); @@ -328,7 +358,7 @@ describe('condition set', () => { expect(conditionExprFromJson.condition).toBeInstanceOf(TimeCondition); }); - it('rpc condition serialization', async () => { + it('rpc condition serialization', () => { const conditionExpr = new ConditionExpression(rpcCondition); const conditionExprJson = conditionExpr.toJson(); @@ -352,7 +382,7 @@ describe('condition set', () => { expect(conditionExprFromJson.condition).toBeInstanceOf(RpcCondition); }); - it('compound condition serialization', async () => { + it('compound condition serialization', () => { const conditionExpr = new ConditionExpression(compoundCondition); const compoundConditionObj = compoundCondition.toObj(); diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index c7f85dec8..1acfe86eb 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -4,7 +4,7 @@ import { TimeConditionProps, } from '../../src/conditions/base'; import { FunctionAbiProps } from '../../src/conditions/base/contract'; -import { ReturnValueTestProps } from '../../src/conditions/base/rpc'; +import { ReturnValueTestProps } from '../../src/conditions/base/shared'; export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174,