Skip to content

Commit

Permalink
feat(user_registry): Get/set user metadata (#7)
Browse files Browse the repository at this point in the history
Adds update_metadata_item and get_metadata_item_value coordinator functions for managing a user curated key-value store - implemented by encoding key-value pairs into link tags on the user's agent pub key.

* chore: refactor e2e test

* feat: get/set user metadata + e2e test

* feat(user_registry): get another user's metadata + test refactor
  • Loading branch information
8e8b2c authored Apr 16, 2024
1 parent 8dc5e14 commit 17239f8
Show file tree
Hide file tree
Showing 28 changed files with 1,153 additions and 869 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ jobs:
path: "**/node_modules"
key: node_modules-${{ hashFiles('**/package-lock.json')}}
- run: npm i
- run: npx puppeteer browsers install chrome
- run: npm run build:client
- name: Start frontend in background
run: npm run dev -w @holochain-game-identity/e2e &
Expand Down
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"cSpell.words": ["Dalek", "Holochain", "Solana", "zome", "zomes"]
"cSpell.words": ["Dalek", "Holochain", "pubkey", "Solana", "zome", "zomes"]
}
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ hdi = "=0.3.6"
hdk = "=0.2.6"
holo_hash = { version = "=0.2.6", features = ["encoding"] }
serde = "=1.0.166"
bincode = "1.3.3"
alloy-primitives = { version = "0.6.3", features = ["serde", "k256"] }
ed25519-dalek = { version = "2.1.1", features = ["serde"] }
bs58 = "0.5.0"
Expand Down
1 change: 1 addition & 0 deletions crates/game_identity_tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"]
name = "game_identity_tests"

[dependencies]
serde = { workspace = true }
game_identity_types = { workspace = true }
hdk = { workspace = true, features = ["encoding", "test_utils"] }
holochain = { workspace = true, default-features = false, features = [
Expand Down
118 changes: 117 additions & 1 deletion crates/game_identity_tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
use std::time::Duration;

use game_identity_types::GameIdentityDnaProperties;
use hdk::prelude::*;
use holochain::{prelude::DnaFile, sweettest::SweetDnaFile};
use holochain::{
conductor::{api::error::ConductorApiResult, config::ConductorConfig},
prelude::DnaFile,
sweettest::{consistency, SweetAgents, SweetCell, SweetConductorBatch, SweetDnaFile},
};

#[cfg(test)]
mod tests;

async fn load_dna() -> DnaFile {
// Use prebuilt dna file
Expand All @@ -24,3 +33,110 @@ pub async fn game_identity_dna_with_authority(authority_agent: &AgentPubKey) ->
quantum_time: None,
})
}

pub struct TestSetup {
pub conductors: SweetConductorBatch,
cells: Vec<SweetCell>,
}

impl TestSetup {
pub async fn new(user_count: usize) -> Self {
// Set up conductors
let mut conductors: SweetConductorBatch =
SweetConductorBatch::from_config(1 + user_count, ConductorConfig::default()).await;

let authority_agent_pubkey = SweetAgents::one(conductors[0].keystore()).await;

let dnas = &[game_identity_dna_with_authority(&authority_agent_pubkey).await];

let authority_app = conductors[0]
.setup_app_for_agent("game_identity", authority_agent_pubkey.clone(), dnas)
.await
.unwrap();
let (authority_cell,) = authority_app.into_tuple();
let mut cells = Vec::from([authority_cell]);
for i in 1..1 + user_count {
let user_app = conductors[i]
.setup_app("game_identity", dnas)
.await
.unwrap();
let (user_cell,) = user_app.into_tuple();
cells.push(user_cell);
}

TestSetup { conductors, cells }
}

pub async fn authority_only() -> Self {
Self::new(0).await
}

pub async fn authority_and_alice() -> Self {
Self::new(1).await
}

pub async fn authority_and_alice_bob() -> Self {
Self::new(2).await
}

pub async fn authority_call<I, O>(
&self,
zome_name: &str,
fn_name: &str,
payload: I,
) -> ConductorApiResult<O>
where
I: serde::Serialize + std::fmt::Debug,
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
self.conductors[0]
.call_fallible(&self.cells[0].zome(zome_name), fn_name, payload)
.await
}

pub async fn alice_call<I, O>(
&self,
zome_name: &str,
fn_name: &str,
payload: I,
) -> ConductorApiResult<O>
where
I: serde::Serialize + std::fmt::Debug,
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
self.conductors[1]
.call_fallible(&self.cells[1].zome(zome_name), fn_name, payload)
.await
}

pub async fn bob_call<I, O>(
&self,
zome_name: &str,
fn_name: &str,
payload: I,
) -> ConductorApiResult<O>
where
I: serde::Serialize + std::fmt::Debug,
O: serde::de::DeserializeOwned + std::fmt::Debug,
{
self.conductors[2]
.call_fallible(&self.cells[2].zome(zome_name), fn_name, payload)
.await
}

pub fn authority_pubkey(&self) -> AgentPubKey {
self.cells[0].agent_pubkey().clone()
}

pub fn alice_pubkey(&self) -> AgentPubKey {
self.cells[1].agent_pubkey().clone()
}

pub fn bob_pubkey(&self) -> AgentPubKey {
self.cells[1].agent_pubkey().clone()
}

pub async fn consistency(&self) {
consistency(self.cells.iter(), 100, Duration::from_secs(10)).await;
}
}
2 changes: 2 additions & 0 deletions crates/game_identity_tests/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod signer;
mod username_registry;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use game_identity_tests::game_identity_dna_with_authority;
use crate::game_identity_dna_with_authority;
use game_identity_types::SignableBytes;
use hdk::prelude::fake_agent_pubkey_1;
use holochain::{conductor::config::ConductorConfig, prelude::Signature, sweettest::*};
Expand Down
3 changes: 3 additions & 0 deletions crates/game_identity_tests/src/tests/username_registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod user_metadata;
mod username_attestation;
mod wallet_attestation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::collections::HashMap;

use game_identity_types::{GetMetadataItemValuePayload, UpdateMetadataItemPayload};
use holochain::conductor::api::error::ConductorApiError;

use crate::TestSetup;

#[tokio::test(flavor = "multi_thread")]
async fn users_can_only_update_their_own_metadata() {
let setup = TestSetup::authority_and_alice_bob().await;
setup.conductors.exchange_peer_info().await;

// Alice starts with no metadata
let initial_metadata: HashMap<String, String> = setup
.alice_call("username_registry", "get_metadata", setup.alice_pubkey())
.await
.unwrap();
assert_eq!(initial_metadata, HashMap::default());

// Bob cannot set Alice's metadata
let res1: Result<(), ConductorApiError> = setup
.bob_call(
"username_registry",
"update_metadata_item",
UpdateMetadataItemPayload {
agent_pubkey: setup.alice_pubkey(),
name: "foo".into(),
value: "bar".into(),
},
)
.await;
assert!(res1.is_err());

// Alice sets an item
let _: () = setup
.alice_call(
"username_registry",
"update_metadata_item",
UpdateMetadataItemPayload {
agent_pubkey: setup.alice_pubkey(),
name: "foo".into(),
value: "bar2".into(),
},
)
.await
.unwrap();

setup.consistency().await;

// Bob sees new item
let value1: String = setup
.bob_call(
"username_registry",
"get_metadata_item_value",
GetMetadataItemValuePayload {
agent_pubkey: setup.alice_pubkey(),
name: "foo".into(),
},
)
.await
.unwrap();
assert_eq!(value1, String::from("bar2"));
}
Loading

0 comments on commit 17239f8

Please sign in to comment.