Skip to content

Commit 0def7e8

Browse files
Move the create-and-call test case from end-to-end to integration test. (#4394)
## Motivation The end-to-end tests are relatively expensive in CI. So, it is advantageous to move them to integration tests. ## Proposal We do the following: * We remove the `&self` of `ActiveChain` that is not needed. * We make the function `ActiveChain` public to be used in the operational test. * We introduce the function to return `Bytecode` since to instantiate, we cannot use the compressed bytecode. * We introduce the test that exercises the `create-and-call` smart contract. ## Test Plan The CI. The PR that introduces the `create-and-call` also addresses the persistence of the contract and service blobs that have been created. We tested that this integration test fails if the correction is removed. ## Release Plan - Nothing to do / These changes follow the usual release cycle. ## Links None.
1 parent a0bdf11 commit 0def7e8

File tree

8 files changed

+78
-102
lines changed

8 files changed

+78
-102
lines changed

Cargo.lock

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ amm = { path = "./examples/amm" }
305305
call-evm-counter = { path = "./examples/call-evm-counter" }
306306
counter = { path = "./examples/counter" }
307307
counter-no-graphql = { path = "./examples/counter-no-graphql" }
308-
create-and-call = { path = "./examples/create-and-call" }
309308
crowd-funding = { path = "./examples/crowd-funding" }
310309
ethereum-tracker = { path = "./examples/ethereum-tracker" }
311310
fungible = { path = "./examples/fungible" }

examples/Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/create-and-call/Cargo.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ serde.workspace = true
1111

1212
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
1313
linera-sdk = { workspace = true, features = ["test", "wasmer"] }
14-
tokio = { workspace = true, features = ["rt", "sync"] }
15-
16-
[dev-dependencies]
17-
assert_matches.workspace = true
18-
linera-sdk = { workspace = true, features = ["test"] }
14+
tokio.workspace = true
1915

2016
[[bin]]
2117
name = "create_and_call_contract"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Zefchain Labs, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Integration tests for the Create and Call application.
5+
6+
#![cfg(not(target_arch = "wasm32"))]
7+
8+
use linera_sdk::test::{ActiveChain, TestValidator};
9+
10+
/// Test creating and calling a counter application dynamically.
11+
///
12+
/// This test publishes the counter-no-graphql bytecode, then uses the create-and-call
13+
/// application to dynamically create an instance of the counter and increment it.
14+
/// The test verifies that the counter is correctly initialized and incremented.
15+
#[tokio::test(flavor = "multi_thread")]
16+
async fn test_create_and_call() {
17+
let (validator, create_call_module_id) =
18+
TestValidator::with_current_module::<create_and_call::CreateAndCallAbi, (), ()>().await;
19+
let mut chain = validator.new_chain().await;
20+
21+
// Step 1: Get the bytecode for "counter-no-graphql" by compiling it from its directory
22+
let counter_no_graphql_path = std::path::Path::new("../counter-no-graphql");
23+
let counter_no_graphql_path = std::fs::canonicalize(counter_no_graphql_path)
24+
.expect("Failed to get absolute path to counter-no-graphql");
25+
26+
// Build and find the counter-no-graphql bytecode files
27+
ActiveChain::build_bytecode_files_in(&counter_no_graphql_path).await;
28+
let (counter_contract, counter_service) =
29+
ActiveChain::find_bytecode_files_in(&counter_no_graphql_path).await;
30+
31+
// Extract the raw bytes from the bytecode
32+
let counter_contract_bytes = counter_contract.bytes.to_vec();
33+
let counter_service_bytes = counter_service.bytes.to_vec();
34+
35+
// Step 2: Create the "create-and-call" application
36+
let application_id = chain
37+
.create_application(create_call_module_id, (), (), vec![])
38+
.await;
39+
40+
// Step 3: Call the CreateAndCall operation with the counter bytecode,
41+
// initialization value of 43, and increment of 5
42+
let initialization_value = 43;
43+
let increment_value = 5;
44+
let create_and_call_operation = create_and_call::CreateAndCallOperation::CreateAndCall(
45+
counter_contract_bytes,
46+
counter_service_bytes,
47+
initialization_value,
48+
increment_value,
49+
);
50+
51+
chain
52+
.add_block(|block| {
53+
block.with_operation(application_id, create_and_call_operation);
54+
})
55+
.await;
56+
57+
// Step 4: Query the create-and-call application to get the result (should be 48 = 43 + 5)
58+
let query_request = create_and_call::CreateAndCallRequest::Query;
59+
let outcome = chain.query(application_id, query_request).await;
60+
61+
let expected_value = 48; // 43 + 5
62+
assert_eq!(outcome.response, expected_value);
63+
}

linera-sdk/src/test/chain.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ impl ActiveChain {
386386
.await
387387
.expect("Failed to obtain absolute application repository path");
388388
Self::build_bytecode_files_in(&repository_path).await;
389-
let (contract, service) = self.find_bytecode_files_in(&repository_path).await;
389+
let (contract, service) = Self::find_compressed_bytecode_files_in(&repository_path).await;
390390
let contract_blob = Blob::new_contract_bytecode(contract);
391391
let service_blob = Blob::new_service_bytecode(service);
392392
let contract_blob_hash = contract_blob.id().hash;
@@ -412,7 +412,7 @@ impl ActiveChain {
412412
}
413413

414414
/// Compiles the crate in the `repository` path.
415-
async fn build_bytecode_files_in(repository: &Path) {
415+
pub async fn build_bytecode_files_in(repository: &Path) {
416416
let output = std::process::Command::new("cargo")
417417
.args(["build", "--release", "--target", "wasm32-unknown-unknown"])
418418
.current_dir(repository)
@@ -431,12 +431,9 @@ impl ActiveChain {
431431
/// Searches the Cargo manifest of the crate calling this method for binaries to use as the
432432
/// contract and service bytecode files.
433433
///
434-
/// Returns a tuple with the loaded contract and service [`CompressedBytecode`]s,
434+
/// Returns a tuple with the loaded contract and service [`Bytecode`]s,
435435
/// ready to be published.
436-
async fn find_bytecode_files_in(
437-
&self,
438-
repository: &Path,
439-
) -> (CompressedBytecode, CompressedBytecode) {
436+
pub async fn find_bytecode_files_in(repository: &Path) -> (Bytecode, Bytecode) {
440437
let manifest_path = repository.join("Cargo.toml");
441438
let cargo_manifest =
442439
Manifest::from_path(manifest_path).expect("Failed to load Cargo.toml manifest");
@@ -461,8 +458,7 @@ impl ActiveChain {
461458
(&binaries[1], &binaries[0])
462459
};
463460

464-
let base_path = self
465-
.find_output_directory_of(repository)
461+
let base_path = Self::find_output_directory_of(repository)
466462
.await
467463
.expect("Failed to look for output binaries");
468464
let contract_path = base_path.join(format!("{}.wasm", contract_binary));
@@ -474,7 +470,15 @@ impl ActiveChain {
474470
let service = Bytecode::load_from_file(service_path)
475471
.await
476472
.expect("Failed to load service bytecode from file");
473+
(contract, service)
474+
}
477475

476+
/// Returns a tuple with the loaded contract and service [`CompressedBytecode`]s,
477+
/// ready to be published.
478+
pub async fn find_compressed_bytecode_files_in(
479+
repository: &Path,
480+
) -> (CompressedBytecode, CompressedBytecode) {
481+
let (contract, service) = Self::find_bytecode_files_in(repository).await;
478482
tokio::task::spawn_blocking(move || (contract.compress(), service.compress()))
479483
.await
480484
.expect("Failed to compress bytecode files")
@@ -486,7 +490,7 @@ impl ActiveChain {
486490
/// `target/wasm32-unknown-unknown/release` sub-directory. However, since the crate with the
487491
/// binaries could be part of a workspace, that output sub-directory must be searched in parent
488492
/// directories as well.
489-
async fn find_output_directory_of(&self, repository: &Path) -> Result<PathBuf, io::Error> {
493+
async fn find_output_directory_of(repository: &Path) -> Result<PathBuf, io::Error> {
490494
let output_sub_directory = Path::new("target/wasm32-unknown-unknown/release");
491495
let mut current_directory = repository;
492496
let mut output_path = current_directory.join(output_sub_directory);

linera-service/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ base64.workspace = true
134134
call-evm-counter.workspace = true
135135
counter.workspace = true
136136
counter-no-graphql.workspace = true
137-
create-and-call.workspace = true
138137
criterion = { workspace = true, features = ["async_tokio"] }
139138
crowd-funding.workspace = true
140139
ethereum-tracker.workspace = true

linera-service/tests/linera_net_tests.rs

Lines changed: 0 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,80 +1736,6 @@ async fn test_wasm_end_to_end_counter_no_graphql(config: impl LineraNetConfig) -
17361736
Ok(())
17371737
}
17381738

1739-
#[cfg_attr(feature = "storage-service", test_case(LocalNetConfig::new_test(Database::Service, Network::Grpc) ; "storage_test_service_grpc"))]
1740-
#[cfg_attr(feature = "scylladb", test_case(LocalNetConfig::new_test(Database::ScyllaDb, Network::Grpc) ; "scylladb_grpc"))]
1741-
#[cfg_attr(all(feature = "rocksdb", feature = "scylladb"), test_case(LocalNetConfig::new_test(Database::DualRocksDbScyllaDb, Network::Grpc) ; "dualrocksdbscylladb_grpc"))]
1742-
#[cfg_attr(feature = "dynamodb", test_case(LocalNetConfig::new_test(Database::DynamoDb, Network::Grpc) ; "aws_grpc"))]
1743-
#[cfg_attr(feature = "kubernetes", test_case(SharedLocalKubernetesNetTestingConfig::new(Network::Grpc, BuildArg::Build) ; "kubernetes_grpc"))]
1744-
#[cfg_attr(feature = "remote-net", test_case(RemoteNetTestingConfig::new(None) ; "remote_net_grpc"))]
1745-
#[test_log::test(tokio::test)]
1746-
async fn test_create_and_call_end_to_end(config: impl LineraNetConfig) -> Result<()> {
1747-
use create_and_call::{CreateAndCallAbi, CreateAndCallRequest};
1748-
use linera_base::data_types::Bytecode;
1749-
1750-
let _guard = INTEGRATION_TEST_GUARD.lock().await;
1751-
tracing::info!("Starting test {}", test_name!());
1752-
1753-
let (mut net, client) = config.instantiate().await?;
1754-
1755-
// Step 1: Download the contract and service of "counter-no-graphql" as Vec<u8>
1756-
let (counter_contract_path, counter_service_path) =
1757-
client.build_example("counter-no-graphql").await?;
1758-
let counter_contract_bytecode = Bytecode::load_from_file(&counter_contract_path).await?;
1759-
let counter_service_bytecode = Bytecode::load_from_file(&counter_service_path).await?;
1760-
let contract_bytes = counter_contract_bytecode.bytes;
1761-
let service_bytes = counter_service_bytecode.bytes;
1762-
1763-
// Step 2: Instantiate the contract "create-and-call"
1764-
let chain = client.load_wallet()?.default_chain().unwrap();
1765-
let (create_call_contract, create_call_service) =
1766-
client.build_example("create-and-call").await?;
1767-
1768-
let application_id = client
1769-
.publish_and_create::<CreateAndCallAbi, (), ()>(
1770-
create_call_contract,
1771-
create_call_service,
1772-
VmRuntime::Wasm,
1773-
&(),
1774-
&(), // Initial value
1775-
&[],
1776-
None,
1777-
)
1778-
.await?;
1779-
1780-
let port = get_node_port().await;
1781-
let mut node_service = client.run_node_service(port, ProcessInbox::Skip).await?;
1782-
1783-
let application = node_service
1784-
.make_application(&chain, &application_id)
1785-
.await?;
1786-
1787-
// Step 3: Call a mutation that takes the Vec<u8> of "contract", "service",
1788-
// the initialization value of 43 and the increment of 5
1789-
let initialization_value = 43;
1790-
let increment_value = 5;
1791-
1792-
let mutation_request = CreateAndCallRequest::CreateAndCall(
1793-
contract_bytes,
1794-
service_bytes,
1795-
initialization_value,
1796-
increment_value,
1797-
);
1798-
1799-
application.run_json_query(&mutation_request).await?;
1800-
1801-
// Step 4: Query the contract and see if we obtain 48 (43 + 5)
1802-
let query_request = CreateAndCallRequest::Query;
1803-
let result_value = application.run_json_query(&query_request).await?;
1804-
assert_eq!(result_value, 48);
1805-
1806-
node_service.ensure_is_running()?;
1807-
net.ensure_is_running().await?;
1808-
net.terminate().await?;
1809-
1810-
Ok(())
1811-
}
1812-
18131739
#[cfg_attr(feature = "storage-service", test_case(LocalNetConfig::new_test(Database::Service, Network::Grpc) ; "storage_test_service_grpc"))]
18141740
#[cfg_attr(feature = "scylladb", test_case(LocalNetConfig::new_test(Database::ScyllaDb, Network::Grpc) ; "scylladb_grpc"))]
18151741
#[cfg_attr(feature = "dynamodb", test_case(LocalNetConfig::new_test(Database::DynamoDb, Network::Grpc) ; "aws_grpc"))]

0 commit comments

Comments
 (0)