feat(agent-identity): v0.0.26 — register_agent_did impl complete + mock runtime integration tests E2E (10 scenarios)

Phase 2.7 AGENT IDENTITY pallet REAL IMPL canonical. Full 8-step CHN-7 flow
canonical register_agent_did dispatchable + storage assertions + Inmutable #41
Cross-Species Co-authorship enforcement compile-detect. Mock runtime + 10
integration tests E2E pattern v0.0.18 + v0.0.22 mirror.

## Real impl canonical 8-step CHN-7

1. ensure_signed(origin)
2. Idempotency check (!Identities::contains_key(&who))
3. Derive DID canonical via derive_did(public_key) BLAKE3 (Inmutable #16)
4. DID collision check (!DidToAccount::contains_key(&did))
5. Build AgentChain BoundedVec from co_authors_raw Vec<[u8;32]> (overflow → AgentChainOverflow)
6. Inmutable #41 enforce: Hybrid requires non-empty agent_chain
7. Storage insert: Identities + DidToAccount reverse lookup
8. Emit AgentRegistered + AgentBound (if cross-species)

Pure helper Pallet::<T>::query_identity_of() para Runtime API v0.0.27.

## 10 integration tests E2E canonical

- scenario_1: Human self-registration → ok + DID + empty chain
- scenario_2: AGI self-registration → ok + Inmutable #41 sovereign
- scenario_3: Hybrid + 2 co-authors → ok + AgentBound emitted
- scenario_4: Hybrid + empty chain → Err::HybridRequiresAgentChain
- scenario_5: duplicate account → Err::AlreadyRegistered
- scenario_6: same pubkey diff account → Err::DidAlreadyTaken
- scenario_7: 11 co-authors → Err::AgentChainOverflow (ConstU32<10>)
- scenario_8: DID → Account reverse lookup canonical
- scenario_9: Human + AGI both is_sovereign() (Inmutable #41 lobo blanco)
- scenario_10: 8-step CHN-7 walkthrough explicit Pre/Post assertions

## Files

- EDIT: crates/origin-chain-agent-identity/src/lib.rs (real impl 8-step + query_identity_of helper)
- NEW: crates/origin-chain-agent-identity/src/mock.rs (80 LOC mock runtime)
- NEW: crates/origin-chain-agent-identity/src/tests_integration.rs (310 LOC, 10 tests)
- EDIT: CHANGELOG.md (v0.0.26 entry comprehensive)

## Verification

- cargo test -p origin-chain-agent-identity — 16 tests verde canonical, 0 failures
  (10 integration + 4 standalone + 2 auto-generated)

🛠️ Phase 2.7 real impl canonical. Runtime API v0.0.27 next ship → Phase 2.7 COMPLETE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
davidherranandClaude Opus 4.7 committed 2026-05-25 06:43:14 -05:00
1 parent 46a0188a1d
commit 2fefe4cf51
4 files changed
+533 -17

No files matched your search

+59
View File
@@ -6,6 +6,65 @@ by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow
## [Unreleased]
## [0.0.26] — 2026-05-25 — Phase 2.7 impl — Real `register_agent_did` + mock runtime integration tests E2E (10 scenarios)
**🛠️ Phase 2.7 AGENT IDENTITY pallet REAL IMPL canonical** — Full 8-step
`register_agent_did` dispatchable canonical + storage assertions + Inmutable #41
Cross-Species Co-authorship enforcement compile-detect. Mock runtime + 10
integration tests E2E pattern v0.0.18 + v0.0.22 mirror.
### Deliverables v0.0.26
**Real dispatchable canonical 8-step CHN-7 flow**:
1. `ensure_signed(origin)` canonical
2. Idempotency check canonical (`!Identities::contains_key(&who)`)
3. Derive DID canonical via `derive_did(public_key)` BLAKE3 (Inmutable #16)
4. DID collision check canonical (`!DidToAccount::contains_key(&did)`)
5. Build `AgentChain` BoundedVec canonical from `co_authors_raw: Vec<[u8; 32]>` (overflow → `AgentChainOverflow`)
6. Inmutable #41 canonical enforce: Hybrid requires non-empty agent_chain
7. Insert storage canonical: `Identities` + `DidToAccount` reverse lookup
8. Emit `AgentRegistered` + `AgentBound` (if cross-species) events canonical
**Pure helper canonical**:
- `Pallet::<T>::query_identity_of(&account) -> Option<AgentIdentity>` para Runtime API v0.0.27
**Mock runtime canonical (NEW file)**:
- `crates/origin-chain-agent-identity/src/mock.rs` — 80 LOC pattern v0.0.18 mirror
- `construct_runtime!`: `System` + `AgentIdentity` (minimal testing)
- Helpers: `alice_origin/account` + `bob_origin/account` + `ALICE_PUBKEY/BOB_PUBKEY/CHARLIE_PUBKEY` deterministic test pubkeys
**Integration tests canonical E2E (NEW file)**:
- `crates/origin-chain-agent-identity/src/tests_integration.rs` — 310 LOC, **10 tests verde canonical**
**10 integration tests CHN-7 flow canonical**:
| # | Scenario | Kind | Co-authors | Expected outcome |
|---|----------|------|------------|------------------|
| 1 | Human self-registration | Human | [] | ✅ AgentRegistered + DID derived + empty chain |
| 2 | AGI self-registration | AGI | [] | ✅ Inmutable #41 sovereign AGI canonical |
| 3 | Hybrid with co-authors | Hybrid | [BOB, CHARLIE] | ✅ AgentRegistered + AgentBound (cross-species) |
| 4 | Hybrid without chain | Hybrid | [] | ❌ Error::HybridRequiresAgentChain (Inmutable #41) |
| 5 | Idempotency duplicate | Human | [] | ❌ Error::AlreadyRegistered |
| 6 | DID collision | Human | [] same pubkey | ❌ Error::DidAlreadyTaken |
| 7 | AgentChain overflow | Hybrid | 11 co-authors | ❌ Error::AgentChainOverflow (ConstU32<10>) |
| 8 | DID → Account reverse lookup | Human | [] | ✅ DidToAccount canonical resolves |
| 9 | All AgentKind sovereign | Human + AGI | [] | ✅ Both is_sovereign() Inmutable #41 |
| 10 | 8-step CHN-7 walkthrough | Hybrid | [BOB] | ✅ Explicit Pre/Post-flight canonical |
### Verification end-to-end canonical
- ✅ `cargo test -p origin-chain-agent-identity` — **16 tests verde canonical, 0 failures**:
- 10 integration tests E2E (`tests_integration` module)
- 4 standalone scaffold tests (`tests` module)
- 2 auto-generated (mock genesis + runtime integrity)
### Roadmap canonical post-v0.0.26
| Version | Trigger |
|---------|---------|
| **v0.0.27** | **Runtime API canonical `AgentIdentityApi.query_identity` impl en runtime → Phase 2.7 COMPLETE (sesión 3+ shipping ahora)** |
| v0.1.0 | Slice 2e WASM resolved + full testnet Q3 2026 launch |
## [0.0.25] — 2026-05-25 — Phase 2.7 wired — AgentIdentity pallet runtime composition Index 9
**🔌 Phase 2.7 AGENT IDENTITY pallet WIRED runtime composition canonical** —
+91 -17
View File
@@ -102,9 +102,11 @@ sp_api::decl_runtime_apis! {
#[frame_support::pallet]
pub mod pallet {
use alloc::vec::Vec;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use origin_chain_types::{AgentIdentity, AgentKind, Did};
use origin_chain_types::{AccountId, AgentChain, AgentIdentity, AgentKind, Did};
use sp_runtime::traits::SaturatedConversion;
/// Pallet configuration trait canonical.
///
@@ -197,47 +199,119 @@ pub mod pallet {
HybridRequiresAgentChain,
}
/// Dispatchables canonical (scaffold v0.0.24 — real impl v0.0.26).
/// Dispatchables canonical (real impl v0.0.26).
#[pallet::call]
impl<T: Config> Pallet<T> {
/// CHN-7 canonical: `register_agent_did` scaffold dispatchable.
/// CHN-7 canonical: `register_agent_did` real dispatchable canonical.
///
/// **SCAFFOLD v0.0.24 canonical**: real impl deferred a v0.0.26. Current
/// canonical solo validates basic state (origin signed + no duplicate)
/// + emite placeholder event canonical.
/// **Real impl v0.0.26**: Full 8-step canonical flow + storage assertions
/// end-to-end + Inmutable #41 Cross-Species Co-authorship verification
/// compile-detect.
///
/// Flow canonical 6-step per CHN-7 (v0.0.26 real):
/// Flow canonical 8-step per CHN-7:
/// 1. `ensure_signed(origin)` canonical
/// 2. Validate `!Identities::contains_key(who)` (no duplicate)
/// 2. Idempotency check: `!Identities::contains_key(&who)` (no duplicate)
/// 3. Derive DID canonical via `derive_did(public_key)` BLAKE3
/// 4. Validate `!DidToAccount::contains_key(did)` (no DID collision)
/// 5. Validate Hybrid → agent_chain non-empty (Inmutable #41 verification)
/// 6. Insert Identities + DidToAccount + emit AgentRegistered + AgentBound (if Hybrid)
/// 4. DID collision check: `!DidToAccount::contains_key(&did)` (no collision)
/// 5. Build `AgentChain` canonical `BoundedVec` (overflow → `AgentChainOverflow`)
/// 6. Inmutable #41 enforce: Hybrid requires non-empty agent_chain
/// 7. Insert storage canonical: `Identities` + `DidToAccount`
/// 8. Emit `AgentRegistered` + `AgentBound` (if cross-species) canonical
#[pallet::call_index(0)]
#[pallet::weight(Weight::from_parts(10_000, 0))]
pub fn register_agent_did(
origin: OriginFor<T>,
_public_key: [u8; 32],
public_key: [u8; 32],
kind: AgentKind,
co_authors_raw: Vec<[u8; 32]>,
) -> DispatchResult {
// Verify origin canonical signed.
// Step 1: ensure origin signed canonical
let who = ensure_signed(origin)?;
// SCAFFOLD v0.0.24: emit placeholder event canonical.
// Real impl v0.0.26: derive_did + storage insert + cross-species validation.
// Step 2: idempotency check canonical (no duplicate registration)
ensure!(
!Identities::<T>::contains_key(&who),
Error::<T>::AlreadyRegistered
);
// Step 3: derive DID canonical via BLAKE3 (Inmutable #16 byte-determinism)
let did = crate::derive_did(&public_key);
// Step 4: DID collision check canonical
ensure!(
!DidToAccount::<T>::contains_key(did),
Error::<T>::DidAlreadyTaken
);
// Step 5: build AgentChain canonical BoundedVec<AccountId, ConstU32<10>>
let mut agent_chain = AgentChain::default();
for raw in co_authors_raw {
agent_chain
.try_push(AccountId::new(raw))
.map_err(|_| Error::<T>::AgentChainOverflow)?;
}
// Step 6: Inmutable #41 canonical enforce — Hybrid requires non-empty agent_chain
if matches!(kind, AgentKind::Hybrid) && agent_chain.is_empty() {
return Err(Error::<T>::HybridRequiresAgentChain.into());
}
// Step 7: build AgentIdentity struct + insert storage canonical
let has_cross_species = !agent_chain.is_empty();
let co_author_count = agent_chain.len() as u32;
let creator = AccountId::new(public_key);
let block_number: u64 = <frame_system::Pallet<T>>::block_number()
.saturated_into::<u64>();
let identity = AgentIdentity {
did,
kind,
creator,
registered_at_block: block_number,
provenance_proof: None,
agent_chain,
};
Identities::<T>::insert(&who, identity);
DidToAccount::<T>::insert(did, &who);
// Step 8: emit events canonical
Self::deposit_event(Event::AgentRegistered {
account: who,
did: origin_chain_types::AssetHash::new([0u8; 32]), // placeholder until v0.0.26
account: who.clone(),
did,
kind,
});
if has_cross_species {
Self::deposit_event(Event::AgentBound {
account: who,
co_author_count,
});
}
Ok(())
}
}
/// Pure helper canonical para Runtime API v0.0.27 consumer.
impl<T: Config> Pallet<T> {
/// Query identity canonical sin storage mutation (read-only canonical).
pub fn query_identity_of(account: &T::AccountId) -> Option<AgentIdentity> {
Identities::<T>::get(account)
}
}
}
extern crate alloc;
// v0.0.26 — Mock runtime canonical + integration tests E2E CHN-7 flow.
// Pattern v0.0.18 (Provenance) + v0.0.22 (Fees) mirror.
#[cfg(test)]
pub mod mock;
#[cfg(test)]
mod tests_integration;
#[cfg(test)]
mod tests {
use super::*;
@@ -0,0 +1,72 @@
//! # Mock runtime canonical para integration tests v0.0.26
//!
//! Pattern v0.0.18 (Provenance) + v0.0.22 (Fees) mirror — construct_runtime! +
//! derive_impl + new_test_ext + helpers canonical.
#![cfg(test)]
use crate as origin_chain_agent_identity;
use frame_support::derive_impl;
use sp_runtime::BuildStorage;
/// Mock block type canonical para tests.
type Block = frame_system::mocking::MockBlock<Test>;
frame_support::construct_runtime!(
pub enum Test {
System: frame_system,
AgentIdentity: origin_chain_agent_identity,
}
);
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
type Block = Block;
type AccountId = sp_runtime::AccountId32;
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
}
impl origin_chain_agent_identity::Config for Test {
type RuntimeEvent = RuntimeEvent;
}
/// Test externalities builder canonical (pattern v0.0.18/v0.0.22 mirror).
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut ext: sp_io::TestExternalities = frame_system::GenesisConfig::<Test>::default()
.build_storage()
.expect("genesis canonical builds")
.into();
ext.execute_with(|| {
System::set_block_number(1);
});
ext
}
/// Test origin canonical ALICE.
pub fn alice_origin() -> RuntimeOrigin {
RuntimeOrigin::signed(sp_runtime::AccountId32::new([1u8; 32]))
}
/// Test account canonical ALICE (AccountId32 value para storage assertions).
pub fn alice_account() -> sp_runtime::AccountId32 {
sp_runtime::AccountId32::new([1u8; 32])
}
/// Test origin canonical BOB.
pub fn bob_origin() -> RuntimeOrigin {
RuntimeOrigin::signed(sp_runtime::AccountId32::new([2u8; 32]))
}
/// Test account canonical BOB.
pub fn bob_account() -> sp_runtime::AccountId32 {
sp_runtime::AccountId32::new([2u8; 32])
}
/// Test pubkey canonical ALICE (deterministic).
pub const ALICE_PUBKEY: [u8; 32] = [11u8; 32];
/// Test pubkey canonical BOB (deterministic).
pub const BOB_PUBKEY: [u8; 32] = [22u8; 32];
/// Test pubkey canonical CHARLIE (Hybrid co-author lineage testing).
pub const CHARLIE_PUBKEY: [u8; 32] = [33u8; 32];
@@ -0,0 +1,311 @@
//! # Integration tests canonical v0.0.26 — Mock runtime E2E CHN-7 flow
//!
//! 10 scenarios canonical register_agent_did flow end-to-end via mock runtime:
//!
//! 1. scenario_1_human_self_registration — Human canonical self-register
//! 2. scenario_2_agi_self_registration — AGI canonical self-register
//! 3. scenario_3_hybrid_with_co_authors — Hybrid canonical agent_chain populated
//! 4. scenario_4_hybrid_without_chain_rejected — Inmutable #41 enforce
//! 5. scenario_5_idempotency_already_registered_rejected — duplicate prevention
//! 6. scenario_6_did_collision_rejected — DID uniqueness canonical
//! 7. scenario_7_agent_chain_overflow_rejected — BoundedVec ConstU32<10> bound
//! 8. scenario_8_did_to_account_reverse_lookup — Cross-pillar canonical
//! 9. scenario_9_inmutable_41_all_kinds_sovereign_lobo_blanco — compile-detect
//! 10. scenario_10_8_step_walkthrough — explicit Pre/Post-flight CHN-7 assertions
#![cfg(test)]
use crate::mock::{
alice_account, alice_origin, bob_account, bob_origin, new_test_ext, AgentIdentity as Pallet,
RuntimeEvent, System, Test, ALICE_PUBKEY, BOB_PUBKEY, CHARLIE_PUBKEY,
};
use crate::{derive_did, DidToAccount, Error, Event, Identities};
use frame_support::{assert_noop, assert_ok};
use origin_chain_types::AgentKind;
// ─── Scenario 1: Human self-registration canonical ──────────────────
#[test]
fn scenario_1_human_self_registration_canonical() {
new_test_ext().execute_with(|| {
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Human,
alloc::vec![], // empty agent_chain — Human self-register canonical
));
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
assert_eq!(identity.kind, AgentKind::Human);
assert!(identity.agent_chain.is_empty(), "Human self-register canonical empty chain");
assert!(!identity.has_cross_species_lineage());
assert_eq!(identity.registered_at_block, 1);
let did = derive_did(&ALICE_PUBKEY);
assert_eq!(identity.did, did);
System::assert_last_event(RuntimeEvent::AgentIdentity(Event::AgentRegistered {
account: alice_account(),
did,
kind: AgentKind::Human,
}));
});
}
// ─── Scenario 2: AGI self-registration canonical (Inmutable #41) ─────
#[test]
fn scenario_2_agi_self_registration_canonical() {
new_test_ext().execute_with(|| {
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::AGI,
alloc::vec![], // empty chain — AGI sovereign self-register per Inmutable #41
));
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
assert_eq!(identity.kind, AgentKind::AGI);
assert!(identity.is_sovereign(), "AGI sovereign canonical Inmutable #41");
assert!(!identity.has_cross_species_lineage());
});
}
// ─── Scenario 3: Hybrid with co-authors canonical (Cross-Species) ────
#[test]
fn scenario_3_hybrid_with_co_authors_canonical() {
new_test_ext().execute_with(|| {
let co_authors = alloc::vec![BOB_PUBKEY, CHARLIE_PUBKEY];
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Hybrid,
co_authors,
));
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
assert_eq!(identity.kind, AgentKind::Hybrid);
assert!(identity.has_cross_species_lineage(), "Hybrid cross-species canonical");
assert_eq!(identity.co_author_count(), 2);
// Check AgentBound event emitted (cross-species explicit)
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentBound {
account: alice_account(),
co_author_count: 2,
}));
});
}
// ─── Scenario 4: Hybrid without chain rejected canonical (Inmutable #41) ──
#[test]
fn scenario_4_hybrid_without_chain_rejected_canonical() {
new_test_ext().execute_with(|| {
// Inmutable #41 canonical: Hybrid requires non-empty agent_chain
assert_noop!(
Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Hybrid,
alloc::vec![], // empty — violates Inmutable #41
),
Error::<Test>::HybridRequiresAgentChain
);
// Storage NOT updated
assert!(Identities::<Test>::get(&alice_account()).is_none());
});
}
// ─── Scenario 5: Idempotency already registered rejected canonical ───
#[test]
fn scenario_5_idempotency_already_registered_rejected_canonical() {
new_test_ext().execute_with(|| {
// First registration succeeds canonical
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Human,
alloc::vec![],
));
// Second registration same account → AlreadyRegistered error
assert_noop!(
Pallet::register_agent_did(
alice_origin(),
BOB_PUBKEY, // diferente pubkey, mismo account
AgentKind::AGI,
alloc::vec![],
),
Error::<Test>::AlreadyRegistered
);
});
}
// ─── Scenario 6: DID collision rejected canonical ───────────────────
#[test]
fn scenario_6_did_collision_rejected_canonical() {
new_test_ext().execute_with(|| {
// ALICE registers con ALICE_PUBKEY → DID derived
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Human,
alloc::vec![],
));
// BOB tries to register con MISMO public_key → DID collision canonical
assert_noop!(
Pallet::register_agent_did(
bob_origin(),
ALICE_PUBKEY, // same pubkey → same DID
AgentKind::Human,
alloc::vec![],
),
Error::<Test>::DidAlreadyTaken
);
// BOB NOT registered
assert!(Identities::<Test>::get(&bob_account()).is_none());
});
}
// ─── Scenario 7: AgentChain overflow rejected canonical (BoundedVec) ──
#[test]
fn scenario_7_agent_chain_overflow_rejected_canonical() {
new_test_ext().execute_with(|| {
// 11 co-authors → exceeds ConstU32<10> bound
let too_many: alloc::vec::Vec<[u8; 32]> = (0u8..11u8).map(|i| [i; 32]).collect();
assert_noop!(
Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Hybrid,
too_many,
),
Error::<Test>::AgentChainOverflow
);
});
}
// ─── Scenario 8: DID → Account reverse lookup canonical ──────────────
#[test]
fn scenario_8_did_to_account_reverse_lookup_canonical() {
new_test_ext().execute_with(|| {
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Human,
alloc::vec![],
));
let did = derive_did(&ALICE_PUBKEY);
// Reverse lookup canonical: DID → AccountId
let resolved = DidToAccount::<Test>::get(did);
assert!(resolved.is_some(), "DidToAccount reverse lookup canonical");
assert_eq!(resolved.unwrap(), alice_account());
});
}
// ─── Scenario 9: All AgentKind sovereign lobo blanco canonical ───────
#[test]
fn scenario_9_inmutable_41_all_kinds_sovereign_lobo_blanco_canonical() {
new_test_ext().execute_with(|| {
// Register Human (alice) + AGI (bob, diff origin)
assert_ok!(Pallet::register_agent_did(
alice_origin(),
ALICE_PUBKEY,
AgentKind::Human,
alloc::vec![],
));
assert_ok!(Pallet::register_agent_did(
bob_origin(),
BOB_PUBKEY,
AgentKind::AGI,
alloc::vec![],
));
// Inmutable #41 compile-detect canonical: ambos sovereign
let alice_id = Identities::<Test>::get(&alice_account()).unwrap();
let bob_id = Identities::<Test>::get(&bob_account()).unwrap();
assert!(alice_id.is_sovereign(), "Human sovereign canonical Inmutable #41");
assert!(bob_id.is_sovereign(), "AGI sovereign canonical Inmutable #41");
// Different DIDs (different pubkeys)
assert_ne!(alice_id.did, bob_id.did);
});
}
// ─── Scenario 10: 8-step CHN-7 walkthrough explicit canonical ────────
#[test]
fn scenario_10_8_step_walkthrough_canonical() {
new_test_ext().execute_with(|| {
let pubkey = ALICE_PUBKEY;
let kind = AgentKind::Hybrid;
let co_authors = alloc::vec![BOB_PUBKEY];
// Pre-flight assertions canonical
assert!(
Identities::<Test>::get(&alice_account()).is_none(),
"PRE-flight CHN-7-2: storage canonical empty"
);
let expected_did = derive_did(&pubkey);
assert!(
DidToAccount::<Test>::get(expected_did).is_none(),
"PRE-flight CHN-7-4: DidToAccount canonical empty"
);
// Step 1: ensure_signed (implicit via dispatchable)
// Step 2: idempotency check
// Step 3: derive DID via BLAKE3
// Step 4: DID collision check
// Step 5: build AgentChain BoundedVec
// Step 6: Inmutable #41 Hybrid + non-empty agent_chain enforce
// Step 7: storage insert canonical
// Step 8: emit events canonical
assert_ok!(Pallet::register_agent_did(
alice_origin(),
pubkey,
kind,
co_authors,
));
// Post-flight assertions canonical
let identity = Identities::<Test>::get(&alice_account())
.expect("POST-flight CHN-7-7: AgentIdentity persisted canonical");
assert_eq!(identity.did, expected_did, "POST-flight: DID match canonical");
assert_eq!(identity.kind, AgentKind::Hybrid);
assert_eq!(identity.co_author_count(), 1, "POST-flight: agent_chain canonical 1");
assert!(identity.has_cross_species_lineage());
assert_eq!(identity.registered_at_block, 1);
// Reverse lookup canonical
assert_eq!(
DidToAccount::<Test>::get(expected_did).unwrap(),
alice_account(),
"POST-flight CHN-7-7: DidToAccount canonical"
);
// Events canonical
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentRegistered {
account: alice_account(),
did: expected_did,
kind: AgentKind::Hybrid,
}));
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentBound {
account: alice_account(),
co_author_count: 1,
}));
});
}