Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: rs-sdk tests refactoring #1566

Merged
merged 7 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

17 changes: 0 additions & 17 deletions packages/rs-sdk/.env.example

This file was deleted.

32 changes: 24 additions & 8 deletions packages/rs-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dpp = { path = "../rs-dpp", features = [
"identity-value-conversion",
] }
dapi-grpc = { path = "../dapi-grpc", features = ["client"] }
rs-dapi-client = { path = "../rs-dapi-client" }
rs-dapi-client = { path = "../rs-dapi-client", default-features = false }
drive = { path = "../rs-drive", default-features = false, features = [
"verify",
] }
Expand All @@ -36,18 +36,19 @@ derive_more = { version = "0.99.16" }
dashcore-rpc = { git = "https://github.com/dashpay/rust-dashcore-rpc", branch = "master" }

[dev-dependencies]
rs-dapi-client = { path = "../rs-dapi-client", features = ["mocks", "dump"] }
rs-dapi-client = { path = "../rs-dapi-client", features = ["mocks"] }
base64 = { version = "0.21.0" }
tracing-subscriber = { version = "0.3.16" }
dpp = { path = "../rs-dpp", features = [
"client",
"validation",
"random-documents",
] }
data-contracts = { path = "../data-contracts" }


[features]
default = ["mocks"]
default = ["mocks", "offline-testing"]
mocks = [
"dep:serde_json",
"rs-dapi-client/mocks",
Expand All @@ -62,10 +63,25 @@ mocks = [
"dep:envy",
]

# Disable this feature to run integration tests against a local Dash Platform
# and dump all traffic to generate test vectors.
# Run integration tests using test vectors from `tests/vectors/` instead of connecting to live Dash Platform.
#
# This feature is enabled by default because disabling it requires a local Dash Platform
# node to be running. Configuration of the node is done via environment variables
# and/or `${CARGO_MAINFEST_DIR}/.env` file; see also `.env.example`.
# This feature is enabled by default to allow testing without connecting to the Dash Platform as
# part of CI/CD workflows.
#
# `offline-testing` and `network-testing` are mutually exclusive.
offline-testing = ["mocks"]

# Run integration tests using a live Dash Platform network.
#
# Requires configuration of Dash Platform connectivity.
# See [README.md] for more details.
#
# `offline-testing` and `network-testing` are mutually exclusive.
network-testing = ["mocks"]

# Generate test vectors for offline tests.
#
# This will run tests in `network-testing` mode and
# dump all requests and responses to `tests/vectors/`,
# so that they can be used later for `offline-testing`.
generate-test-vectors = ["network-testing"]
61 changes: 18 additions & 43 deletions packages/rs-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,65 +12,40 @@ You can also inspect tests in `tests/` folder for more detailed examples.

## Tests

This section provides instructions on how to test the RS-SDK for Dash Platform. The tests can be run in two modes: **offline** (without connectivity to the Dash Platform) and **online** (with connectivity to the Dash Platform). **Offline** mode is the default one.
This section provides instructions on how to test the RS-SDK for Dash Platform. The tests can be run in two modes: **offline** (without connectivity to the Dash Platform) and **network** (with connectivity to the Dash Platform). **Offline** mode is the default one.

## Online Testing
## Network Testing

Online testing requires connectivity to the Dash Platform and Dash Core. This mode generates new test vectors that can be used in offline mode.
Network testing requires connectivity to the Dash Platform and Dash Core.

Follow these steps to conduct online testing:
Follow these steps to conduct network testing:

1. Configure the environment variables in `packages/rs-sdk/.env`. Refer to the "Test Configuration" section below.
2. Optionally, you can remove existing test vectors.
3. Run the test without default features, but with `mocks` feature enabled.

Use the following commands for the above steps:
1. Configure platform address and credentials in `packages/rs-sdk/tests/.env`.
Note that the `.env` file might already be configured during project setup (`yarn setup`).
2. Run the test without default features, but with `network-testing` feature enabled.

```bash
cd packages/rs-sdk
rm tests/vectors/*
cargo test -p rs-sdk --no-default-features --features mocks
cargo test -p rs-sdk --no-default-features --features network-testing
```

## Offline Testing

Offline testing uses the vectors generated in online mode. These vectors must be saved in `packages/rs-sdk/tests/vectors`.

Run the offline test using the following command:

```bash
cargo test -p rs-sdk
```

## Test Configuration
Offline testing uses the vectors generated using `packages/rs-sdk/scripts/generate_test_vectors.sh` script.
These vectors must be saved in `packages/rs-sdk/tests/vectors`.

For the `offline-testing` feature, you need to set the configuration in the environment variables or in `packages/rs-sdk/.env` file. You can refer to `packages/rs-sdk/.env.example` for the format.
### Generating test vectors

The identifiers are generated with the platform test suite. To display them, apply the following diff:
To generate test vectors for offline testing:

```diff
diff --git a/packages/platform-test-suite/test/functional/platform/Document.spec.js b/packages/platform-test-suite/test/functional/platform/Document.spec.js
index 29dca311b..fba0aefc2 100644
--- a/packages/platform-test-suite/test/functional/platform/Document.spec.js
+++ b/packages/platform-test-suite/test/functional/platform/Document.spec.js
@@ -180,6 +180,9 @@ describe('Platform', () => {

// Additional wait time to mitigate testnet latency
await waitForSTPropagated();
+ console.log("Owner ID: " + document.getOwnerId().toString("base58"));
+ console.log("Data Contract: " + document.getDataContractId().toString("base58"));
+ console.log("Document: " + document.getId().toString("base58"));
});

it('should fetch created document', async () => {
1. Configure platform address and credentials in `packages/rs-sdk/tests/.env`.
Note that the `.env` file might already be configured during project setup (`yarn setup`).
2. Run `packages/rs-sdk/scripts/generate_test_vectors.sh` script.

```
### Running tests in offline mode

To run the document test, use the following commands:
Run the offline test using the following command:

```bash
cd packages/platform-test-suite/
yarn mocha -b test/functional/platform/Document.spec.js
cargo test -p rs-sdk
```

Find the values in the output and copy them to `packages/rs-sdk/.env`.
21 changes: 21 additions & 0 deletions packages/rs-sdk/scripts/generate_test_vectors.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#! /bin/bash -ex

# This script generates test vectors for offline testing of the SDK.
# Test vectors will be generated in the `tests/vectors` directory.
#
# Existing test vectors are removed before generating new ones.
#
# Generation of test vectors is done by running the SDK tests with the
# `generate-test-vectors` feature enabled.

CARGO_DIR="$(realpath "$(dirname "$0")/..")"

pushd "$CARGO_DIR"

rm -f "${CARGO_DIR}"/tests/vectors/*

cargo test -p rs-sdk \
--no-default-features \
--features generate-test-vectors

popd
3 changes: 1 addition & 2 deletions packages/rs-sdk/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
//! ```
//!
//! See tests/mock_*.rs for more detailed examples.
#[cfg(feature = "mocks")]
pub mod config;

#[cfg(feature = "mocks")]
mod requests;
#[cfg(feature = "mocks")]
Expand Down
8 changes: 8 additions & 0 deletions packages/rs-sdk/tests/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Configuration of tests and examples

RS_SDK_PLATFORM_HOST="127.0.0.1"
RS_SDK_PLATFORM_PORT=2443

RS_SDK_CORE_PORT=20002
RS_SDK_CORE_USER="someuser"
RS_SDK_CORE_PASSWORD="verysecretpassword"
21 changes: 0 additions & 21 deletions packages/rs-sdk/tests/fetch/common.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,4 @@
use dpp::{data_contract::DataContractFactory, prelude::Identifier};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct TestConfig {
// IDs of some objects generated by the testnet
/// ID of existing identity.
///
/// Format: Base58
pub existing_identity_id: Identifier,
/// ID of existing data contract.
///
/// Format: Base58
pub existing_data_contract_id: Identifier,
/// Name of document type defined for [`existing_data_contract_id`](Config::existing_data_contract_id).
pub existing_document_type_name: String,
/// ID of document of the type [`existing_document_type_name`](Config::existing_document_type_name)
/// in [`existing_data_contract_id`](Config::existing_data_contract_id).
pub existing_document_id: Identifier,
}

pub type Config = rs_sdk::mock::config::Config<TestConfig>;

/// Create a mock document type for testing of mock API
pub fn mock_document_type() -> dpp::data_contract::document_type::DocumentType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,29 @@
//!
//! This module contains [Config] struct that can be used to configure rs-sdk.
//! It's mainly used for testing.

use dpp::prelude::Identifier;
use rs_dapi_client::AddressList;
use serde::Deserialize;
use std::{path::PathBuf, str::FromStr};

/// Existing document ID
///
// TODO: this is copy-paste from drive-abci `packages/rs-sdk/tests/fetch/main.rs` where it's private,
// consider defining it in `data-contracts` crate
const DPNS_DASH_TLD_DOCUMENT_ID: [u8; 32] = [
215, 242, 197, 63, 70, 169, 23, 171, 110, 91, 57, 162, 215, 188, 38, 11, 100, 146, 137, 69, 55,
68, 209, 224, 212, 242, 106, 141, 142, 255, 55, 207,
];

#[derive(Debug, Deserialize)]
/// Configuration for rs-sdk.
///
/// Content of this configuration is loaded from environment variables or `${CARGO_MANIFEST_DIR}/.env` file
/// when the [Config::new()] is called.
/// Variable names in the enviroment and `.env` file must be prefixed with [RS_SDK_](Config::CONFIG_PREFIX)
/// and written as SCREAMING_SNAKE_CASE (e.g. `RS_SDK_PLATFORM_HOST`).
pub struct Config<D> {
pub struct Config {
/// Hostname of the Dash Platform node to connect to
pub platform_host: String,
/// Port of the Dash Platform node grpc interface
Expand All @@ -31,22 +42,37 @@ pub struct Config<D> {
#[serde(default = "default_dump_dir")]
pub dump_dir: PathBuf,

/// Custom settings, used as needed by the test
#[serde(flatten)]
pub settings: D,
// IDs of some objects generated by the testnet
/// ID of existing identity.
///
/// Format: Base58
#[serde(default = "Config::default_identity_id")]
pub existing_identity_id: Identifier,
/// ID of existing data contract.
///
/// Format: Base58
#[serde(default = "Config::default_data_contract_id")]
pub existing_data_contract_id: Identifier,
/// Name of document type defined for [`existing_data_contract_id`](Config::existing_data_contract_id).
#[serde(default = "Config::default_document_type_name")]
pub existing_document_type_name: String,
/// ID of document of the type [`existing_document_type_name`](Config::existing_document_type_name)
/// in [`existing_data_contract_id`](Config::existing_data_contract_id).
#[serde(default = "Config::default_document_id")]
pub existing_document_id: Identifier,
}

impl<D: for<'de1> Deserialize<'de1>> Config<D> {
impl Config {
/// Prefix of configuration options in the environment variables and `.env` file.
pub const CONFIG_PREFIX: &str = "RS_SDK_";
/// Load configuration from operating system environment variables and `.env` file.
///
/// Create new [Config] with data from environment variables and `${CARGO_MANIFEST_DIR}/.env` file.
/// Create new [Config] with data from environment variables and `${CARGO_MANIFEST_DIR}/tests/.env` file.
/// Variable names in the environment and `.env` file must be converted to SCREAMING_SNAKE_CASE and
/// prefixed with [RS_SDK_](Config::CONFIG_PREFIX).
pub fn new() -> Self {
// load config from .env file, ignore errors
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/.env";
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/tests/.env";

dotenvy::from_path(path).expect("failed to load config file");

Expand All @@ -72,23 +98,26 @@ impl<D: for<'de1> Deserialize<'de1>> Config<D> {
/// new test vectors during execution
/// * `offline-testing` is set - use mock implementation and
/// load existing test vectors from disk
pub async fn setup_api(&self) -> crate::Sdk {
#[cfg(not(feature = "offline-testing"))]
// Dump all traffic to disk
let sdk = crate::SdkBuilder::new(self.address_list())
.with_core(
pub async fn setup_api(&self) -> rs_sdk::Sdk {
#[cfg(feature = "network-testing")]
let sdk = {
// Dump all traffic to disk
let builder = rs_sdk::SdkBuilder::new(self.address_list()).with_core(
&self.platform_host,
self.core_port,
&self.core_user,
&self.core_password,
)
.with_dump_dir(&self.dump_dir)
.build()
.expect("cannot initialize api");
);

#[cfg(feature = "generate-test-vectors")]
let builder = builder.with_dump_dir(&self.dump_dir);

builder.build().expect("cannot initialize api")
};

#[cfg(feature = "offline-testing")]
let sdk = {
let mut mock_sdk = crate::SdkBuilder::new_mock()
let mut mock_sdk = rs_sdk::SdkBuilder::new_mock()
.build()
.expect("initialize api");

Expand All @@ -104,9 +133,28 @@ impl<D: for<'de1> Deserialize<'de1>> Config<D> {

sdk
}

fn default_identity_id() -> Identifier {
data_contracts::SystemDataContract::DPNS
.source()
.expect("data contract source")
.owner_id_bytes
.into()
}

fn default_data_contract_id() -> Identifier {
data_contracts::SystemDataContract::DPNS.id()
}

fn default_document_type_name() -> String {
"domain".to_string()
}
fn default_document_id() -> Identifier {
DPNS_DASH_TLD_DOCUMENT_ID.into()
}
}

impl<D: for<'de1> Deserialize<'de1>> Default for Config<D> {
impl Default for Config {
fn default() -> Self {
Self::new()
}
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/tests/fetch/data_contract.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::common::Config;
use dpp::prelude::{DataContract, Identifier};
use rs_sdk::platform::Fetch;
use crate::config::Config;

/// Given some dummy data contract ID, when I fetch data contract, I get None because it doesn't exist.
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
Expand All @@ -21,7 +21,7 @@ async fn test_data_contract_read_not_found() {
async fn test_data_contract_read() {
use dpp::data_contract::accessors::v0::DataContractV0Getters;
let cfg = Config::new();
let id = cfg.settings.existing_data_contract_id;
let id = cfg.existing_data_contract_id;

let mut api = cfg.setup_api().await;

Expand Down
Loading
Loading