Skip to content

Commit

Permalink
fix: move logic to utils
Browse files Browse the repository at this point in the history
  • Loading branch information
shuffledex committed Aug 3, 2020
1 parent 0568c3f commit 8bbf1be
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 166 deletions.
64 changes: 1 addition & 63 deletions src/api/procedures/__tests__/cancelProposal.ts
@@ -1,7 +1,5 @@
import { Call } from '@polkadot/types/interfaces/runtime';
import sinon from 'sinon';

import { ProposalStage } from '~/api/entities/Proposal/types';
import { isAuthorized, Params, prepareCancelProposal } from '~/api/procedures/cancelProposal';
import { PostTransactionValue } from '~/base';
import { Context } from '~/context';
Expand All @@ -11,10 +9,7 @@ import { Mocked } from '~/testUtils/types';
import { PolymeshTx } from '~/types/internal';
import * as utilsModule from '~/utils';

jest.mock(
'~/api/entities/Proposal',
require('~/testUtils/mocks/entities').mockProposalModule('~/api/entities/Proposal')
);
jest.mock('~/api/procedures/utils');

describe('cancelProposal procedure', () => {
const pipId = 10;
Expand Down Expand Up @@ -57,64 +52,7 @@ describe('cancelProposal procedure', () => {
dsMockUtils.cleanup();
});

test('should throw an error if the proposal is not in pending state', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Killed'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

let error;
try {
await prepareCancelProposal.call(proc, { pipId });
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal must be in pending state');
});

test('should throw an error if the proposal is not in the cool off period', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

let error;
try {
await prepareCancelProposal.call(proc, { pipId });
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal can be canceled only during its cool off period');
});

test('should add a cancel proposal transaction to the queue', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getStage: ProposalStage.CoolOff,
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

await prepareCancelProposal.call(proc, { pipId });
Expand Down
60 changes: 1 addition & 59 deletions src/api/procedures/__tests__/editProposal.ts
@@ -1,8 +1,6 @@
import { Text } from '@polkadot/types';
import { Call } from '@polkadot/types/interfaces/runtime';
import sinon from 'sinon';

import { ProposalStage } from '~/api/entities/Proposal/types';
import { isAuthorized, Params, prepareEditProposal } from '~/api/procedures/editProposal';
import { PostTransactionValue } from '~/base';
import { Context } from '~/context';
Expand All @@ -16,6 +14,7 @@ jest.mock(
'~/api/entities/Proposal',
require('~/testUtils/mocks/entities').mockProposalModule('~/api/entities/Proposal')
);
jest.mock('~/api/procedures/utils');

describe('editProposal procedure', () => {
const pipId = 10;
Expand Down Expand Up @@ -80,64 +79,7 @@ describe('editProposal procedure', () => {
);
});

test('should throw an error if the proposal is not in pending state', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Killed'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

let error;
try {
await prepareEditProposal.call(proc, { pipId, ...args });
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal must be in pending state');
});

test('should throw an error if the proposal is not in the cool off period', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

let error;
try {
await prepareEditProposal.call(proc, { pipId, ...args });
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal is mutable only during its cool off period');
});

test('should add an edit proposal transaction to the queue', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getStage: ProposalStage.CoolOff,
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
},
});

const proc = procedureMockUtils.getInstance<Params, void>(mockContext);

await prepareEditProposal.call(proc, { pipId, ...args });
Expand Down
102 changes: 102 additions & 0 deletions src/api/procedures/__tests__/utils.ts
@@ -0,0 +1,102 @@
import { Call } from '@polkadot/types/interfaces/runtime';

import { ProposalStage } from '~/api/entities/Proposal/types';
import { Context } from '~/context';
import { dsMockUtils, entityMockUtils } from '~/testUtils/mocks';
import { Mocked } from '~/testUtils/types';

import { assertProposalUnlocked } from '../utils';

jest.mock(
'~/api/entities/Proposal',
require('~/testUtils/mocks/entities').mockProposalModule('~/api/entities/Proposal')
);

describe('assertProposalUnlocked', () => {
const pipId = 10;
const mockAddress = 'someAddress';

let mockContext: Mocked<Context>;

beforeAll(() => {
dsMockUtils.initMocks({
contextOptions: {
currentPairAddress: mockAddress,
},
});
entityMockUtils.initMocks();
});

beforeEach(() => {
mockContext = dsMockUtils.getContextInstance();
});

afterEach(() => {
entityMockUtils.reset();
dsMockUtils.reset();
});

afterAll(() => {
entityMockUtils.cleanup();
dsMockUtils.cleanup();
});

test('should throw an error if the proposal is not in pending state', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Killed'),
}),
},
});

let error;
try {
await assertProposalUnlocked(pipId, mockContext);
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal must be in pending state');
});

test('should throw an error if the proposal is not in the cool off period', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
},
});

let error;
try {
await assertProposalUnlocked(pipId, mockContext);
} catch (err) {
error = err;
}

expect(error.message).toBe('The proposal can be canceled only during its cool off period');
});

test('should does not throw an error if the proposal is unlocked', async () => {
entityMockUtils.configureMocks({
proposalOptions: {
getDetails: dsMockUtils.createMockPip({
id: dsMockUtils.createMockU32(),
proposal: ('proposal' as unknown) as Call,
state: dsMockUtils.createMockProposalState('Pending'),
}),
getStage: ProposalStage.CoolOff,
},
});

const result = await assertProposalUnlocked(pipId, mockContext);

expect(result).toBeUndefined();
});
});
27 changes: 4 additions & 23 deletions src/api/procedures/cancelProposal.ts
@@ -1,9 +1,8 @@
import { Proposal } from '~/api/entities';
import { ProposalStage } from '~/api/entities/Proposal/types';
import { PolymeshError, Procedure } from '~/base';
import { ErrorCode } from '~/types';
import { Procedure } from '~/base';
import { accountKeyToString } from '~/utils';

import { assertProposalUnlocked } from './utils';

export type Params = { pipId: number };

/**
Expand All @@ -21,25 +20,7 @@ export async function prepareCancelProposal(
} = this;
const { pipId } = args;

const proposal = new Proposal({ pipId }, context);

const [details, stage] = await Promise.all([proposal.getDetails(), proposal.getStage()]);

const { state } = details;

if (!state.isPending) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal must be in pending state',
});
}

if (stage !== ProposalStage.CoolOff) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal can be canceled only during its cool off period',
});
}
await assertProposalUnlocked(pipId, context);

this.addTransaction(tx.pips.cancelProposal, {}, pipId);
}
Expand Down
23 changes: 2 additions & 21 deletions src/api/procedures/editProposal.ts
@@ -1,5 +1,4 @@
import { Proposal } from '~/api/entities';
import { ProposalStage } from '~/api/entities/Proposal/types';
import { assertProposalUnlocked } from '~/api/procedures/utils';
import { PolymeshError, Procedure } from '~/base';
import { ErrorCode } from '~/types';
import { accountKeyToString, stringToText } from '~/utils';
Expand Down Expand Up @@ -38,25 +37,7 @@ export async function prepareEditProposal(
});
}

const proposal = new Proposal({ pipId }, context);

const [details, stage] = await Promise.all([proposal.getDetails(), proposal.getStage()]);

const { state } = details;

if (!state.isPending) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal must be in pending state',
});
}

if (stage !== ProposalStage.CoolOff) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal is mutable only during its cool off period',
});
}
await assertProposalUnlocked(pipId, context);

this.addTransaction(
tx.pips.amendProposal,
Expand Down
30 changes: 30 additions & 0 deletions src/api/procedures/utils.ts
@@ -0,0 +1,30 @@
import { Proposal } from '~/api/entities';
import { ProposalStage } from '~/api/entities/Proposal/types';
import { PolymeshError } from '~/base';
import { Context } from '~/context';
import { ErrorCode } from '~/types';

/**
* @hidden
*/
export async function assertProposalUnlocked(pipId: number, context: Context): Promise<void> {
const proposal = new Proposal({ pipId }, context);

const [details, stage] = await Promise.all([proposal.getDetails(), proposal.getStage()]);

const { state } = details;

if (!state.isPending) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal must be in pending state',
});
}

if (stage !== ProposalStage.CoolOff) {
throw new PolymeshError({
code: ErrorCode.ValidationError,
message: 'The proposal can be canceled only during its cool off period',
});
}
}

0 comments on commit 8bbf1be

Please sign in to comment.