Skip to content

Commit 349ab68

Browse files
Add cloud engine test
1 parent 26f31b6 commit 349ab68

File tree

5 files changed

+192
-11
lines changed

5 files changed

+192
-11
lines changed

crates/icp-cli/src/operations/create.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,7 @@ impl CreateOperation {
107107
.get_subnet_by_id(&selected_subnet)
108108
.await
109109
.context(GetSubnetSnafu)?;
110-
let cid = if let Some(SubnetType::Unknown(kind)) = subnet_info.subnet_type()
111-
&& kind == "cloud_engine"
112-
{
110+
let cid = if let Some(SubnetType::CloudEngine) = subnet_info.subnet_type() {
113111
self.create_mgmt(settings, &subnet_info).await?
114112
} else {
115113
self.create_ledger(settings, selected_subnet).await?

crates/icp-cli/tests/canister_create_tests.rs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ use predicates::{
44
prelude::PredicateBooleanExt,
55
str::{contains, starts_with},
66
};
7+
use test_tag::tag;
78

8-
use crate::common::{ENVIRONMENT_RANDOM_PORT, NETWORK_RANDOM_PORT, TestContext, clients};
9+
use crate::common::{
10+
ENVIRONMENT_DOCKER_ENGINE, ENVIRONMENT_RANDOM_PORT, NETWORK_DOCKER_ENGINE, NETWORK_RANDOM_PORT,
11+
TestContext, clients,
12+
};
913
use icp::{fs::write_string, prelude::*};
1014

1115
mod common;
@@ -537,3 +541,76 @@ async fn canister_create_detached() {
537541
.assert()
538542
.failure();
539543
}
544+
545+
#[tag(docker)]
546+
#[tokio::test]
547+
async fn canister_create_cloud_engine() {
548+
let ctx = TestContext::new();
549+
550+
let project_dir = ctx.create_project_dir("icp");
551+
552+
let pm = formatdoc! {r#"
553+
canisters:
554+
- name: my-canister
555+
build:
556+
steps:
557+
- type: script
558+
command: echo hi
559+
560+
{NETWORK_DOCKER_ENGINE}
561+
{ENVIRONMENT_DOCKER_ENGINE}
562+
"#};
563+
564+
write_string(&project_dir.join("icp.yaml"), &pm).expect("failed to write project manifest");
565+
566+
ctx.docker_pull_engine_network();
567+
let _guard = ctx
568+
.start_network_in(&project_dir, "docker-engine-network")
569+
.await;
570+
ctx.ping_until_healthy(&project_dir, "docker-engine-network");
571+
572+
// Find the CloudEngine subnet by querying the topology endpoint
573+
// TODO replace with a subnet selection parameter once we have one
574+
let topology_url = ctx.gateway_url().join("/_/topology").unwrap();
575+
let topology: serde_json::Value = reqwest::get(topology_url)
576+
.await
577+
.expect("failed to fetch topology")
578+
.json()
579+
.await
580+
.expect("failed to parse topology");
581+
582+
let subnet_configs = topology["subnet_configs"]
583+
.as_object()
584+
.expect("subnet_configs should be an object");
585+
let cloud_engine_subnet_id = subnet_configs
586+
.iter()
587+
.find_map(|(id, config)| {
588+
(config["subnet_kind"].as_str()? == "CloudEngine").then_some(id.clone())
589+
})
590+
.expect("no CloudEngine subnet found in topology");
591+
592+
// Create the canister on the CloudEngine subnet
593+
ctx.icp()
594+
.current_dir(&project_dir)
595+
.args([
596+
"canister",
597+
"create",
598+
"my-canister",
599+
"--subnet",
600+
&cloud_engine_subnet_id,
601+
"--environment",
602+
"docker-engine-environment",
603+
])
604+
.assert()
605+
.success();
606+
607+
let id_mapping_path = project_dir
608+
.join(".icp")
609+
.join("cache")
610+
.join("mappings")
611+
.join("docker-engine-environment.ids.json");
612+
assert!(
613+
id_mapping_path.exists(),
614+
"ID mapping file should exist at {id_mapping_path}"
615+
);
616+
}

crates/icp-cli/tests/common/context.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,10 @@ impl TestContext {
395395
.expect("Failed to write network descriptor file");
396396
}
397397

398+
pub(crate) fn gateway_url(&self) -> &Url {
399+
self.http_gateway_url.get().unwrap()
400+
}
401+
398402
pub(crate) fn agent(&self) -> Agent {
399403
let agent = Agent::builder()
400404
.with_url(self.http_gateway_url.get().unwrap().as_str())
@@ -441,18 +445,21 @@ impl TestContext {
441445
}
442446

443447
pub(crate) fn docker_pull_network(&self) {
448+
self.docker_pull_image("ghcr.io/dfinity/icp-cli-network-launcher:v11.0.0");
449+
}
450+
451+
pub(crate) fn docker_pull_engine_network(&self) {
452+
self.docker_pull_image("ghcr.io/dfinity/icp-cli-network-launcher:engine-beta");
453+
}
454+
455+
fn docker_pull_image(&self, image: &str) {
444456
let platform = if cfg!(target_arch = "aarch64") {
445457
"linux/arm64"
446458
} else {
447459
"linux/amd64"
448460
};
449461
Command::new("docker")
450-
.args([
451-
"pull",
452-
"--platform",
453-
platform,
454-
"ghcr.io/dfinity/icp-cli-network-launcher:v11.0.0",
455-
])
462+
.args(["pull", "--platform", platform, image])
456463
.assert()
457464
.success();
458465
}

crates/icp-cli/tests/common/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ environments:
4949
network: docker-network
5050
"#;
5151

52+
pub(crate) const NETWORK_DOCKER_ENGINE: &str = r#"
53+
networks:
54+
- name: docker-engine-network
55+
mode: managed
56+
image: ghcr.io/dfinity/icp-cli-network-launcher:engine-beta
57+
port-mapping:
58+
- 0:4943
59+
- 0:4942
60+
"#;
61+
62+
pub(crate) const ENVIRONMENT_DOCKER_ENGINE: &str = r#"
63+
environments:
64+
- name: docker-engine-environment
65+
network: docker-engine-network
66+
"#;
67+
5268
/// This ID is dependent on the toplogy being served by pocket-ic
5369
/// NOTE: If the topology is changed (another subnet is added, etc) the ID may change.
5470
/// References:

crates/icp-cli/tests/deploy_tests.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ use predicates::{
33
ord::eq,
44
str::{PredicateStrExt, contains},
55
};
6+
use test_tag::tag;
67

7-
use crate::common::{ENVIRONMENT_RANDOM_PORT, NETWORK_RANDOM_PORT, TestContext, clients};
8+
use crate::common::{
9+
ENVIRONMENT_DOCKER_ENGINE, ENVIRONMENT_RANDOM_PORT, NETWORK_DOCKER_ENGINE, NETWORK_RANDOM_PORT,
10+
TestContext, clients,
11+
};
812
use icp::{
913
fs::{create_dir_all, write_string},
1014
prelude::*,
@@ -599,3 +603,82 @@ async fn deploy_upgrade_rejects_incompatible_candid() {
599603
.success()
600604
.stdout(eq("(\"Hello, 42!\")").trim());
601605
}
606+
607+
#[tag(docker)]
608+
#[tokio::test]
609+
async fn deploy_cloud_engine() {
610+
let ctx = TestContext::new();
611+
612+
let project_dir = ctx.create_project_dir("icp");
613+
614+
let wasm = ctx.make_asset("example_icp_mo.wasm");
615+
616+
let pm = formatdoc! {r#"
617+
canisters:
618+
- name: my-canister
619+
build:
620+
steps:
621+
- type: script
622+
command: cp '{wasm}' "$ICP_WASM_OUTPUT_PATH"
623+
624+
{NETWORK_DOCKER_ENGINE}
625+
{ENVIRONMENT_DOCKER_ENGINE}
626+
"#};
627+
628+
write_string(&project_dir.join("icp.yaml"), &pm).expect("failed to write project manifest");
629+
630+
ctx.docker_pull_engine_network();
631+
let _guard = ctx
632+
.start_network_in(&project_dir, "docker-engine-network")
633+
.await;
634+
ctx.ping_until_healthy(&project_dir, "docker-engine-network");
635+
636+
// Find the CloudEngine subnet by querying the topology endpoint
637+
// TODO replace with a subnet selection parameter once we have one
638+
let topology_url = ctx.gateway_url().join("/_/topology").unwrap();
639+
let topology: serde_json::Value = reqwest::get(topology_url)
640+
.await
641+
.expect("failed to fetch topology")
642+
.json()
643+
.await
644+
.expect("failed to parse topology");
645+
646+
let subnet_configs = topology["subnet_configs"]
647+
.as_object()
648+
.expect("subnet_configs should be an object");
649+
let cloud_engine_subnet_id = subnet_configs
650+
.iter()
651+
.find_map(|(id, config)| {
652+
(config["subnet_kind"].as_str()? == "CloudEngine").then_some(id.clone())
653+
})
654+
.expect("no CloudEngine subnet found in topology");
655+
656+
// Deploy to the CloudEngine subnet
657+
ctx.icp()
658+
.current_dir(&project_dir)
659+
.args([
660+
"deploy",
661+
"--subnet",
662+
&cloud_engine_subnet_id,
663+
"--environment",
664+
"docker-engine-environment",
665+
])
666+
.assert()
667+
.success();
668+
669+
// Query canister to verify it works
670+
ctx.icp()
671+
.current_dir(&project_dir)
672+
.args([
673+
"canister",
674+
"call",
675+
"--environment",
676+
"docker-engine-environment",
677+
"my-canister",
678+
"greet",
679+
"(\"test\")",
680+
])
681+
.assert()
682+
.success()
683+
.stdout(eq("(\"Hello, test!\")").trim());
684+
}

0 commit comments

Comments
 (0)