[][src]Crate exonum_testkit

Testkit for Exonum blockchain framework, allowing to test service APIs synchronously and in the same process as the testkit.

Example

use exonum::{
    runtime::{BlockchainData, SnapshotExt, rust::{ServiceFactory, Transaction, CallContext, Service}},
    blockchain::{Block, Schema, ExecutionError, InstanceCollection},
    crypto::{gen_keypair, Hash},
    explorer::TransactionInfo,
    helpers::Height,
    api::node::public::explorer::{BlocksQuery, BlocksRange, TransactionQuery},
};
use serde_derive::{Serialize, Deserialize};
use exonum_derive::{exonum_interface, ServiceFactory, ServiceDispatcher, BinaryValue};
use exonum_proto::ProtobufConvert;
use exonum_merkledb::{ObjectHash, Snapshot};
use exonum_testkit::{txvec, ApiKind, TestKitBuilder};

// Simple service implementation.

const SERVICE_ID: u32 = 1;

#[derive(Debug, Clone, Serialize, Deserialize, ProtobufConvert, BinaryValue)]
#[protobuf_convert(source = "exonum_testkit::proto::examples::TxTimestamp")]
pub struct TxTimestamp {
    message: String,
}

#[derive(Clone, Default, Debug, ServiceFactory, ServiceDispatcher)]
#[service_dispatcher(implements("TimestampingInterface"))]
#[service_factory(
    artifact_name = "timestamping",
    artifact_version = "1.0.0",
    proto_sources = "exonum_testkit::proto",
)]
struct TimestampingService;

impl Service for TimestampingService {
    fn state_hash(&self, _: BlockchainData<&dyn Snapshot>) -> Vec<Hash> { vec![] }
}

#[exonum_interface]
pub trait TimestampingInterface {
    fn timestamp(&self, _: CallContext<'_>, arg: TxTimestamp) -> Result<(), ExecutionError>;
}

impl TimestampingInterface for TimestampingService {
    fn timestamp(&self, _: CallContext<'_>, arg: TxTimestamp) -> Result<(), ExecutionError> {
        Ok(())
    }
}

fn main() {
    // Create testkit for network with four validators
    // and add a builtin timestamping service with ID=1.
    let service = TimestampingService;
    let artifact = service.artifact_id();
    let mut testkit = TestKitBuilder::validator()
        .with_validators(4)
        .with_artifact(artifact.clone())
        .with_instance(artifact.into_default_instance(SERVICE_ID, "timestamping"))
        .with_rust_service(service)
        .create();

    // Create few transactions.
    let keys = gen_keypair();
    let id = SERVICE_ID;
    let tx1 = TxTimestamp { message: "Down To Earth".into() }.sign(id, keys.0, &keys.1);
    let tx2 = TxTimestamp { message: "Cry Over Spilt Milk".into() }.sign(id, keys.0, &keys.1);
    let tx3 = TxTimestamp { message: "Dropping Like Flies".into() }.sign(id, keys.0, &keys.1);
    // Commit them into blockchain.
    testkit.create_block_with_transactions(txvec![
        tx1.clone(), tx2.clone(), tx3.clone()
    ]);

    // Add a single transaction.
    let tx4 = TxTimestamp { message: "Barking up the wrong tree".into() }.sign(id, keys.0, &keys.1);
    testkit.create_block_with_transaction(tx4.clone());

    // Check results with schema.
    let snapshot = testkit.snapshot();
    let schema = snapshot.for_core();
    assert!(schema.transactions().contains(&tx1.object_hash()));
    assert!(schema.transactions().contains(&tx2.object_hash()));
    assert!(schema.transactions().contains(&tx3.object_hash()));
    assert!(schema.transactions().contains(&tx4.object_hash()));

    // Check results with api.
    let api = testkit.api();
    let explorer_api = api.public(ApiKind::Explorer);
    let response: BlocksRange = explorer_api
        .query(&BlocksQuery {
            count: 10,
            ..Default::default()
        })
        .get("v1/blocks")
        .unwrap();
    let (blocks, range) = (response.blocks, response.range);
    assert_eq!(blocks.len(), 3);
    assert_eq!(range.start, Height(0));
    assert_eq!(range.end, Height(3));

    let info = explorer_api
        .query(&TransactionQuery::new(tx1.object_hash()))
        .get::<TransactionInfo>("v1/transactions")
        .unwrap();
}

Re-exports

pub use crate::compare::ComparableSnapshot;

Modules

compare

Routines for comparison between 2 states.

proto

Module of the rust-protobuf generated files.

Macros

txvec

Creates a Vec<Box<Transaction>> from the given transactions, or other objects implementing the Into<Box<Transaction>> trait.

Structs

InstanceCollection

Rust runtime artifact with the list of instances.

StoppedTestKit

Persistent state of an Exonum node allowing to emulate node restart.

TestKit

Testkit for testing blockchain services. It offers simple network configuration emulation (with no real network setup).

TestKitApi

API encapsulation for the testkit. Allows to execute and synchronously retrieve results for REST-ful endpoints of services.

TestKitBuilder

Builder for TestKit.

TestKitStatus

Testkit status, returned by the corresponding API endpoint.

TestNetwork

Emulated test network.

TestNode

An emulated node in the test network.

Enums

ApiKind

Kind of public or private REST API of an Exonum node.