Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand tests #93

Merged
merged 6 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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