Skip to content

Commit 4c1d089

Browse files
committed
fix: e2e tests
1 parent 7b96fea commit 4c1d089

6 files changed

Lines changed: 111 additions & 1 deletion

File tree

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,6 @@ jobs:
102102
- name: Run integration tests
103103
env:
104104
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105+
PRIVATE_KEY: //Alice
105106
run: |
106107
cargo test --locked --features pop-e2e

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,13 @@ Deploys and instantiates a contract.
418418
Signing:
419419
- Set `PRIVATE_KEY` in the environment to a dev key URI (e.g. `//Alice`) when `execute=true`.
420420

421+
MCP client config example (recommended):
422+
423+
```toml
424+
[mcp_servers.pop-mcp.env]
425+
PRIVATE_KEY = "//Alice"
426+
```
427+
421428
#### call_contract
422429
Calls a method on a deployed contract.
423430

@@ -436,6 +443,18 @@ Calls a method on a deployed contract.
436443
Signing:
437444
- Set `PRIVATE_KEY` in the environment to a dev key URI (e.g. `//Alice`) when `execute=true`.
438445

446+
MCP client config example (recommended):
447+
448+
```toml
449+
[mcp_servers.pop-mcp.env]
450+
PRIVATE_KEY = "//Alice"
451+
```
452+
453+
Security note:
454+
- Use dev keys only for local networks.
455+
- Prefer environment variables set in the MCP client config over CLI arguments.
456+
- Avoid logging or echoing secrets in tooling output.
457+
439458
### Network Tools
440459

441460
#### pop_up_parachain

tests/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ mod common {
5555
previous,
5656
}
5757
}
58+
59+
pub(crate) fn clear() -> Self {
60+
let lock = PRIVATE_KEY_LOCK
61+
.get_or_init(|| Mutex::new(()))
62+
.lock()
63+
.expect("Failed to lock PRIVATE_KEY guard");
64+
let previous = std::env::var("PRIVATE_KEY").ok();
65+
std::env::remove_var("PRIVATE_KEY");
66+
Self {
67+
_lock: lock,
68+
previous,
69+
}
70+
}
5871
}
5972

6073
impl Drop for PrivateKeyGuard {
@@ -321,7 +334,6 @@ mod common {
321334

322335
/// Deploy to shared ink-node.
323336
pub(crate) fn deploy(&mut self, url: &str, constructor: &str, args: &str) -> Result<()> {
324-
let _guard = PrivateKeyGuard::set();
325337
let executor = PopExecutor::new();
326338

327339
let result = deploy_contract(

tests/tools/call/chain.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::common::{is_error, is_success, text, InkNode, PrivateKeyGuard, TestEnv};
22
use anyhow::Result;
33
use pop_mcp_server::tools::call::chain::{call_chain, CallChainParams};
4+
use pop_mcp_server::PopMcpError;
45

56
#[test]
67
fn call_chain_metadata_lists_pallets() -> Result<()> {
@@ -183,3 +184,28 @@ fn call_chain_transaction_uses_env_suri() -> Result<()> {
183184
assert!(output.contains("Extrinsic") || output.contains("hash") || output.contains("0x"));
184185
Ok(())
185186
}
187+
188+
#[test]
189+
fn call_chain_execute_requires_private_key() -> Result<()> {
190+
let _guard = PrivateKeyGuard::clear();
191+
192+
let err = call_chain(
193+
TestEnv::new()?.executor(),
194+
CallChainParams {
195+
url: "ws://localhost:9944".to_string(),
196+
pallet: Some("System".to_string()),
197+
function: Some("remark".to_string()),
198+
args: Some(vec!["0x9999".to_string()]),
199+
sudo: None,
200+
execute: Some(true),
201+
metadata: None,
202+
},
203+
)
204+
.unwrap_err();
205+
206+
let PopMcpError::InvalidInput(message) = err else {
207+
panic!("expected InvalidInput error when PRIVATE_KEY is missing");
208+
};
209+
assert!(message.contains("PRIVATE_KEY"));
210+
Ok(())
211+
}

tests/tools/call/contract.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::common::{is_error, is_success, text, Contract, InkNode, PrivateKeyGuard, TestEnv};
22
use anyhow::Result;
33
use pop_mcp_server::tools::call::contract::{call_contract, CallContractParams};
4+
use pop_mcp_server::PopMcpError;
45

56
#[test]
67
fn call_contract_nonexistent_path_fails() -> Result<()> {
@@ -80,3 +81,28 @@ fn call_contract_get_and_flip_mutates_state() -> Result<()> {
8081

8182
Ok(())
8283
}
84+
85+
#[test]
86+
fn call_contract_execute_requires_private_key() -> Result<()> {
87+
let _guard = PrivateKeyGuard::clear();
88+
89+
let err = call_contract(
90+
TestEnv::new()?.executor(),
91+
CallContractParams {
92+
path: "dummy_contract".to_string(),
93+
contract: "0x1234".to_string(),
94+
message: "flip".to_string(),
95+
args: None,
96+
value: None,
97+
execute: Some(true),
98+
url: Some("ws://localhost:9944".to_string()),
99+
},
100+
)
101+
.unwrap_err();
102+
103+
let PopMcpError::InvalidInput(message) = err else {
104+
panic!("expected InvalidInput error when PRIVATE_KEY is missing");
105+
};
106+
assert!(message.contains("PRIVATE_KEY"));
107+
Ok(())
108+
}

tests/tools/up/contract.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::common::{is_error, is_success, text, Contract, InkNode, PrivateKeyGuard, TestEnv};
22
use anyhow::Result;
33
use pop_mcp_server::tools::up::contract::{deploy_contract, DeployContractParams};
4+
use pop_mcp_server::PopMcpError;
45

56
#[test]
67
fn deploy_contract_nonexistent_path_fails() -> Result<()> {
@@ -45,3 +46,28 @@ fn deploy_contract_succeeds_and_returns_address() -> Result<()> {
4546
assert!(output.contains("0x") || output.contains("5"));
4647
Ok(())
4748
}
49+
50+
#[test]
51+
fn deploy_contract_execute_requires_private_key() -> Result<()> {
52+
let _guard = PrivateKeyGuard::clear();
53+
54+
let err = deploy_contract(
55+
TestEnv::new()?.executor(),
56+
DeployContractParams {
57+
path: "dummy_contract".to_string(),
58+
constructor: Some("new".to_string()),
59+
args: Some("false".to_string()),
60+
value: None,
61+
execute: Some(true),
62+
url: Some("ws://localhost:9944".to_string()),
63+
},
64+
None,
65+
)
66+
.unwrap_err();
67+
68+
let PopMcpError::InvalidInput(message) = err else {
69+
panic!("expected InvalidInput error when PRIVATE_KEY is missing");
70+
};
71+
assert!(message.contains("PRIVATE_KEY"));
72+
Ok(())
73+
}

0 commit comments

Comments
 (0)