Browse Source

Merge pull request #33 from Relayz-io/kyrylo/implement-tests-for-rpc

We've implemented the initial design for integration tests, using reqwest as a .wasm getter
develop
Kyrylo Stepanov 2 years ago
parent
commit
ce3e09f7ea
No account linked to committer's email address
4 changed files with 216 additions and 3 deletions
  1. 9
    1
      near-client/Cargo.toml
  2. 54
    2
      near-client/src/client.rs
  3. 5
    0
      near-client/src/types/near.rs
  4. 148
    0
      near-client/tests/rpc.rs

+ 9
- 1
near-client/Cargo.toml View File

2
 name = "near-client"
2
 name = "near-client"
3
 version = "0.1.0"
3
 version = "0.1.0"
4
 edition = "2021"
4
 edition = "2021"
5
-authors = ["silvestr@relayz.io"]
5
+authors = ["silvestr@relayz.io", "kyrylo@relayz.io"]
6
 description = """
6
 description = """
7
 """
7
 """
8
 
8
 
23
 serde_json = { version = "1", default-features = false }
23
 serde_json = { version = "1", default-features = false }
24
 thiserror = "1"
24
 thiserror = "1"
25
 url = "2"
25
 url = "2"
26
+
27
+[dev-dependencies]
28
+anyhow = "1"
29
+reqwest = { version = "0.11", features = ["json"] }
30
+rand = "0.8.5"
31
+rand_chacha = "0.3"
32
+tokio = { version = "1.2.1", features = ["full"] }
33
+workspaces = { version = "0.5", features = ["unstable"] }

+ 54
- 2
near-client/src/client.rs View File

10
         crypto::{ED25519PublicKey, ED25519Signature, Keypair},
10
         crypto::{ED25519PublicKey, ED25519Signature, Keypair},
11
         near::{
11
         near::{
12
             Action, Chunks, DeployContractAction, FinalExecutionOutcomeView, FinalExecutionStatus,
12
             Action, Chunks, DeployContractAction, FinalExecutionOutcomeView, FinalExecutionStatus,
13
-            FunctionCallAction, SignedTransaction, Transaction, ViewResult,
13
+            FunctionCallAction, SignedTransaction, Transaction, ViewAccessKeyResult, ViewResult,
14
         },
14
         },
15
     },
15
     },
16
     Error,
16
     Error,
118
         serde_json::from_slice(&view_result.result).map_err(Error::DeserializeTxResp)
118
         serde_json::from_slice(&view_result.result).map_err(Error::DeserializeTxResp)
119
     }
119
     }
120
 
120
 
121
+    pub async fn view_access_key<'a>(
122
+        &'a self,
123
+        account_id: &'a AccountId,
124
+        public_key: &'a ED25519PublicKey,
125
+    ) -> Result<ViewAccessKeyResult> {
126
+        let result = self
127
+            .rpc_client
128
+            .request::<ViewAccessKeyResult>(
129
+                "query",
130
+                Some(json!({
131
+                    "request_type": "view_access_key",
132
+                    "finality": "optimistic",
133
+                    "account_id": account_id,
134
+                    "public_key": public_key,
135
+                })),
136
+            )
137
+            .await
138
+            .map_err(|error| Error::Rpc {
139
+                error,
140
+                method: "query",
141
+            })?;
142
+
143
+        Ok(result)
144
+    }
145
+
121
     pub fn function_call<'a>(
146
     pub fn function_call<'a>(
122
         &'a self,
147
         &'a self,
123
         signer: &'a mut Signer,
148
         signer: &'a mut Signer,
169
         match execution_outcome.status {
194
         match execution_outcome.status {
170
             FinalExecutionStatus::Failure(err) => Err(Error::TransactionExec(err)),
195
             FinalExecutionStatus::Failure(err) => Err(Error::TransactionExec(err)),
171
             FinalExecutionStatus::SuccessValue(mut data) => {
196
             FinalExecutionStatus::SuccessValue(mut data) => {
172
-                self.transaction_info.signer.key_nonce += 1;
197
+                let nonce = execution_outcome.transaction.nonce;
198
+                self.transaction_info.signer.key_nonce = nonce;
173
                 if data.is_empty() {
199
                 if data.is_empty() {
174
                     data = serde_json::to_vec(&()).unwrap();
200
                     data = serde_json::to_vec(&()).unwrap();
175
                 }
201
                 }
179
         }
205
         }
180
     }
206
     }
181
 
207
 
208
+    pub async fn commit_empty(self) -> Result<()> {
209
+        let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
210
+        let execution_outcome = self
211
+            .transaction_info
212
+            .client
213
+            .rpc_client
214
+            .request::<FinalExecutionOutcomeView>(
215
+                "broadcast_tx_commit",
216
+                Some(json!(vec![base64::encode(transaction_bytes)])),
217
+            )
218
+            .await
219
+            .map_err(|error| Error::Rpc {
220
+                error,
221
+                method: "broadcast_tx_commit",
222
+            })?;
223
+        match execution_outcome.status {
224
+            FinalExecutionStatus::Failure(err) => Err(Error::TransactionExec(err)),
225
+            FinalExecutionStatus::SuccessValue(_) => {
226
+                let nonce = execution_outcome.transaction.nonce;
227
+                self.transaction_info.signer.key_nonce = nonce;
228
+                Ok(())
229
+            }
230
+            _ => Err(Error::TxNotStarted),
231
+        }
232
+    }
233
+
182
     pub async fn commit_async(self) -> Result<String> {
234
     pub async fn commit_async(self) -> Result<String> {
183
         let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
235
         let transaction_bytes = serialize_transaction(&self.transaction_info, self.action).await?;
184
         self.transaction_info
236
         self.transaction_info

+ 5
- 0
near-client/src/types/near.rs View File

352
     pub result: Vec<u8>,
352
     pub result: Vec<u8>,
353
     pub logs: Vec<String>,
353
     pub logs: Vec<String>,
354
 }
354
 }
355
+
356
+#[derive(Debug, Clone, Serialize, Deserialize)]
357
+pub struct ViewAccessKeyResult {
358
+    pub nonce: u64,
359
+}

+ 148
- 0
near-client/tests/rpc.rs View File

1
+use itertools::Itertools;
2
+use near_account_id::AccountId;
3
+use near_client::{
4
+    client::{NearClient, Signer},
5
+    types::crypto::{ED25519PublicKey, ED25519SecretKey, Key},
6
+};
7
+use rand::{RngCore, SeedableRng};
8
+use rand_chacha::ChaChaRng;
9
+use reqwest::Url;
10
+use serde_json::json;
11
+use std::{
12
+    fs::{create_dir, File},
13
+    io::{copy, Cursor, Read},
14
+    path::Path,
15
+    str::FromStr,
16
+};
17
+use workspaces::{network::Sandbox, types::SecretKey, Worker};
18
+
19
+// auxiliary structs and methods
20
+struct InitMeta {
21
+    worker: Worker<Sandbox>,
22
+    client: NearClient,
23
+    wasm: Vec<u8>,
24
+    signer_account_id: AccountId,
25
+}
26
+
27
+impl InitMeta {
28
+    async fn new() -> anyhow::Result<Self> {
29
+        let worker = workspaces::sandbox().await?;
30
+        let wasm = download_contract().await?;
31
+        let rpc_url = Url::parse(format!("http://localhost:{}", worker.rpc_port()).as_str())?;
32
+        let client = NearClient::new(rpc_url)?;
33
+        let signer_account_id = AccountId::from_str("alice.test.near")?;
34
+
35
+        Ok(InitMeta {
36
+            worker,
37
+            client,
38
+            wasm,
39
+            signer_account_id,
40
+        })
41
+    }
42
+}
43
+
44
+async fn create_signer(meta: &InitMeta) -> anyhow::Result<Signer> {
45
+    let secret_key = ED25519SecretKey::from_bytes(&random_bits())?;
46
+    let ws_secret_key_str = to_workspaces_sk(&secret_key);
47
+    let pk = ED25519PublicKey::from(&secret_key);
48
+    let ws_sk = SecretKey::from_str(&ws_secret_key_str)?;
49
+    let _ = meta
50
+        .worker
51
+        .create_tla(meta.signer_account_id.clone(), ws_sk)
52
+        .await?;
53
+
54
+    let access_key_nonce = meta
55
+        .client
56
+        .view_access_key(&meta.signer_account_id, &pk)
57
+        .await?
58
+        .nonce;
59
+
60
+    let signer_acc = Signer::new(
61
+        &ws_secret_key_str,
62
+        meta.signer_account_id.clone(),
63
+        access_key_nonce,
64
+    )?;
65
+
66
+    Ok(signer_acc)
67
+}
68
+
69
+async fn download_contract() -> anyhow::Result<Vec<u8>> {
70
+    let target = "https://github.com/near-examples/FT/raw/master/res/fungible_token.wasm";
71
+    let target_path = "../target/tmp-contracts";
72
+    let fname = "contract.wasm";
73
+    let full_dest = format!("{}/{}", target_path, fname);
74
+
75
+    if !Path::new(target_path).exists() {
76
+        create_dir(target_path)?;
77
+    }
78
+
79
+    if !Path::new(&full_dest).exists() {
80
+        let response = reqwest::get(target).await?;
81
+        let mut file = File::create(&full_dest)?;
82
+        let mut content = Cursor::new(response.bytes().await?);
83
+        copy(&mut content, &mut file)?;
84
+    }
85
+
86
+    let mut file = File::open(full_dest)?;
87
+    let mut data = Vec::new();
88
+    file.read_to_end(&mut data)?;
89
+
90
+    Ok(data)
91
+}
92
+
93
+fn random_bits() -> [u8; 32] {
94
+    let mut chacha = ChaChaRng::from_entropy();
95
+    let mut secret_bytes = [0_u8; 32];
96
+    chacha.fill_bytes(&mut secret_bytes);
97
+    secret_bytes
98
+}
99
+
100
+fn to_workspaces_sk(sk: &ED25519SecretKey) -> String {
101
+    let pk = ED25519PublicKey::from(sk);
102
+    let keypair = sk
103
+        .as_bytes()
104
+        .iter()
105
+        .chain(pk.as_bytes().iter())
106
+        .copied()
107
+        .collect_vec();
108
+    format!("ed25519:{}", bs58::encode(keypair).into_string())
109
+}
110
+
111
+// tests themselves
112
+#[tokio::test]
113
+async fn contract_creation() -> anyhow::Result<()> {
114
+    let meta = InitMeta::new().await?;
115
+    let mut signer = create_signer(&meta).await?;
116
+
117
+    let call = meta
118
+        .client
119
+        .deploy_contract(&mut signer, &meta.signer_account_id, meta.wasm);
120
+
121
+    call.commit_empty().await?;
122
+
123
+    Ok(())
124
+}
125
+
126
+#[tokio::test]
127
+async fn contract_function_call() -> anyhow::Result<()> {
128
+    let meta = InitMeta::new().await?;
129
+    let mut signer = create_signer(&meta).await?;
130
+
131
+    let call = meta
132
+        .client
133
+        .deploy_contract(&mut signer, &meta.signer_account_id, meta.wasm);
134
+    call.commit_empty().await?;
135
+
136
+    let call = meta
137
+        .client
138
+        .function_call(&mut signer, &meta.signer_account_id, "new_default_meta")
139
+        .args(json!({
140
+            "owner_id": &meta.signer_account_id,
141
+            "total_supply": "100",
142
+        }))
143
+        .gas(near_units::parse_gas!("300 T") as u64)
144
+        .build()?;
145
+    call.commit_empty().await?;
146
+
147
+    Ok(())
148
+}

Loading…
Cancel
Save