feat: complete phase 3 developer ecosystem and dao governance
This commit is contained in:
124
apps/coordinator-api/tests/test_dao_governance.py
Normal file
124
apps/coordinator-api/tests/test_dao_governance.py
Normal file
@@ -0,0 +1,124 @@
|
||||
import pytest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from sqlmodel import Session, create_engine, SQLModel
|
||||
from sqlmodel.pool import StaticPool
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.services.dao_governance_service import DAOGovernanceService
|
||||
from app.domain.dao_governance import ProposalState, ProposalType
|
||||
from app.schemas.dao_governance import MemberCreate, ProposalCreate, VoteCreate
|
||||
|
||||
@pytest.fixture
|
||||
def test_db():
|
||||
engine = create_engine(
|
||||
"sqlite:///:memory:",
|
||||
connect_args={"check_same_thread": False},
|
||||
poolclass=StaticPool,
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
session = Session(engine)
|
||||
yield session
|
||||
session.close()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_contract_service():
|
||||
return AsyncMock()
|
||||
|
||||
@pytest.fixture
|
||||
def dao_service(test_db, mock_contract_service):
|
||||
return DAOGovernanceService(
|
||||
session=test_db,
|
||||
contract_service=mock_contract_service
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_member(dao_service):
|
||||
req = MemberCreate(wallet_address="0xDAO1", staked_amount=100.0)
|
||||
member = await dao_service.register_member(req)
|
||||
|
||||
assert member.wallet_address == "0xDAO1"
|
||||
assert member.staked_amount == 100.0
|
||||
assert member.voting_power == 100.0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_proposal(dao_service):
|
||||
# Register proposer
|
||||
await dao_service.register_member(MemberCreate(wallet_address="0xDAO1", staked_amount=100.0))
|
||||
|
||||
req = ProposalCreate(
|
||||
proposer_address="0xDAO1",
|
||||
title="Fund new AI model",
|
||||
description="Allocate 1000 AITBC to train a new model",
|
||||
proposal_type=ProposalType.GRANT,
|
||||
execution_payload={"amount": "1000", "recipient_address": "0xDev1"},
|
||||
voting_period_days=7
|
||||
)
|
||||
|
||||
proposal = await dao_service.create_proposal(req)
|
||||
assert proposal.title == "Fund new AI model"
|
||||
assert proposal.status == ProposalState.ACTIVE
|
||||
assert proposal.proposal_type == ProposalType.GRANT
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_cast_vote(dao_service):
|
||||
await dao_service.register_member(MemberCreate(wallet_address="0xDAO1", staked_amount=100.0))
|
||||
await dao_service.register_member(MemberCreate(wallet_address="0xDAO2", staked_amount=50.0))
|
||||
|
||||
prop_req = ProposalCreate(
|
||||
proposer_address="0xDAO1",
|
||||
title="Test Proposal",
|
||||
description="Testing voting"
|
||||
)
|
||||
proposal = await dao_service.create_proposal(prop_req)
|
||||
|
||||
# Cast vote
|
||||
vote_req = VoteCreate(
|
||||
member_address="0xDAO2",
|
||||
proposal_id=proposal.id,
|
||||
support=True
|
||||
)
|
||||
vote = await dao_service.cast_vote(vote_req)
|
||||
|
||||
assert vote.support is True
|
||||
assert vote.weight == 50.0
|
||||
|
||||
dao_service.session.refresh(proposal)
|
||||
assert proposal.for_votes == 50.0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_proposal_success(dao_service, test_db):
|
||||
await dao_service.register_member(MemberCreate(wallet_address="0xDAO1", staked_amount=100.0))
|
||||
|
||||
prop_req = ProposalCreate(
|
||||
proposer_address="0xDAO1",
|
||||
title="Test Grant",
|
||||
description="Testing grant execution",
|
||||
proposal_type=ProposalType.GRANT,
|
||||
execution_payload={"amount": "500", "recipient_address": "0xDev"}
|
||||
)
|
||||
proposal = await dao_service.create_proposal(prop_req)
|
||||
|
||||
await dao_service.cast_vote(VoteCreate(
|
||||
member_address="0xDAO1",
|
||||
proposal_id=proposal.id,
|
||||
support=True
|
||||
))
|
||||
|
||||
# Fast forward time to end of voting period
|
||||
proposal.end_time = datetime.utcnow() - timedelta(seconds=1)
|
||||
test_db.commit()
|
||||
|
||||
exec_proposal = await dao_service.execute_proposal(proposal.id)
|
||||
|
||||
assert exec_proposal.status == ProposalState.EXECUTED
|
||||
|
||||
# Verify treasury allocation was created
|
||||
from app.domain.dao_governance import TreasuryAllocation
|
||||
from sqlmodel import select
|
||||
allocation = test_db.exec(select(TreasuryAllocation).where(TreasuryAllocation.proposal_id == proposal.id)).first()
|
||||
|
||||
assert allocation is not None
|
||||
assert allocation.amount == 500.0
|
||||
assert allocation.recipient_address == "0xDev"
|
||||
110
apps/coordinator-api/tests/test_developer_platform.py
Normal file
110
apps/coordinator-api/tests/test_developer_platform.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import pytest
|
||||
from unittest.mock import AsyncMock
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from sqlmodel import Session, create_engine, SQLModel
|
||||
from sqlmodel.pool import StaticPool
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.services.developer_platform_service import DeveloperPlatformService
|
||||
from app.domain.developer_platform import BountyStatus, CertificationLevel
|
||||
from app.schemas.developer_platform import (
|
||||
DeveloperCreate, BountyCreate, BountySubmissionCreate, CertificationGrant
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def test_db():
|
||||
engine = create_engine(
|
||||
"sqlite:///:memory:",
|
||||
connect_args={"check_same_thread": False},
|
||||
poolclass=StaticPool,
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
session = Session(engine)
|
||||
yield session
|
||||
session.close()
|
||||
|
||||
@pytest.fixture
|
||||
def mock_contract_service():
|
||||
return AsyncMock()
|
||||
|
||||
@pytest.fixture
|
||||
def dev_service(test_db, mock_contract_service):
|
||||
return DeveloperPlatformService(
|
||||
session=test_db,
|
||||
contract_service=mock_contract_service
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_developer(dev_service):
|
||||
req = DeveloperCreate(
|
||||
wallet_address="0xDev1",
|
||||
github_handle="dev_one",
|
||||
skills=["python", "solidity"]
|
||||
)
|
||||
|
||||
dev = await dev_service.register_developer(req)
|
||||
|
||||
assert dev.wallet_address == "0xDev1"
|
||||
assert dev.reputation_score == 0.0
|
||||
assert "solidity" in dev.skills
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_grant_certification(dev_service):
|
||||
dev = await dev_service.register_developer(DeveloperCreate(wallet_address="0xDev1"))
|
||||
|
||||
req = CertificationGrant(
|
||||
developer_id=dev.id,
|
||||
certification_name="ZK-Circuit Architect",
|
||||
level=CertificationLevel.ADVANCED,
|
||||
issued_by="0xDAOAdmin"
|
||||
)
|
||||
|
||||
cert = await dev_service.grant_certification(req)
|
||||
|
||||
assert cert.developer_id == dev.id
|
||||
assert cert.level == CertificationLevel.ADVANCED
|
||||
|
||||
# Check reputation boost (ADVANCED = +50.0)
|
||||
dev_service.session.refresh(dev)
|
||||
assert dev.reputation_score == 50.0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bounty_lifecycle(dev_service):
|
||||
# 1. Register Developer
|
||||
dev = await dev_service.register_developer(DeveloperCreate(wallet_address="0xDev1"))
|
||||
|
||||
# 2. Create Bounty
|
||||
bounty_req = BountyCreate(
|
||||
title="Implement Atomic Swap",
|
||||
description="Write a secure HTLC contract",
|
||||
reward_amount=1000.0,
|
||||
creator_address="0xCreator"
|
||||
)
|
||||
bounty = await dev_service.create_bounty(bounty_req)
|
||||
assert bounty.status == BountyStatus.OPEN
|
||||
|
||||
# 3. Submit Work
|
||||
sub_req = BountySubmissionCreate(
|
||||
developer_id=dev.id,
|
||||
github_pr_url="https://github.com/aitbc/pr/1"
|
||||
)
|
||||
sub = await dev_service.submit_bounty(bounty.id, sub_req)
|
||||
assert sub.bounty_id == bounty.id
|
||||
|
||||
dev_service.session.refresh(bounty)
|
||||
assert bounty.status == BountyStatus.IN_REVIEW
|
||||
|
||||
# 4. Approve Submission
|
||||
appr_sub = await dev_service.approve_submission(sub.id, reviewer_address="0xReviewer", review_notes="Looks great!")
|
||||
|
||||
assert appr_sub.is_approved is True
|
||||
assert appr_sub.tx_hash_reward is not None
|
||||
|
||||
dev_service.session.refresh(bounty)
|
||||
dev_service.session.refresh(dev)
|
||||
|
||||
assert bounty.status == BountyStatus.COMPLETED
|
||||
assert bounty.assigned_developer_id == dev.id
|
||||
assert dev.total_earned_aitbc == 1000.0
|
||||
assert dev.reputation_score == 5.0 # Base bump for finishing a bounty
|
||||
Reference in New Issue
Block a user