Skip to content

Commit

Permalink
Merge pull request #93 from josejulio/expand-hydra-tests
Browse files Browse the repository at this point in the history
Expand tests
  • Loading branch information
Gerard097 committed Jun 16, 2021
2 parents f6a372e + 0a07d05 commit dc191c3
Show file tree
Hide file tree
Showing 14 changed files with 456 additions and 97 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/hydra-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: test-hydra
on:
push:
branches: [ master, develop ]
pull_request:
pull_request_target:
branches: [ master, develop ]

jobs:
Expand All @@ -25,6 +25,7 @@ jobs:
env:
CODE: "/github/workspace/"
PATCH_WASM_LD: "1"
- run: yarn test
- run: mkdir -p ~/.hydra && echo $HYDRA_AUTH > ~/.hydra/auth.json && ls -l ~/.hydra
env:
HYDRA_AUTH: ${{ secrets.HYDRA_AUTH }}
- run: yarn test --forceExit --maxWorkers=4 --detectOpenHandles
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testTimeout: 60 * 1e3
testTimeout: 120 * 1e3
};
50 changes: 34 additions & 16 deletions tests/DaoBlockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { loadConfig, Blockchain, Contract } from '@klevoya/hydra';
import { THydraConfig } from '@klevoya/hydra/lib/config/hydra';
import Account from '@klevoya/hydra/lib/main/account';
import { Document } from './types/Document';
import { Edge } from './types/Edge';
import { last } from './utils/Arrays';
import { getDocumentByHash, getDocumentsByType } from './utils/Dao';
import { getAccountPermission } from './utils/Permissions';

export interface DaoSettings {
votingDurationSeconds: number;
Expand All @@ -20,13 +24,22 @@ export interface DaoPeerContracts {
bank: Account;
}

export interface Member {
account: Account;
doc: Document;
}

export class DaoBlockchain extends Blockchain {

readonly config: THydraConfig;
readonly dao: Account;
readonly settings: DaoSettings;
readonly peerContracts: DaoPeerContracts;
readonly members: Array<Account>;
readonly members: Array<Member>;

// There should be a better way to get this - But currently seems stable
readonly rootHash = 'D9B40C418C850A65B71CA55ABB0DE9B0E4646A02B95B230E3917E877610BFAE5';
private root: Document;

private constructor(config: THydraConfig, settings: DaoSettings) {
super(config);
Expand All @@ -51,13 +64,17 @@ export class DaoBlockchain extends Blockchain {
}

for (let index = 0; index < testSettings.createMembers; ++index) {
const member: Account = await blockchain.createMember(`mem${index + 1}.hypha`);
blockchain.members.push(member);
const account: Account = await blockchain.createMember(`mem${index + 1}.hypha`);
const doc = last(getDocumentsByType(blockchain.getDaoDocuments(), 'member'));
blockchain.members.push({
account,
doc
});
}

// Member 0 is always awarded 99 HVOICE (for a total of 100)
if (testSettings.createMembers > 0) {
await blockchain.increaseVoice(blockchain.members[0].accountName, '99.00 HVOICE');
await blockchain.increaseVoice(blockchain.members[0].account.accountName, '99.00 HVOICE');
}

}
Expand All @@ -66,15 +83,6 @@ export class DaoBlockchain extends Blockchain {
return blockchain;
}

static getAccountPermission(account: Account): import('eosjs/dist/eosjs-serialize').Authorization[] {
return [
{
actor: account.accountName,
permission: 'active'
}
];
}

createContract(accountName: string, templateName: string): Account {
const account = this.createAccount(accountName);
account.setContract(this.contractTemplates[templateName]);
Expand All @@ -100,7 +108,7 @@ export class DaoBlockchain extends Blockchain {
await this.dao.contract.apply({
applicant: account.accountName,
content: 'Apply to DAO'
}, DaoBlockchain.getAccountPermission(account));
}, getAccountPermission(account));

await this.dao.contract.enroll({
enroller: this.dao.accountName,
Expand All @@ -116,14 +124,14 @@ export class DaoBlockchain extends Blockchain {
to: this.dao.accountName,
quantity,
memo: 'Increasing voice'
}, DaoBlockchain.getAccountPermission(this.dao));
}, getAccountPermission(this.dao));

await this.peerContracts.voice.contract.transfer({
from: this.dao.accountName,
to: accountName,
quantity,
memo: 'Increasing voice'
}, DaoBlockchain.getAccountPermission(this.dao));
}, getAccountPermission(this.dao));
}

async setup() {
Expand Down Expand Up @@ -168,10 +176,20 @@ export class DaoBlockchain extends Blockchain {
key: 'treasury_contract',
value: [ 'name', this.peerContracts.bank.accountName ]
});

this.root = getDocumentByHash(this.getDaoDocuments(), this.rootHash);
}

public getDaoDocuments(): Array<Document> {
return this.dao.getTableRowsScoped('documents')['dao'];
}

public getDaoEdges(): Array<Edge> {
return this.dao.getTableRowsScoped('edges')['dao'];
}

public getRoot(): Document {
return this.root;
}

}
54 changes: 0 additions & 54 deletions tests/dao.test.ts

This file was deleted.

111 changes: 111 additions & 0 deletions tests/proposal.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { setupEnvironment } from "./setup";
import { Document } from './types/Document';
import { last } from './utils/Arrays';
import { getDocumentsByType } from './utils/Dao';
import { DocumentBuilder } from './utils/DocumentBuilder';
import { getDaoExpect } from './utils/Expect';
import { getAccountPermission } from './utils/Permissions';

describe('Proposal', () => {
const getSampleRole = (title: string = 'Underwater Basketweaver'): Document => DocumentBuilder
.builder()
.contentGroup(builder => {
builder
.groupLabel('details')
.string('title', title)
.string('description', 'Weave baskets at the bottom of the sea')
.asset('annual_usd_salary', '150000.00 USD')
.int64('start_period', 0)
.int64('end_period', 9)
.int64('fulltime_capacity_x100', 100)
.int64('min_time_share_x100', 50)
.int64('min_deferred_x100', 50)
})
.build();

it('Proposal failed', async() => {
const environment = await setupEnvironment();

const now = new Date();
const whenVoteExpires = new Date();
whenVoteExpires.setSeconds( whenVoteExpires.getSeconds() + environment.settings.votingDurationSeconds + 1);

environment.setCurrentTime(now);

// Proposing
await environment.dao.contract.propose({
proposer: environment.members[0].account.accountName,
proposal_type: 'role',
content_groups: getSampleRole().content_groups
});

const proposal = last(getDocumentsByType(
environment.getDaoDocuments(),
'role'
));

const daoExpect = getDaoExpect(environment);
daoExpect.toHaveEdge(environment.getRoot(), proposal, 'proposal');
daoExpect.toHaveEdge(environment.members[0].doc, proposal, 'owns');
daoExpect.toHaveEdge(proposal, environment.members[0].doc, 'ownedby');

// Sets the time to the end of proposal
environment.setCurrentTime(whenVoteExpires);

// Now we can close the proposal
await environment.dao.contract.closedocprop({
proposal_hash: proposal.hash
}, getAccountPermission(environment.members[0].account));

daoExpect.toHaveEdge(environment.getRoot(), proposal, 'failedprops');
});

it('Proposal passes', async() => {
const environment = await setupEnvironment();

const now = new Date();
const whenVoteExpires = new Date();
whenVoteExpires.setSeconds( whenVoteExpires.getSeconds() + environment.settings.votingDurationSeconds + 1);

environment.setCurrentTime(now);

// Proposing
await environment.dao.contract.propose({
proposer: environment.members[0].account.accountName,
proposal_type: 'role',
content_groups: getSampleRole().content_groups
});

let proposal = last(getDocumentsByType(
environment.getDaoDocuments(),
'role'
));

const daoExpect = getDaoExpect(environment);
daoExpect.toHaveEdge(environment.getRoot(), proposal, 'proposal');
daoExpect.toHaveEdge(environment.members[0].doc, proposal, 'owns');
daoExpect.toHaveEdge(proposal, environment.members[0].doc, 'ownedby');

await environment.dao.contract.vote({
voter: environment.members[0].account.accountName,
proposal_hash: proposal.hash,
vote: 'pass'
});

// Sets the time to the end of proposal
environment.setCurrentTime(whenVoteExpires);

// Now we can close the proposal
await environment.dao.contract.closedocprop({
proposal_hash: proposal.hash
}, getAccountPermission(environment.members[0].account));

// When passing, the proposal is updated
proposal = last(getDocumentsByType(
environment.getDaoDocuments(),
'role'
));

daoExpect.toHaveEdge(environment.getRoot(), proposal, 'passedprops');
});
});
12 changes: 6 additions & 6 deletions tests/setup.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { loadConfig, Blockchain } from '@klevoya/hydra';
import Account from '@klevoya/hydra/lib/main/account';
import { StrictBuilder } from 'builder-pattern';
import { DaoBlockchain } from './DaoBlockchain';
import { DaoBlockchain, DaoSettings, TestSettings } from './DaoBlockchain';

const config = loadConfig("hydra.yml");

const MINUTE = 60;
const HOUR = MINUTE * 60;
const DAY = HOUR * 24;

export const setupEnvironment = async (): Promise<DaoBlockchain> => {
export const setupEnvironment = async (settings?: Partial<DaoSettings>, testSettings?: Partial<TestSettings>): Promise<DaoBlockchain> => {
const votingDurationSeconds = DAY;
const periodDurations = 120;

const blockchain = await DaoBlockchain.build(config, {
votingDurationSeconds,
votingDurationSeconds: settings?.votingDurationSeconds ?? votingDurationSeconds,
voice: {
decayPeriod: 5,
decayPerPeriodx10M: 5000000
decayPeriod: settings?.voice?.decayPeriod ?? 5,
decayPerPeriodx10M: settings?.voice?.decayPerPeriodx10M ?? 5000000
}
}, {
createMembers: 3
createMembers: testSettings?.createMembers ?? 5
});

return blockchain;
Expand Down
7 changes: 5 additions & 2 deletions tests/types/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ export enum ContentType {
STRING = 'string',
ASSET = 'asset',
NAME = 'name',
INT64 = 'int64'
INT64 = 'int64',
TIME_POINT = 'time_point'
}

type ContentValueType<T,V> = [T, V];
Expand All @@ -26,11 +27,12 @@ type ContentValueNumberType<T> = ContentValueType<T, number>;
type ContentValueString = ContentValueStringType<ContentType.STRING>;
type ContentValueAsset = ContentValueStringType<ContentType.ASSET>;
type ContentValueName = ContentValueStringType<ContentType.NAME>;
type ContentValueTimePoint = ContentValueStringType<ContentType.TIME_POINT>;
type ContentValueInt64 = ContentValueNumberType<ContentType.INT64>;

export type ContentValue =
// string
ContentValueString | ContentValueAsset | ContentValueName
ContentValueString | ContentValueAsset | ContentValueName | ContentValueTimePoint
// number
| ContentValueInt64;

Expand All @@ -50,6 +52,7 @@ export const SYSTEM_CONTENT_GROUP_LABEL = 'system';
export const makeStringContent = (label: string, value: string): Content => makeContent(label, [ ContentType.STRING, value ]);
export const makeAssetContent = (label: string, value: string): Content => makeContent(label, [ ContentType.ASSET, value ]);
export const makeNameContent = (label: string, value: string): Content => makeContent(label, [ ContentType.NAME, value ]);
export const makeTimePointContent = (label: string, value: string): Content => makeContent(label, [ ContentType.TIME_POINT, value ]);
export const makeInt64Content = (label: string, value: number): Content => makeContent(label, [ ContentType.INT64, value ]);

export const makeContentGroup = (groupLabel: string | undefined, ...content: ContentGroup) : ContentGroup => {
Expand Down
8 changes: 8 additions & 0 deletions tests/types/Edge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface Edge {
from_node: string;
to_node: string;
edge_name: string;
created_date: string;
creator: string;
contract: string;
}
Loading

0 comments on commit dc191c3

Please sign in to comment.