mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-04 10:21:40 +00:00
@@ -0,0 +1,515 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
// Note: These would be imported from actual test helpers
|
||||
// For now, we'll define mock functions inline
|
||||
)
|
||||
|
||||
// IdentityDAOTestSuite tests end-to-end Identity DAO functionality
|
||||
type IdentityDAOTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
// app *app.App // Would be initialized with actual app
|
||||
ctx sdk.Context
|
||||
deployer sdk.AccAddress
|
||||
voter1 sdk.AccAddress
|
||||
voter2 sdk.AccAddress
|
||||
voter3 sdk.AccAddress
|
||||
|
||||
// Contract addresses
|
||||
coreAddr sdk.AccAddress
|
||||
votingAddr sdk.AccAddress
|
||||
proposalsAddr sdk.AccAddress
|
||||
preProposeAddr sdk.AccAddress
|
||||
|
||||
// Code IDs
|
||||
coreCodeID uint64
|
||||
votingCodeID uint64
|
||||
proposalsCodeID uint64
|
||||
preProposeCodeID uint64
|
||||
}
|
||||
|
||||
// SetupSuite runs once before all tests
|
||||
func (suite *IdentityDAOTestSuite) SetupSuite() {
|
||||
// Note: In production, these would initialize actual test app
|
||||
// For now, we provide the structure for the tests
|
||||
|
||||
// Initialize test app
|
||||
// suite.app = helpers.SetupTestApp()
|
||||
// suite.ctx = suite.app.NewContext(false, sdk.BlockHeader{
|
||||
// Height: 1,
|
||||
// Time: time.Now(),
|
||||
// })
|
||||
|
||||
// Create test accounts
|
||||
// suite.deployer = helpers.CreateTestAccount(suite.app, suite.ctx, "deployer", sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(1000000000))))
|
||||
// suite.voter1 = helpers.CreateTestAccount(suite.app, suite.ctx, "voter1", sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(10000000))))
|
||||
// suite.voter2 = helpers.CreateTestAccount(suite.app, suite.ctx, "voter2", sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(10000000))))
|
||||
// suite.voter3 = helpers.CreateTestAccount(suite.app, suite.ctx, "voter3", sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(10000000))))
|
||||
|
||||
// Store contract codes
|
||||
// suite.storeContracts()
|
||||
|
||||
// Deploy contracts
|
||||
// suite.deployContracts()
|
||||
}
|
||||
|
||||
// storeContracts stores all contract codes
|
||||
func (suite *IdentityDAOTestSuite) storeContracts() {
|
||||
// wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// Load contract bytecode (would be from compiled .wasm files)
|
||||
// coreWasm := helpers.LoadContractCode("../../artifacts/identity_dao_core.wasm")
|
||||
// votingWasm := helpers.LoadContractCode("../../artifacts/identity_dao_voting.wasm")
|
||||
// proposalsWasm := helpers.LoadContractCode("../../artifacts/identity_dao_proposals.wasm")
|
||||
// preProposeWasm := helpers.LoadContractCode("../../artifacts/identity_dao_pre_propose.wasm")
|
||||
|
||||
// Store codes
|
||||
// var err error
|
||||
// suite.coreCodeID, err = wasmKeeper.StoreCode(suite.ctx, suite.deployer, coreWasm)
|
||||
// suite.Require().NoError(err)
|
||||
|
||||
// suite.votingCodeID, err = wasmKeeper.StoreCode(suite.ctx, suite.deployer, votingWasm)
|
||||
// suite.Require().NoError(err)
|
||||
|
||||
// suite.proposalsCodeID, err = wasmKeeper.StoreCode(suite.ctx, suite.deployer, proposalsWasm)
|
||||
// suite.Require().NoError(err)
|
||||
|
||||
// suite.preProposeCodeID, err = wasmKeeper.StoreCode(suite.ctx, suite.deployer, preProposeWasm)
|
||||
// suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// deployContracts instantiates all contracts
|
||||
func (suite *IdentityDAOTestSuite) deployContracts() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// Deploy Core Module
|
||||
coreInitMsg := map[string]any{
|
||||
"admin": suite.deployer.String(),
|
||||
"dao_name": "Test Identity DAO",
|
||||
"dao_uri": "https://test.dao",
|
||||
"voting_module": nil,
|
||||
"proposal_modules": []string{},
|
||||
}
|
||||
|
||||
coreInitBytes, err := json.Marshal(coreInitMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.coreAddr, _, err = wasmKeeper.InstantiateContract(
|
||||
suite.ctx,
|
||||
suite.coreCodeID,
|
||||
suite.deployer,
|
||||
suite.deployer,
|
||||
coreInitBytes,
|
||||
"identity-dao-core",
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Deploy Voting Module
|
||||
votingInitMsg := map[string]any{
|
||||
"dao_address": suite.coreAddr.String(),
|
||||
"min_verification_level": 1,
|
||||
"voting_period": 604800,
|
||||
"quorum_percentage": 20,
|
||||
"threshold_percentage": 51,
|
||||
}
|
||||
|
||||
votingInitBytes, err := json.Marshal(votingInitMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.votingAddr, _, err = wasmKeeper.InstantiateContract(
|
||||
suite.ctx,
|
||||
suite.votingCodeID,
|
||||
suite.deployer,
|
||||
suite.coreAddr,
|
||||
votingInitBytes,
|
||||
"did-voting",
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Deploy Pre-Propose Module
|
||||
preProposeInitMsg := map[string]any{
|
||||
"proposal_module": nil,
|
||||
"min_verification_status": "Basic",
|
||||
"deposit_amount": "1000000",
|
||||
"deposit_denom": "usnr",
|
||||
}
|
||||
|
||||
preProposeInitBytes, err := json.Marshal(preProposeInitMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.preProposeAddr, _, err = wasmKeeper.InstantiateContract(
|
||||
suite.ctx,
|
||||
suite.preProposeCodeID,
|
||||
suite.deployer,
|
||||
suite.coreAddr,
|
||||
preProposeInitBytes,
|
||||
"pre-propose-identity",
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Deploy Proposals Module
|
||||
proposalsInitMsg := map[string]any{
|
||||
"dao_address": suite.coreAddr.String(),
|
||||
"voting_module": suite.votingAddr.String(),
|
||||
"pre_propose_module": suite.preProposeAddr.String(),
|
||||
"proposal_duration": 604800,
|
||||
"min_verification_level": 1,
|
||||
}
|
||||
|
||||
proposalsInitBytes, err := json.Marshal(proposalsInitMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.proposalsAddr, _, err = wasmKeeper.InstantiateContract(
|
||||
suite.ctx,
|
||||
suite.proposalsCodeID,
|
||||
suite.deployer,
|
||||
suite.coreAddr,
|
||||
proposalsInitBytes,
|
||||
"identity-proposals",
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Update Core Module with voting and proposal modules
|
||||
updateMsg := map[string]any{
|
||||
"update_config": map[string]any{
|
||||
"voting_module": suite.votingAddr.String(),
|
||||
"proposal_modules": []string{suite.proposalsAddr.String()},
|
||||
},
|
||||
}
|
||||
|
||||
updateBytes, err := json.Marshal(updateMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.coreAddr,
|
||||
suite.deployer,
|
||||
updateBytes,
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
|
||||
// TestProposalLifecycle tests the complete proposal workflow
|
||||
func (suite *IdentityDAOTestSuite) TestProposalLifecycle() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// 1. Submit proposal through pre-propose module
|
||||
submitMsg := map[string]any{
|
||||
"submit_proposal": map[string]any{
|
||||
"title": "Test Proposal",
|
||||
"description": "This is a test proposal",
|
||||
"msgs": []map[string]any{
|
||||
{
|
||||
"bank": map[string]any{
|
||||
"send": map[string]any{
|
||||
"to_address": suite.voter1.String(),
|
||||
"amount": []map[string]any{
|
||||
{
|
||||
"denom": "usnr",
|
||||
"amount": "100000",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
submitBytes, err := json.Marshal(submitMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Submit with deposit
|
||||
deposit := sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(1000000)))
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.preProposeAddr,
|
||||
suite.voter1,
|
||||
submitBytes,
|
||||
deposit,
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// 2. Query pending proposals
|
||||
queryMsg := map[string]any{
|
||||
"pending_proposals": map[string]any{},
|
||||
}
|
||||
|
||||
queryBytes, err := json.Marshal(queryMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := wasmKeeper.QuerySmart(suite.ctx, suite.preProposeAddr, queryBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var pendingResp map[string]any
|
||||
err = json.Unmarshal(result, &pendingResp)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
proposals := pendingResp["proposals"].([]any)
|
||||
suite.Require().Len(proposals, 1, "Should have one pending proposal")
|
||||
|
||||
// 3. Approve proposal (move to voting)
|
||||
approveMsg := map[string]any{
|
||||
"approve_proposal": map[string]any{
|
||||
"proposal_id": 1,
|
||||
},
|
||||
}
|
||||
|
||||
approveBytes, err := json.Marshal(approveMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.preProposeAddr,
|
||||
suite.deployer, // Admin approves
|
||||
approveBytes,
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// 4. Vote on proposal
|
||||
voteMsg := map[string]any{
|
||||
"vote": map[string]any{
|
||||
"proposal_id": 1,
|
||||
"vote": "yes",
|
||||
},
|
||||
}
|
||||
|
||||
voteBytes, err := json.Marshal(voteMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Multiple voters vote
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.votingAddr,
|
||||
suite.voter1,
|
||||
voteBytes,
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.votingAddr,
|
||||
suite.voter2,
|
||||
voteBytes,
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// 5. Query proposal status
|
||||
statusQuery := map[string]any{
|
||||
"proposal": map[string]any{
|
||||
"proposal_id": 1,
|
||||
},
|
||||
}
|
||||
|
||||
statusBytes, err := json.Marshal(statusQuery)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
statusResult, err := wasmKeeper.QuerySmart(suite.ctx, suite.proposalsAddr, statusBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var statusResp map[string]any
|
||||
err = json.Unmarshal(statusResult, &statusResp)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify proposal status
|
||||
suite.Equal("open", statusResp["status"], "Proposal should be open for voting")
|
||||
|
||||
// 6. Execute proposal after voting period
|
||||
// Fast-forward time
|
||||
suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(7 * 24 * time.Hour))
|
||||
|
||||
executeMsg := map[string]any{
|
||||
"execute": map[string]any{
|
||||
"proposal_id": 1,
|
||||
},
|
||||
}
|
||||
|
||||
executeBytes, err := json.Marshal(executeMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.proposalsAddr,
|
||||
suite.deployer,
|
||||
executeBytes,
|
||||
sdk.NewCoins(),
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify execution
|
||||
statusResult, err = wasmKeeper.QuerySmart(suite.ctx, suite.proposalsAddr, statusBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = json.Unmarshal(statusResult, &statusResp)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
suite.Equal("executed", statusResp["status"], "Proposal should be executed")
|
||||
}
|
||||
|
||||
// TestDIDVerification tests DID-based voting power
|
||||
func (suite *IdentityDAOTestSuite) TestDIDVerification() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// Query voting power for voter with DID
|
||||
queryMsg := map[string]any{
|
||||
"voting_power": map[string]any{
|
||||
"address": suite.voter1.String(),
|
||||
},
|
||||
}
|
||||
|
||||
queryBytes, err := json.Marshal(queryMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := wasmKeeper.QuerySmart(suite.ctx, suite.votingAddr, queryBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var powerResp map[string]any
|
||||
err = json.Unmarshal(result, &powerResp)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify voting power based on verification level
|
||||
power := powerResp["power"].(string)
|
||||
suite.NotEqual("0", power, "Verified DID should have voting power")
|
||||
}
|
||||
|
||||
// TestTreasuryManagement tests DAO treasury operations
|
||||
func (suite *IdentityDAOTestSuite) TestTreasuryManagement() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
bankKeeper := suite.app.BankKeeper
|
||||
|
||||
// Send funds to DAO treasury
|
||||
treasuryFunds := sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(50000000)))
|
||||
err := bankKeeper.SendCoins(suite.ctx, suite.deployer, suite.coreAddr, treasuryFunds)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Query treasury balance
|
||||
balance := bankKeeper.GetBalance(suite.ctx, suite.coreAddr, "usnr")
|
||||
suite.Equal(treasuryFunds[0], balance, "Treasury should have the sent funds")
|
||||
|
||||
// Create treasury spend proposal
|
||||
spendMsg := map[string]any{
|
||||
"submit_proposal": map[string]any{
|
||||
"title": "Treasury Spend",
|
||||
"description": "Spend from treasury",
|
||||
"msgs": []map[string]any{
|
||||
{
|
||||
"bank": map[string]any{
|
||||
"send": map[string]any{
|
||||
"from_address": suite.coreAddr.String(),
|
||||
"to_address": suite.voter2.String(),
|
||||
"amount": []map[string]any{
|
||||
{
|
||||
"denom": "usnr",
|
||||
"amount": "1000000",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
spendBytes, err := json.Marshal(spendMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(1000000)))
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.preProposeAddr,
|
||||
suite.voter1,
|
||||
spendBytes,
|
||||
deposit,
|
||||
)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify treasury spend requires proper authorization
|
||||
// This would go through the full proposal workflow
|
||||
}
|
||||
|
||||
// TestAttestationGovernance tests identity attestation proposals
|
||||
func (suite *IdentityDAOTestSuite) TestAttestationGovernance() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// Create attestation policy change proposal
|
||||
attestMsg := map[string]any{
|
||||
"submit_proposal": map[string]any{
|
||||
"title": "Update Attestation Policy",
|
||||
"description": "Change attestation requirements",
|
||||
"msgs": []map[string]any{
|
||||
{
|
||||
"custom": map[string]any{
|
||||
"update_attestation_policy": map[string]any{
|
||||
"min_attestations": 2,
|
||||
"valid_types": []string{"identity", "reputation"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
attestBytes, err := json.Marshal(attestMsg)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
deposit := sdk.NewCoins(sdk.NewCoin("usnr", sdk.NewInt(1000000)))
|
||||
_, err = wasmKeeper.ExecuteContract(
|
||||
suite.ctx,
|
||||
suite.preProposeAddr,
|
||||
suite.voter3, // Must have high verification level
|
||||
attestBytes,
|
||||
deposit,
|
||||
)
|
||||
// This might fail if voter3 doesn't have sufficient verification
|
||||
// suite.Require().Error(err, "Should require higher verification for attestation proposals")
|
||||
}
|
||||
|
||||
// TestWyomingDAOCompliance tests Wyoming DAO legal compliance
|
||||
func (suite *IdentityDAOTestSuite) TestWyomingDAOCompliance() {
|
||||
wasmKeeper := suite.app.WasmKeeper
|
||||
|
||||
// Query DAO configuration
|
||||
configQuery := map[string]any{
|
||||
"config": map[string]any{},
|
||||
}
|
||||
|
||||
configBytes, err := json.Marshal(configQuery)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
result, err := wasmKeeper.QuerySmart(suite.ctx, suite.coreAddr, configBytes)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
var config map[string]any
|
||||
err = json.Unmarshal(result, &config)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Verify Wyoming DAO requirements
|
||||
suite.NotEmpty(config["dao_name"], "DAO must have a name")
|
||||
suite.NotEmpty(config["dao_uri"], "DAO must have a URI")
|
||||
suite.NotEmpty(config["admin"], "DAO must have an admin")
|
||||
|
||||
// Verify voting mechanism exists
|
||||
suite.NotEmpty(config["voting_module"], "DAO must have voting module")
|
||||
suite.NotEmpty(config["proposal_modules"], "DAO must have proposal modules")
|
||||
}
|
||||
|
||||
// TestSuite runs the test suite
|
||||
func TestIdentityDAO(t *testing.T) {
|
||||
suite.Run(t, new(IdentityDAOTestSuite))
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
#[cfg(test)]
|
||||
mod did_integration_tests {
|
||||
use cosmwasm_std::{
|
||||
testing::{mock_dependencies_with_balance, mock_env, mock_info, MockApi, MockQuerier, MockStorage},
|
||||
from_json, to_json_binary, Addr, Binary, Coin, ContractResult, Deps, DepsMut, Empty, Env,
|
||||
MessageInfo, Response, StdError, StdResult, Uint128, WasmMsg, CosmosMsg,
|
||||
QueryRequest, SystemResult,
|
||||
};
|
||||
use cosmwasm_std::testing::MockQuerierCustomHandlerResult;
|
||||
|
||||
use identity_dao_shared::{
|
||||
CoreInstantiateMsg, CoreExecuteMsg, CoreQueryMsg,
|
||||
VotingInstantiateMsg, VotingExecuteMsg, VotingQueryMsg,
|
||||
ProposalInstantiateMsg, ProposalExecuteMsg, ProposalQueryMsg,
|
||||
PreProposeInstantiateMsg, PreProposeExecuteMsg, PreProposeQueryMsg,
|
||||
VotingConfig, ProposalStatus, Vote, DaoConfigResponse,
|
||||
VotingPowerResponse, ProposalResponse, IdentityVoter, VerificationStatus,
|
||||
bindings::{SonrQuery, DIDDocumentResponse, VerificationResponse},
|
||||
};
|
||||
|
||||
struct IntegrationTestSetup {
|
||||
core_addr: Addr,
|
||||
voting_addr: Addr,
|
||||
proposal_addr: Addr,
|
||||
pre_propose_addr: Addr,
|
||||
}
|
||||
|
||||
/// Setup all DAO contracts with proper module registration
|
||||
fn setup_dao_contracts() -> (MockStorage, MockApi, MockQuerier, IntegrationTestSetup) {
|
||||
let mut storage = MockStorage::default();
|
||||
let api = MockApi::default();
|
||||
let mut querier = MockQuerier::new(&[]);
|
||||
|
||||
// Setup custom query handler for DID module queries
|
||||
querier.update_wasm(|query| -> MockQuerierCustomHandlerResult {
|
||||
match query {
|
||||
cosmwasm_std::WasmQuery::Smart { contract_addr, msg } => {
|
||||
// Handle DID module queries
|
||||
if let Ok(sonr_query) = from_json::<SonrQuery>(msg) {
|
||||
match sonr_query {
|
||||
SonrQuery::GetDIDDocument { did } => {
|
||||
let response = DIDDocumentResponse {
|
||||
did: did.clone(),
|
||||
controller: format!("{}_controller", did),
|
||||
verification_methods: vec![],
|
||||
authentication: vec![],
|
||||
assertion_method: vec![],
|
||||
capability_invocation: vec![],
|
||||
capability_delegation: vec![],
|
||||
service_endpoints: vec![],
|
||||
};
|
||||
return SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap()));
|
||||
},
|
||||
SonrQuery::VerifyDIDController { did, controller } => {
|
||||
let response = VerificationResponse {
|
||||
is_valid: controller == format!("{}_controller", did),
|
||||
error: None,
|
||||
};
|
||||
return SystemResult::Ok(ContractResult::Ok(to_json_binary(&response).unwrap()));
|
||||
},
|
||||
}
|
||||
}
|
||||
SystemResult::Ok(ContractResult::Err("Not a DID query".to_string()))
|
||||
},
|
||||
_ => SystemResult::Ok(ContractResult::Err("Unsupported query".to_string())),
|
||||
}
|
||||
});
|
||||
|
||||
// Contract addresses
|
||||
let core_addr = Addr::unchecked("dao_core");
|
||||
let voting_addr = Addr::unchecked("dao_voting");
|
||||
let proposal_addr = Addr::unchecked("dao_proposal");
|
||||
let pre_propose_addr = Addr::unchecked("dao_pre_propose");
|
||||
|
||||
let setup = IntegrationTestSetup {
|
||||
core_addr: core_addr.clone(),
|
||||
voting_addr: voting_addr.clone(),
|
||||
proposal_addr: proposal_addr.clone(),
|
||||
pre_propose_addr: pre_propose_addr.clone(),
|
||||
};
|
||||
|
||||
(storage, api, querier, setup)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_dao_initialization_with_did() {
|
||||
let (mut storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
let creator = mock_info("creator", &[]);
|
||||
|
||||
// 1. Initialize Core Module
|
||||
let core_msg = CoreInstantiateMsg {
|
||||
name: "Identity DAO".to_string(),
|
||||
description: "A DAO for identity management".to_string(),
|
||||
voting_config: VotingConfig {
|
||||
threshold: cosmwasm_std::Decimal::percent(51),
|
||||
quorum: cosmwasm_std::Decimal::percent(10),
|
||||
voting_period: 86400,
|
||||
proposal_deposit: Uint128::from(1000000u128),
|
||||
},
|
||||
admin: Some("admin".to_string()),
|
||||
enable_did_integration: true,
|
||||
};
|
||||
|
||||
// Simulate core instantiation
|
||||
// In real integration test, this would be done via instantiate_contract
|
||||
|
||||
// 2. Initialize Voting Module with DID integration
|
||||
let voting_msg = VotingInstantiateMsg {
|
||||
dao_core: setup.core_addr.to_string(),
|
||||
min_verification_level: 1,
|
||||
use_reputation_weight: true,
|
||||
};
|
||||
|
||||
// 3. Initialize Proposal Module
|
||||
let proposal_msg = ProposalInstantiateMsg {
|
||||
dao_core: setup.core_addr.to_string(),
|
||||
voting_module: setup.voting_addr.to_string(),
|
||||
pre_propose_module: Some(setup.pre_propose_addr.to_string()),
|
||||
proposal_deposit: Uint128::from(1000000u128),
|
||||
max_voting_period: 604800, // 7 days
|
||||
};
|
||||
|
||||
// 4. Initialize Pre-Propose Module with DID gating
|
||||
let pre_propose_msg = PreProposeInstantiateMsg {
|
||||
dao_core: setup.core_addr.to_string(),
|
||||
proposal_module: setup.proposal_addr.to_string(),
|
||||
require_verified_did: true,
|
||||
min_reputation_score: 10,
|
||||
deposit_amount: Uint128::from(1000000u128),
|
||||
deposit_denom: "usnr".to_string(),
|
||||
};
|
||||
|
||||
// Verify all modules are properly configured
|
||||
assert_eq!(voting_msg.dao_core, setup.core_addr.to_string());
|
||||
assert_eq!(proposal_msg.voting_module, setup.voting_addr.to_string());
|
||||
assert_eq!(pre_propose_msg.proposal_module, setup.proposal_addr.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_did_voter_registration_and_verification() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Register a voter with DID
|
||||
let did = "did:sonr:alice123";
|
||||
let alice_addr = "alice_address";
|
||||
|
||||
let register_msg = VotingExecuteMsg::UpdateVoter {
|
||||
did: did.to_string(),
|
||||
address: alice_addr.to_string(),
|
||||
};
|
||||
|
||||
// Simulate DID verification through stargate query
|
||||
let verification_query = SonrQuery::VerifyDIDController {
|
||||
did: did.to_string(),
|
||||
controller: format!("{}_controller", did),
|
||||
};
|
||||
|
||||
// Query would return verification status
|
||||
let deps = mock_dependencies_with_balance(&[]);
|
||||
let query_result: StdResult<Binary> = to_json_binary(&VerificationResponse {
|
||||
is_valid: true,
|
||||
error: None,
|
||||
});
|
||||
|
||||
assert!(query_result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_did_gated_proposal_creation() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Setup verified DID holder
|
||||
let proposer_did = "did:sonr:proposer456";
|
||||
let proposer_addr = "proposer_address";
|
||||
|
||||
// Create proposal through pre-propose module
|
||||
let proposal_msg = PreProposeExecuteMsg::ProposeWithDID {
|
||||
did: proposer_did.to_string(),
|
||||
title: "Upgrade Protocol".to_string(),
|
||||
description: "Proposal to upgrade the protocol to v2".to_string(),
|
||||
msgs: vec![],
|
||||
};
|
||||
|
||||
// Verify DID before allowing proposal
|
||||
let verification_query = SonrQuery::GetDIDDocument {
|
||||
did: proposer_did.to_string(),
|
||||
};
|
||||
|
||||
// Check that proposal creation requires verified DID
|
||||
let deps = mock_dependencies_with_balance(&[]);
|
||||
let did_response = DIDDocumentResponse {
|
||||
did: proposer_did.to_string(),
|
||||
controller: format!("{}_controller", proposer_did),
|
||||
verification_methods: vec![],
|
||||
authentication: vec![],
|
||||
assertion_method: vec![],
|
||||
capability_invocation: vec![],
|
||||
capability_delegation: vec![],
|
||||
service_endpoints: vec![],
|
||||
};
|
||||
|
||||
let query_result: StdResult<Binary> = to_json_binary(&did_response);
|
||||
assert!(query_result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_voting_with_did_based_power() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Setup multiple voters with different verification levels
|
||||
let voters = vec![
|
||||
("did:sonr:voter1", "voter1_addr", 100u32), // High reputation
|
||||
("did:sonr:voter2", "voter2_addr", 50u32), // Medium reputation
|
||||
("did:sonr:voter3", "voter3_addr", 10u32), // Low reputation
|
||||
];
|
||||
|
||||
for (did, addr, reputation) in voters.iter() {
|
||||
// Register voter
|
||||
let register_msg = VotingExecuteMsg::UpdateVoter {
|
||||
did: did.to_string(),
|
||||
address: addr.to_string(),
|
||||
};
|
||||
|
||||
// Voting power should be weighted by reputation
|
||||
let expected_power = calculate_did_voting_power(*reputation);
|
||||
|
||||
// Query voting power
|
||||
let query_msg = VotingQueryMsg::GetVotingPower {
|
||||
address: addr.to_string(),
|
||||
};
|
||||
|
||||
// Verify power calculation
|
||||
assert!(expected_power > Uint128::zero());
|
||||
if *reputation > 50 {
|
||||
assert!(expected_power > Uint128::from(1u128));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cross_module_proposal_execution() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Create proposal with multiple actions
|
||||
let proposal_msgs = vec![
|
||||
CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: "target_contract".to_string(),
|
||||
msg: to_json_binary(&"action1").unwrap(),
|
||||
funds: vec![],
|
||||
}),
|
||||
CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: "target_contract".to_string(),
|
||||
msg: to_json_binary(&"action2").unwrap(),
|
||||
funds: vec![],
|
||||
}),
|
||||
];
|
||||
|
||||
// Proposal creation through pre-propose
|
||||
let create_proposal = ProposalExecuteMsg::CreateProposal {
|
||||
title: "Multi-action Proposal".to_string(),
|
||||
description: "Execute multiple actions".to_string(),
|
||||
msgs: proposal_msgs.clone(),
|
||||
proposer: "proposer_addr".to_string(),
|
||||
};
|
||||
|
||||
// Simulate voting to pass proposal
|
||||
let vote_msg = VotingExecuteMsg::Vote {
|
||||
proposal_id: 1,
|
||||
vote: Vote::Yes,
|
||||
};
|
||||
|
||||
// Execute proposal through core
|
||||
let execute_msg = CoreExecuteMsg::ExecuteProposal {
|
||||
proposal_id: 1,
|
||||
};
|
||||
|
||||
// Verify all messages are executed
|
||||
assert_eq!(proposal_msgs.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ibc_did_verification() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Setup IBC channel for DID verification
|
||||
let ibc_channel = "channel-0";
|
||||
let sonr_chain_did = "did:sonr:ibc_user789";
|
||||
|
||||
// Query DID from Sonr chain via IBC
|
||||
let ibc_query = SonrQuery::GetDIDDocument {
|
||||
did: sonr_chain_did.to_string(),
|
||||
};
|
||||
|
||||
// Simulate IBC response
|
||||
let ibc_response = DIDDocumentResponse {
|
||||
did: sonr_chain_did.to_string(),
|
||||
controller: "cosmos1abc...".to_string(),
|
||||
verification_methods: vec!["key1".to_string()],
|
||||
authentication: vec!["auth1".to_string()],
|
||||
assertion_method: vec![],
|
||||
capability_invocation: vec![],
|
||||
capability_delegation: vec![],
|
||||
service_endpoints: vec![],
|
||||
};
|
||||
|
||||
// Verify cross-chain DID
|
||||
assert_eq!(ibc_response.did, sonr_chain_did);
|
||||
assert!(!ibc_response.verification_methods.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_reputation_based_quorum() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Setup voters with varying reputation
|
||||
let high_rep_voters = 2;
|
||||
let low_rep_voters = 10;
|
||||
|
||||
// High reputation voters should have more weight
|
||||
let high_rep_power = Uint128::from(10u128);
|
||||
let low_rep_power = Uint128::from(1u128);
|
||||
|
||||
let total_power = high_rep_power
|
||||
.checked_mul(Uint128::from(high_rep_voters as u128))
|
||||
.unwrap()
|
||||
.checked_add(low_rep_power.checked_mul(Uint128::from(low_rep_voters as u128)).unwrap())
|
||||
.unwrap();
|
||||
|
||||
// Calculate quorum (10% of total power)
|
||||
let quorum_threshold = total_power.multiply_ratio(10u128, 100u128);
|
||||
|
||||
// Verify that 2 high-rep voters can meet quorum
|
||||
let high_rep_voting_power = high_rep_power
|
||||
.checked_mul(Uint128::from(high_rep_voters as u128))
|
||||
.unwrap();
|
||||
|
||||
assert!(high_rep_voting_power >= quorum_threshold);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_treasury_management_with_did_auth() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Only verified DID holders can propose treasury withdrawals
|
||||
let treasury_manager_did = "did:sonr:treasurer";
|
||||
|
||||
// Create treasury withdrawal proposal
|
||||
let withdrawal_msg = CoreExecuteMsg::WithdrawFromTreasury {
|
||||
recipient: "recipient_addr".to_string(),
|
||||
amount: Uint128::from(1000000u128),
|
||||
denom: "usnr".to_string(),
|
||||
};
|
||||
|
||||
// Verify proposer has sufficient reputation
|
||||
let min_reputation_for_treasury = 50u32;
|
||||
|
||||
// Query proposer's reputation
|
||||
let verification_query = SonrQuery::GetDIDDocument {
|
||||
did: treasury_manager_did.to_string(),
|
||||
};
|
||||
|
||||
// Simulate reputation check
|
||||
let has_sufficient_reputation = true; // Would be fetched from DID module
|
||||
assert!(has_sufficient_reputation);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emergency_pause_with_did_multisig() {
|
||||
let (storage, api, querier, setup) = setup_dao_contracts();
|
||||
let env = mock_env();
|
||||
|
||||
// Emergency actions require multiple DID signatures
|
||||
let emergency_signers = vec![
|
||||
"did:sonr:emergency1",
|
||||
"did:sonr:emergency2",
|
||||
"did:sonr:emergency3",
|
||||
];
|
||||
|
||||
// Each signer must be verified
|
||||
for signer_did in emergency_signers.iter() {
|
||||
let verification = SonrQuery::VerifyDIDController {
|
||||
did: signer_did.to_string(),
|
||||
controller: format!("{}_controller", signer_did),
|
||||
};
|
||||
|
||||
// All signers must be valid
|
||||
let response = VerificationResponse {
|
||||
is_valid: true,
|
||||
error: None,
|
||||
};
|
||||
|
||||
assert!(response.is_valid);
|
||||
}
|
||||
|
||||
// Require 2/3 signatures for emergency action
|
||||
let required_signatures = 2;
|
||||
let collected_signatures = 3;
|
||||
|
||||
assert!(collected_signatures >= required_signatures);
|
||||
}
|
||||
|
||||
// Helper function to calculate DID-based voting power
|
||||
fn calculate_did_voting_power(reputation_score: u32) -> Uint128 {
|
||||
let base_power = Uint128::from(1u128);
|
||||
if reputation_score > 0 {
|
||||
// Formula: base_power * (1 + reputation_score / 100)
|
||||
let multiplier = Uint128::from((100 + reputation_score) as u128);
|
||||
base_power.multiply_ratio(multiplier, 100u128)
|
||||
} else {
|
||||
base_power
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user