Add backend API to house forum content
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 38s
Details
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 38s
Details
This commit is contained in:
parent
8da5772d86
commit
afe9da73c9
|
@ -0,0 +1,2 @@
|
|||
PORT=3000
|
||||
DATA_DIR="./data"
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'airbnb',
|
||||
],
|
||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: false,
|
||||
optionalDependencies: false,
|
||||
peerDependencies: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
.env
|
||||
data/
|
|
@ -0,0 +1,9 @@
|
|||
FROM node
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ADD package.json package-lock.json index.js /app/
|
||||
|
||||
RUN npm ci
|
||||
|
||||
ENTRYPOINT ["node", "index.js"]
|
|
@ -0,0 +1,10 @@
|
|||
services:
|
||||
backend:
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- "3002:3000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
environment:
|
||||
- DATA_DIR=/data
|
|
@ -0,0 +1,45 @@
|
|||
const express = require('express');
|
||||
const { Level } = require('level');
|
||||
const {
|
||||
createHash,
|
||||
} = require('node:crypto');
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const port = process.env.PORT || 3000;
|
||||
const dataDir = process.env.DATA_DIR || 'data';
|
||||
|
||||
const db = new Level(`${dataDir}/forum`, { valueEncoding: 'json' });
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
app.post('/write', async (req, res) => {
|
||||
const { body: data } = req;
|
||||
// TODO: Require author signature
|
||||
// Compute content hash
|
||||
const hash = createHash('sha256').update(JSON.stringify(data)).digest('base64url');
|
||||
console.log('write', hash);
|
||||
// Store content
|
||||
db.put(hash, data);
|
||||
// Return hash
|
||||
res.send(hash);
|
||||
});
|
||||
|
||||
app.get('/read/:hash', async (req, res) => {
|
||||
// Fetch content
|
||||
const { hash } = req.params;
|
||||
const data = await db.get(req.params.hash);
|
||||
console.log('read', hash);
|
||||
// Return content
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
console.log(`req.path: ${req.path}`);
|
||||
res.send('Hello World!');
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Listening on port ${port}`);
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.7",
|
||||
"casper-js-sdk": "^2.15.4",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.18.2",
|
||||
"level": "^8.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -10,11 +10,16 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@helia/dag-json": "^3.0.2",
|
||||
"@libp2p/websockets": "^8.0.16",
|
||||
"@metamask/sdk-react": "^0.16.0",
|
||||
"@multiformats/multiaddr": "^12.2.1",
|
||||
"@tanstack/react-table": "^8.13.2",
|
||||
"axios": "^1.6.7",
|
||||
"axios": "^1.6.8",
|
||||
"bootstrap": "^5.3.3",
|
||||
"bootswatch": "^5.3.3",
|
||||
"helia": "^4.1.0",
|
||||
"ipfs-core": "^0.18.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.10.1",
|
||||
|
|
|
@ -3,6 +3,8 @@ import {
|
|||
} from 'react';
|
||||
import { useSDK } from '@metamask/sdk-react';
|
||||
import { Web3 } from 'web3';
|
||||
import axios from 'axios';
|
||||
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
import Tabs from 'react-bootstrap/Tabs';
|
||||
|
@ -10,14 +12,16 @@ import Container from 'react-bootstrap/Container';
|
|||
import Row from 'react-bootstrap/Row';
|
||||
import Col from 'react-bootstrap/Col';
|
||||
import Stack from 'react-bootstrap/Stack';
|
||||
import Modal from 'react-bootstrap/Modal';
|
||||
import Form from 'react-bootstrap/Form';
|
||||
|
||||
import useList from './List';
|
||||
import { getContractByChainId, getContractNameByAddress } from './contract-config';
|
||||
import Web3Context from './Web3Context';
|
||||
import useList from './utils/List';
|
||||
import { getContractByChainId, getContractNameByAddress } from './utils/contract-config';
|
||||
import Web3Context from './contexts/Web3Context';
|
||||
import DAOArtifact from './assets/DAO.json';
|
||||
import Work1Artifact from './assets/Work1.json';
|
||||
import OnboardingArtifact from './assets/Onboarding.json';
|
||||
import WorkContract from './WorkContract';
|
||||
import WorkContract from './components/WorkContract';
|
||||
|
||||
function App() {
|
||||
const {
|
||||
|
@ -33,6 +37,11 @@ function App() {
|
|||
const [posts, dispatchPost] = useList();
|
||||
const [validationPools, dispatchValidationPool] = useList();
|
||||
|
||||
const [showAddPost, setShowAddPost] = useState(false);
|
||||
const [addPostContent, setAddPostContent] = useState('');
|
||||
const [showViewPost, setShowViewPost] = useState(false);
|
||||
const [viewPostContent, setViewPostContent] = useState('');
|
||||
|
||||
const web3ProviderValue = useMemo(() => ({
|
||||
provider,
|
||||
DAO,
|
||||
|
@ -201,8 +210,8 @@ function App() {
|
|||
});
|
||||
}, [provider, chainId]);
|
||||
|
||||
const addPost = useCallback(async () => {
|
||||
await DAO.methods.addPost(account).send({
|
||||
const addPost = useCallback(async (contentId) => {
|
||||
await DAO.methods.addPost(account, contentId).send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
||||
|
@ -246,6 +255,30 @@ function App() {
|
|||
});
|
||||
}, [DAO, account]);
|
||||
|
||||
const handleCloseAddPost = () => setShowAddPost(false);
|
||||
const handleShowAddPost = () => setShowAddPost(true);
|
||||
const handleSubmitAddPost = async () => {
|
||||
// Upload content to API
|
||||
// TODO: include metamask signature
|
||||
const data = { author: account, content: addPostContent };
|
||||
const res = await axios.post('/api/write', data);
|
||||
const hash = res.data;
|
||||
// Upload hash to blockchain
|
||||
await addPost(hash);
|
||||
setShowAddPost(false);
|
||||
setAddPostContent('');
|
||||
};
|
||||
|
||||
const handleCloseViewPost = () => setShowViewPost(false);
|
||||
const handleShowViewPost = async (post) => {
|
||||
const res = await axios.get(`/api/read/${post.contentId}`);
|
||||
const { data } = res;
|
||||
// TODO: Verify base64url(sha256(JSON.stringify(data))) = contentId
|
||||
// TODO: Verify data.author = post.author
|
||||
setViewPostContent(data.content);
|
||||
setShowViewPost(true);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
/* --------------------------- END UI ACTIONS ------------------------------------- */
|
||||
/* -------------------------------------------------------------------------------- */
|
||||
|
@ -258,6 +291,48 @@ function App() {
|
|||
|
||||
return (
|
||||
<Web3Context.Provider value={web3ProviderValue}>
|
||||
|
||||
<Modal show={showAddPost} onHide={handleCloseAddPost}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>Add Post</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<Form>
|
||||
<Form.Group className="mb-3" controlId="addPost.content">
|
||||
<Form.Label>Example textarea</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
rows={3}
|
||||
onChange={(e) => setAddPostContent(e.target.value)}
|
||||
/>
|
||||
</Form.Group>
|
||||
</Form>
|
||||
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleCloseAddPost}>
|
||||
Close
|
||||
</Button>
|
||||
<Button variant="primary" onClick={handleSubmitAddPost}>
|
||||
Submit
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
<Modal show={showViewPost} onHide={handleCloseViewPost}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>View Post</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
{viewPostContent}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleCloseViewPost}>
|
||||
Close
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
|
||||
{!connected && <Button onClick={() => connect()}>Connect</Button>}
|
||||
|
||||
{connected && (
|
||||
|
@ -325,6 +400,10 @@ function App() {
|
|||
<td>{post.author}</td>
|
||||
<td>{getAdressName(post.sender)}</td>
|
||||
<td>
|
||||
<Button onClick={() => handleShowViewPost(post)}>
|
||||
View Post
|
||||
</Button>
|
||||
{' '}
|
||||
Initiate Validation Pool
|
||||
{' '}
|
||||
<Button onClick={() => initiateValidationPool(post.id, 60)}>
|
||||
|
@ -345,7 +424,7 @@ function App() {
|
|||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<Button onClick={() => addPost()}>Add Post</Button>
|
||||
<Button onClick={handleShowAddPost}>Add Post</Button>
|
||||
</div>
|
||||
<div>
|
||||
{`Validation Pool Count: ${validationPools.length}`}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -2,9 +2,9 @@ import { useCallback, useContext, useEffect } from 'react';
|
|||
import { PropTypes } from 'prop-types';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
|
||||
import { getContractByChainId } from './contract-config';
|
||||
import Web3Context from './Web3Context';
|
||||
import WorkContractContext from './WorkContractContext';
|
||||
import { getContractByChainId } from '../utils/contract-config';
|
||||
import Web3Context from '../contexts/Web3Context';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
|
||||
const getAvailabilityStatus = (stake) => {
|
||||
if (stake.reclaimed) return 'Reclaimed';
|
|
@ -1,7 +1,7 @@
|
|||
import { useMemo } from 'react';
|
||||
import { PropTypes } from 'prop-types';
|
||||
import useList from './List';
|
||||
import WorkContractContext from './WorkContractContext';
|
||||
import useList from '../utils/List';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
import AvailabilityStakes from './AvailabilityStakes';
|
||||
import WorkRequests from './WorkRequests';
|
||||
|
|
@ -5,9 +5,9 @@ import { PropTypes } from 'prop-types';
|
|||
import Button from 'react-bootstrap/Button';
|
||||
|
||||
import Web3 from 'web3';
|
||||
import Web3Context from './Web3Context';
|
||||
import useList from './List';
|
||||
import WorkContractContext from './WorkContractContext';
|
||||
import Web3Context from '../contexts/Web3Context';
|
||||
import useList from '../utils/List';
|
||||
import WorkContractContext from '../contexts/WorkContractContext';
|
||||
|
||||
const getRequestStatus = (request) => {
|
||||
switch (Number(request.status)) {
|
||||
|
@ -85,9 +85,10 @@ function WorkRequests({
|
|||
}, [workContract, fetchWorkRequests, fetchPrice, fetchWorkRequest]);
|
||||
|
||||
const requestWork = useCallback(async () => {
|
||||
// TODO: Accept input, upload to API, include hash in contract call
|
||||
const web3 = new Web3(provider);
|
||||
const priceWei = BigInt(web3.utils.toWei(price, 'ether'));
|
||||
await workContract.methods.requestWork().send({
|
||||
await workContract.methods.requestWork('').send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
value: priceWei,
|
||||
|
@ -95,7 +96,8 @@ function WorkRequests({
|
|||
}, [provider, workContract, account, price]);
|
||||
|
||||
const submitWorkEvidence = useCallback(async (requestIndex) => {
|
||||
await workContract.methods.submitWorkEvidence(requestIndex).send({
|
||||
// TODO: Accept input, upload to API, include hash in contract call
|
||||
await workContract.methods.submitWorkEvidence(requestIndex, '').send({
|
||||
from: account,
|
||||
gas: 1000000,
|
||||
});
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x4CC737e8Ec8873abCBC98c8c4a401990d6Fc4F38",
|
||||
"Work1": "0xa0A1c1e84F50f6786A6927b448903a37776D2e74",
|
||||
"Onboarding": "0xA6bE53f1F25816c65CF3bc36d6F8793Eed14fd09"
|
||||
"DAO": "0x2D812555F4eF06267406D80E7fA01Ac3288f626c",
|
||||
"Work1": "0x3CAB55d59af095618F2ee539463E33447cfc97BA",
|
||||
"Onboarding": "0xaB0c7Cf9A436978F55831C8EdB67892419ABAE62"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8611676563EBcd69dC52E5829bF2914A957398C3",
|
||||
"Work1": "0xCd5881DB1aa6b86283a9c5660FaB65C989cf2721",
|
||||
"Onboarding": "0x215078c5cf21ffB79Ee14Cf09156B94a11b7340f"
|
||||
"DAO": "0xa3b15aBD114C2332652A4fD5f9A43B86315E5078",
|
||||
"Work1": "0x7C4F9cA684B875d21e121E9C1D718f89A2603d0d",
|
||||
"Onboarding": "0x6C04aA0e984193A2A624B56F6407594f45d6554c"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import contractAddresses from './contract-addresses.json';
|
||||
import contractAddresses from '../contract-addresses.json';
|
||||
|
||||
const networks = {
|
||||
localhost: '0x539',
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"localhost": {
|
||||
"DAO": "0x4CC737e8Ec8873abCBC98c8c4a401990d6Fc4F38",
|
||||
"Work1": "0xa0A1c1e84F50f6786A6927b448903a37776D2e74",
|
||||
"Onboarding": "0xA6bE53f1F25816c65CF3bc36d6F8793Eed14fd09"
|
||||
"DAO": "0x2D812555F4eF06267406D80E7fA01Ac3288f626c",
|
||||
"Work1": "0x3CAB55d59af095618F2ee539463E33447cfc97BA",
|
||||
"Onboarding": "0xaB0c7Cf9A436978F55831C8EdB67892419ABAE62"
|
||||
},
|
||||
"sepolia": {
|
||||
"DAO": "0x8611676563EBcd69dC52E5829bF2914A957398C3",
|
||||
"Work1": "0xCd5881DB1aa6b86283a9c5660FaB65C989cf2721",
|
||||
"Onboarding": "0x215078c5cf21ffB79Ee14Cf09156B94a11b7340f"
|
||||
"DAO": "0xa3b15aBD114C2332652A4fD5f9A43B86315E5078",
|
||||
"Work1": "0x7C4F9cA684B875d21e121E9C1D718f89A2603d0d",
|
||||
"Onboarding": "0x6C04aA0e984193A2A624B56F6407594f45d6554c"
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ struct Post {
|
|||
uint id;
|
||||
address sender;
|
||||
address author;
|
||||
string contentId;
|
||||
}
|
||||
|
||||
struct Stake {
|
||||
|
@ -61,12 +62,16 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
event ValidationPoolInitiated(uint poolIndex);
|
||||
event ValidationPoolResolved(uint poolIndex, bool votePasses);
|
||||
|
||||
function addPost(address author) public returns (uint postIndex) {
|
||||
function addPost(
|
||||
address author,
|
||||
string calldata contentId
|
||||
) external returns (uint postIndex) {
|
||||
postIndex = postCount++;
|
||||
Post storage post = posts[postIndex];
|
||||
post.author = author;
|
||||
post.sender = msg.sender;
|
||||
post.id = postIndex;
|
||||
post.contentId = contentId;
|
||||
emit PostAdded(postIndex);
|
||||
}
|
||||
|
||||
|
@ -78,7 +83,7 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
uint duration,
|
||||
bool callbackOnValidate,
|
||||
bytes calldata callbackData
|
||||
) public payable returns (uint poolIndex) {
|
||||
) external payable returns (uint poolIndex) {
|
||||
require(msg.value > 0, "Fee is required to initiate validation pool");
|
||||
require(duration >= minDuration, "Duration is too short");
|
||||
require(duration <= maxDuration, "Duration is too long");
|
||||
|
@ -161,6 +166,12 @@ contract DAO is ERC20("Reputation", "REP") {
|
|||
members[memberCount++] = post.author;
|
||||
isMember[post.author] = true;
|
||||
}
|
||||
console.log(
|
||||
"stakedFor: %d, stakedAgainst: %d, stakeCount: %d",
|
||||
stakedFor,
|
||||
stakedAgainst,
|
||||
pool.stakeCount
|
||||
);
|
||||
pool.resolved = true;
|
||||
pool.outcome = votePasses;
|
||||
emit ValidationPoolResolved(poolIndex, votePasses);
|
||||
|
|
|
@ -22,7 +22,7 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
request.status = WorkStatus.ApprovalSubmitted;
|
||||
request.approval = approval;
|
||||
// Make work evidence post
|
||||
uint postIndex = dao.addPost(stake.worker);
|
||||
uint postIndex = dao.addPost(stake.worker, request.evidenceContentId);
|
||||
emit WorkApprovalSubmitted(requestIndex, approval);
|
||||
// Initiate validation pool
|
||||
uint poolIndex = dao.initiateValidationPool{
|
||||
|
@ -44,7 +44,10 @@ contract Onboarding is WorkContract, IOnValidate {
|
|||
payable(request.customer).transfer(request.fee / 10);
|
||||
return;
|
||||
}
|
||||
uint postIndex = dao.addPost(request.customer);
|
||||
uint postIndex = dao.addPost(
|
||||
request.customer,
|
||||
request.requestContentId
|
||||
);
|
||||
dao.initiateValidationPool{value: request.fee / 10}(
|
||||
postIndex,
|
||||
POOL_DURATION,
|
||||
|
|
|
@ -25,6 +25,8 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
uint256 fee;
|
||||
WorkStatus status;
|
||||
uint stakeIndex;
|
||||
string requestContentId;
|
||||
string evidenceContentId;
|
||||
bool approval;
|
||||
}
|
||||
|
||||
|
@ -127,17 +129,21 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
}
|
||||
|
||||
/// Accept work request with fee
|
||||
function requestWork() external payable {
|
||||
function requestWork(string calldata requestContentId) external payable {
|
||||
require(msg.value >= price, "Insufficient fee");
|
||||
uint requestIndex = requestCount++;
|
||||
WorkRequest storage request = requests[requestIndex];
|
||||
request.customer = msg.sender;
|
||||
request.fee = msg.value;
|
||||
request.stakeIndex = assignWork(requestIndex);
|
||||
request.requestContentId = requestContentId;
|
||||
}
|
||||
|
||||
/// Accept work evidence from worker
|
||||
function submitWorkEvidence(uint requestIndex) external {
|
||||
function submitWorkEvidence(
|
||||
uint requestIndex,
|
||||
string calldata evidenceContentId
|
||||
) external {
|
||||
WorkRequest storage request = requests[requestIndex];
|
||||
require(
|
||||
request.status == WorkStatus.Requested,
|
||||
|
@ -149,6 +155,7 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
"Worker can only submit evidence for work they are assigned"
|
||||
);
|
||||
request.status = WorkStatus.EvidenceSubmitted;
|
||||
request.evidenceContentId = evidenceContentId;
|
||||
emit WorkEvidenceSubmitted(requestIndex);
|
||||
}
|
||||
|
||||
|
@ -166,7 +173,7 @@ abstract contract WorkContract is IAcceptAvailability {
|
|||
request.status = WorkStatus.ApprovalSubmitted;
|
||||
request.approval = approval;
|
||||
// Make work evidence post
|
||||
uint postIndex = dao.addPost(stake.worker);
|
||||
uint postIndex = dao.addPost(stake.worker, request.evidenceContentId);
|
||||
emit WorkApprovalSubmitted(requestIndex, approval);
|
||||
// Initiate validation pool
|
||||
uint poolIndex = dao.initiateValidationPool{value: request.fee}(
|
||||
|
|
|
@ -22,18 +22,22 @@ describe('DAO', () => {
|
|||
describe('Post', () => {
|
||||
it('should be able to add a post', async () => {
|
||||
const { dao, account1 } = await loadFixture(deploy);
|
||||
await expect(dao.addPost(account1)).to.emit(dao, 'PostAdded').withArgs(0);
|
||||
const contentId = 'some-id';
|
||||
await expect(dao.addPost(account1, contentId)).to.emit(dao, 'PostAdded').withArgs(0);
|
||||
const post = await dao.posts(0);
|
||||
expect(post.author).to.equal(account1);
|
||||
expect(post.sender).to.equal(account1);
|
||||
expect(post.contentId).to.equal(contentId);
|
||||
});
|
||||
|
||||
it('should be able to add a post on behalf of another account', async () => {
|
||||
const { dao, account1, account2 } = await loadFixture(deploy);
|
||||
await dao.addPost(account2);
|
||||
const contentId = 'some-id';
|
||||
await dao.addPost(account2, contentId);
|
||||
const post = await dao.posts(0);
|
||||
expect(post.author).to.equal(account2);
|
||||
expect(post.sender).to.equal(account1);
|
||||
expect(post.contentId).to.equal(contentId);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -46,7 +50,7 @@ describe('DAO', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
({ dao, account1 } = await loadFixture(deploy));
|
||||
await dao.addPost(account1);
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const init = () => dao.initiateValidationPool(
|
||||
0,
|
||||
POOL_DURATION,
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('Onboarding', () => {
|
|||
const Onboarding = await ethers.getContractFactory('Onboarding');
|
||||
const onboarding = await Onboarding.deploy(dao.target, PRICE);
|
||||
|
||||
await dao.addPost(account1);
|
||||
await dao.addPost(account1, 'content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, false, callbackData, { value: 100 });
|
||||
await time.increase(61);
|
||||
|
@ -39,165 +39,7 @@ describe('Onboarding', () => {
|
|||
expect(await onboarding.stakeCount()).to.equal(0);
|
||||
});
|
||||
|
||||
describe('Stake availability', () => {
|
||||
let dao;
|
||||
let onboarding;
|
||||
let account1;
|
||||
let account2;
|
||||
|
||||
beforeEach(async () => {
|
||||
({
|
||||
dao, onboarding, account1, account2,
|
||||
} = await loadFixture(deploy));
|
||||
await expect(dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION)).to.emit(onboarding, 'AvailabilityStaked').withArgs(0);
|
||||
});
|
||||
|
||||
it('Should be able to stake availability', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
expect(await dao.balanceOf(onboarding.target)).to.equal(50);
|
||||
expect(await onboarding.stakeCount()).to.equal(1);
|
||||
const stake = await onboarding.stakes(0);
|
||||
expect(stake.worker).to.equal(account1);
|
||||
expect(stake.amount).to.equal(50);
|
||||
expect(stake.endTime).to.equal(await time.latest() + STAKE_DURATION);
|
||||
});
|
||||
|
||||
it('should not be able to stake availability without reputation value', async () => {
|
||||
await expect(dao.stakeAvailability(onboarding.target, 0, STAKE_DURATION)).to.be.revertedWith('No stake provided');
|
||||
});
|
||||
|
||||
it('should be able to reclaim staked availability after duration elapses', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await expect(onboarding.reclaimAvailability(0)).to.emit(onboarding, 'AvailabilityStaked').withArgs(0);
|
||||
expect(await dao.balanceOf(account1)).to.equal(100);
|
||||
});
|
||||
|
||||
it('should not be able to reclaim staked availability twice', async () => {
|
||||
expect(await dao.balanceOf(account1)).to.equal(50);
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await onboarding.reclaimAvailability(0);
|
||||
await expect(onboarding.reclaimAvailability(0)).to.be.revertedWith('Stake has already been reclaimed');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim staked availability before duration elapses', async () => {
|
||||
await expect(onboarding.reclaimAvailability(0)).to.be.revertedWith('Stake duration has not yet elapsed');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim availability staked by another account', async () => {
|
||||
time.increase(STAKE_DURATION + 1);
|
||||
await expect(onboarding.connect(account2).reclaimAvailability(0)).to.be.revertedWith('Worker can only reclaim their own availability stake');
|
||||
});
|
||||
|
||||
it('should be able to extend the duration of an availability stake before it expires', async () => {
|
||||
await time.increase(STAKE_DURATION / 2);
|
||||
await expect(onboarding.extendAvailability(0, STAKE_DURATION)).to.emit(onboarding, 'AvailabilityStaked').withArgs(0);
|
||||
});
|
||||
|
||||
it('should be able to extend the duration of an availability stake after it expires', async () => {
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await onboarding.extendAvailability(0, STAKE_DURATION);
|
||||
});
|
||||
|
||||
it('should not be able to extend the duration of another worker\'s availability stake', async () => {
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await expect(onboarding.connect(account2).extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Worker can only extend their own availability stake');
|
||||
});
|
||||
|
||||
it('should not be able to extend a stake that has been reclaimed', async () => {
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await onboarding.reclaimAvailability(0);
|
||||
await expect(onboarding.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been reclaimed');
|
||||
});
|
||||
|
||||
it('extending a stake before expiration should increase the end time by the given duration', async () => {
|
||||
await time.increase(STAKE_DURATION / 2);
|
||||
await onboarding.extendAvailability(0, STAKE_DURATION * 2);
|
||||
const expectedEndTime = await time.latest() + 2.5 * STAKE_DURATION;
|
||||
const stake = await onboarding.stakes(0);
|
||||
expect(stake.endTime).to.be.within(expectedEndTime - 1, expectedEndTime);
|
||||
});
|
||||
|
||||
it('extending a stake after expiration should restart the stake for the given duration', async () => {
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await onboarding.extendAvailability(0, STAKE_DURATION * 2);
|
||||
const expectedEndTime = await time.latest() + STAKE_DURATION * 2;
|
||||
const stake = await onboarding.stakes(0);
|
||||
expect(stake.endTime).to.be.within(expectedEndTime - 1, expectedEndTime);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request and assign work', () => {
|
||||
it('should be able to request work and assign to a worker', async () => {
|
||||
const {
|
||||
dao, onboarding, account1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(requestWork()).to.emit(onboarding, 'WorkAssigned').withArgs(account1, 0);
|
||||
expect(await onboarding.requestCount()).to.equal(1);
|
||||
const request = await onboarding.requests(0);
|
||||
expect(request.customer).to.equal(account2);
|
||||
});
|
||||
|
||||
it('should not be able to reclaim stake after work is assigned', async () => {
|
||||
const {
|
||||
dao, onboarding, account1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(requestWork()).to.emit(onboarding, 'WorkAssigned').withArgs(account1, 0);
|
||||
await time.increase(STAKE_DURATION + 1);
|
||||
await expect(onboarding.reclaimAvailability(0)).to.be.revertedWith('Stake has already been assigned work');
|
||||
});
|
||||
it('should not be able to request work if there are no availability stakes', async () => {
|
||||
const {
|
||||
onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
||||
it('should not be able to request work if fee is insufficient', async () => {
|
||||
const {
|
||||
onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE / 2 });
|
||||
await expect(requestWork()).to.be.revertedWith('Insufficient fee');
|
||||
});
|
||||
|
||||
it('should not assign work to an expired availability stake', async () => {
|
||||
const {
|
||||
dao, onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await time.increase(STAKE_DURATION + 1);
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
||||
it('should not assign work to the same availability stake twice', async () => {
|
||||
const {
|
||||
dao, onboarding, account1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(requestWork()).to.emit(onboarding, 'WorkAssigned').withArgs(account1, 0);
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
||||
it('should not be able to extend a stake that has been assigned work', async () => {
|
||||
const {
|
||||
dao, onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await expect(onboarding.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been assigned work');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Work evidence and approval/disapproval', () => {
|
||||
describe('Work approval/disapproval', () => {
|
||||
let dao;
|
||||
let onboarding;
|
||||
let account1;
|
||||
|
@ -210,31 +52,16 @@ describe('Onboarding', () => {
|
|||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
});
|
||||
|
||||
it('should be able to submit work evidence', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(onboarding.submitWorkEvidence(0)).to.emit(onboarding, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence twice', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(onboarding.submitWorkEvidence(0)).to.emit(onboarding, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(onboarding.submitWorkEvidence(0)).to.be.revertedWith('Status must be Requested');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence for a different worker', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await expect(onboarding.connect(account2).submitWorkEvidence(0)).to.be.revertedWith('Worker can only submit evidence for work they are assigned');
|
||||
});
|
||||
|
||||
it('should be able to submit work approval', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(onboarding, 'WorkApprovalSubmitted').withArgs(0, true);
|
||||
const post = await dao.posts(1);
|
||||
expect(post.author).to.equal(account1);
|
||||
expect(post.sender).to.equal(onboarding.target);
|
||||
expect(post.contentId).to.equal('evidence-content-id');
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.postIndex).to.equal(1);
|
||||
expect(pool.fee).to.equal(PRICE * 0.9);
|
||||
|
@ -242,29 +69,29 @@ describe('Onboarding', () => {
|
|||
});
|
||||
|
||||
it('should be able to submit work disapproval', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, false))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(onboarding, 'WorkApprovalSubmitted').withArgs(0, false);
|
||||
});
|
||||
|
||||
it('should not be able to submit work approval/disapproval twice', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence after work approval', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(onboarding.submitWorkEvidence(0)).to.be.revertedWith('Status must be Requested');
|
||||
await expect(onboarding.submitWorkEvidence(0, 'evidence-content-id')).to.be.revertedWith('Status must be Requested');
|
||||
});
|
||||
|
||||
it('should not be able to submit work approval/disapproval before work evidence', async () => {
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted');
|
||||
});
|
||||
});
|
||||
|
@ -275,8 +102,8 @@ describe('Onboarding', () => {
|
|||
dao, onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await time.increase(86401);
|
||||
await expect(dao.evaluateOutcome(1)).to.emit(dao, 'ValidationPoolInitiated').withArgs(2);
|
||||
|
@ -284,6 +111,7 @@ describe('Onboarding', () => {
|
|||
const post = await dao.posts(2);
|
||||
expect(post.author).to.equal(account2);
|
||||
expect(post.sender).to.equal(onboarding.target);
|
||||
expect(post.contentId).to.equal('req-content-id');
|
||||
const pool = await dao.validationPools(2);
|
||||
expect(pool.postIndex).to.equal(2);
|
||||
expect(pool.fee).to.equal(PRICE * 0.1);
|
||||
|
@ -295,11 +123,11 @@ describe('Onboarding', () => {
|
|||
const {
|
||||
dao, onboarding, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(onboarding.target, 50, STAKE_DURATION);
|
||||
await onboarding.connect(account2).requestWork({ value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0);
|
||||
await dao.stakeAvailability(onboarding.target, 40, STAKE_DURATION);
|
||||
await onboarding.connect(account2).requestWork('req-content-id', { value: PRICE });
|
||||
await onboarding.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(onboarding.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await dao.stake(1, 50, false);
|
||||
await dao.stake(1, 60, false);
|
||||
await time.increase(86401);
|
||||
await expect(dao.evaluateOutcome(1)).not.to.emit(dao, 'ValidationPoolInitiated');
|
||||
expect(await dao.postCount()).to.equal(2);
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('Work1', () => {
|
|||
const Work1 = await ethers.getContractFactory('Work1');
|
||||
const work1 = await Work1.deploy(dao.target, WORK1_PRICE);
|
||||
|
||||
await dao.addPost(account1);
|
||||
await dao.addPost(account1, 'some-content-id');
|
||||
const callbackData = ethers.AbiCoder.defaultAbiCoder().encode([], []);
|
||||
await dao.initiateValidationPool(0, 60, false, callbackData, { value: 100 });
|
||||
await time.increase(61);
|
||||
|
@ -132,11 +132,12 @@ describe('Work1', () => {
|
|||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(0, 0);
|
||||
expect(await work1.requestCount()).to.equal(1);
|
||||
const request = await work1.requests(0);
|
||||
expect(request.customer).to.equal(account2);
|
||||
expect(request.requestContentId).to.equal('req-content-id');
|
||||
});
|
||||
|
||||
it('should not be able to reclaim stake after work is assigned', async () => {
|
||||
|
@ -144,7 +145,7 @@ describe('Work1', () => {
|
|||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(0, 0);
|
||||
await time.increase(STAKE_DURATION + 1);
|
||||
await expect(work1.reclaimAvailability(0)).to.be.revertedWith('Stake has already been assigned work');
|
||||
|
@ -153,7 +154,7 @@ describe('Work1', () => {
|
|||
const {
|
||||
work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
||||
|
@ -161,7 +162,7 @@ describe('Work1', () => {
|
|||
const {
|
||||
work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE / 2 });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE / 2 });
|
||||
await expect(requestWork()).to.be.revertedWith('Insufficient fee');
|
||||
});
|
||||
|
||||
|
@ -170,7 +171,7 @@ describe('Work1', () => {
|
|||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await time.increase(STAKE_DURATION + 1);
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
@ -180,7 +181,7 @@ describe('Work1', () => {
|
|||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
const requestWork = () => work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
const requestWork = () => work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(requestWork()).to.emit(work1, 'WorkAssigned').withArgs(0, 0);
|
||||
await expect(requestWork()).to.be.revertedWith('No available worker stakes');
|
||||
});
|
||||
|
@ -190,7 +191,7 @@ describe('Work1', () => {
|
|||
dao, work1, account2,
|
||||
} = await loadFixture(deploy);
|
||||
await dao.stakeAvailability(work1.target, 50, STAKE_DURATION);
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await time.increase(STAKE_DURATION * 2);
|
||||
await expect(work1.extendAvailability(0, STAKE_DURATION)).to.be.revertedWith('Stake has already been assigned work');
|
||||
});
|
||||
|
@ -210,30 +211,32 @@ describe('Work1', () => {
|
|||
});
|
||||
|
||||
it('should be able to submit work evidence', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0)).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence twice', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0)).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(work1.submitWorkEvidence(0)).to.be.revertedWith('Status must be Requested');
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.emit(work1, 'WorkEvidenceSubmitted').withArgs(0);
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.be.revertedWith('Status must be Requested');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence for a different worker', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await expect(work1.connect(account2).submitWorkEvidence(0)).to.be.revertedWith('Worker can only submit evidence for work they are assigned');
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.connect(account2).submitWorkEvidence(0, 'evidence-content-id'))
|
||||
.to.be.revertedWith('Worker can only submit evidence for work they are assigned');
|
||||
});
|
||||
|
||||
it('should be able to submit work approval', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0);
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(work1.submitWorkApproval(0, true))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, true);
|
||||
const post = await dao.posts(1);
|
||||
expect(post.author).to.equal(account1);
|
||||
expect(post.sender).to.equal(work1.target);
|
||||
expect(post.contentId).to.equal('evidence-content-id');
|
||||
const pool = await dao.validationPools(1);
|
||||
expect(pool.fee).to.equal(WORK1_PRICE);
|
||||
expect(pool.sender).to.equal(work1.target);
|
||||
|
@ -244,29 +247,29 @@ describe('Work1', () => {
|
|||
});
|
||||
|
||||
it('should be able to submit work disapproval', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0);
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(work1.submitWorkApproval(0, false))
|
||||
.to.emit(dao, 'ValidationPoolInitiated').withArgs(1)
|
||||
.to.emit(work1, 'WorkApprovalSubmitted').withArgs(0, false);
|
||||
});
|
||||
|
||||
it('should not be able to submit work approval/disapproval twice', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0);
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(work1.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted');
|
||||
});
|
||||
|
||||
it('should not be able to submit work evidence after work approval', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0);
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await work1.submitWorkEvidence(0, 'evidence-content-id');
|
||||
await expect(work1.submitWorkApproval(0, true)).to.emit(dao, 'ValidationPoolInitiated').withArgs(1);
|
||||
await expect(work1.submitWorkEvidence(0)).to.be.revertedWith('Status must be Requested');
|
||||
await expect(work1.submitWorkEvidence(0, 'evidence-content-id')).to.be.revertedWith('Status must be Requested');
|
||||
});
|
||||
|
||||
it('should not be able to submit work approval/disapproval before work evidence', async () => {
|
||||
await work1.connect(account2).requestWork({ value: WORK1_PRICE });
|
||||
await work1.connect(account2).requestWork('req-content-id', { value: WORK1_PRICE });
|
||||
await expect(work1.submitWorkApproval(0, true)).to.be.revertedWith('Status must be EvidenceSubmitted');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue