diff --git a/CHANGELOG.md b/CHANGELOG.md index a9a15c9a82..065c77b180 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,30 @@ # UNRELEASED +### feat: Added support for reserved_cycles and reserved_cycles_limit + +`dfx canister status` will now display the reserved cycles balance and reserved cycles limit for a canister. + +Added command-line options: + - `dfx canister create --reserved-cycles-limit ` + - `dfx canister update-settings --reserved-cycles-limit ` + +In addition, `dfx deploy` will set `reserved_cycles_limit` when creating canisters if specified in `canisters..initialization_values.reserved_cycles_limit` in dfx.json. + +### feat: emit management canister idl when imported by Motoko canister + +`import management "ic:aaaaa-aa;` + +This will automatically produce the idl in the `.dfx` folder. + ### fix: Include remote canisters in canisters_to_generate Generate frontend declarations for remote canisters too because frontend JS code may want to call them. +### feat: dfx extension install --version + +Install a specific version of an extension, bypassing version checks. + ### feat: Updated handling of missing values in state tree certificates The `Unknown` lookup of a path in a certificate results in an `AgentError` (the IC returns `Absent` for non-existing paths). @@ -57,8 +77,34 @@ Background: In order to determine whether to start a project-specific network or If `dfx start` is starting the shared network from within a dfx project, and that dfx.json contains settings in the `defaults` key for `bitcoin`, `replica`, or `canister_http`, then `dfx start` will warn that it is ignoring those settings. It will also describe how to define equivalent settings in networks.json. +### fix: dfx canister call --wallet no longer passes the parameter twice + +The parameter was erroneously passed twice. Now it is passed only once. + +### fix: Removed deprecation warning about project-specific networks + +Removed this warning: "Project-specific networks are deprecated and will be removed after February 2023." While we may remove project-specific networks in the future, it is not imminent. One key requirement is the ability to run more than one subnet type at one time. + ## Dependencies +### icx-proxy + +Updated to a version of the icx-proxy that is released with the replica and other related binaries. + +Changes in behavior: +- "%%" is no longer accepted when url-decoding filenames for the asset canister. Though curl supports this, it's not part of the standard. Please replace with %25. +- The icx-proxy now performs response verification. This has exposed some bugs in the asset canister. However, since this new icx-proxy matches what the boundary nodes use, this will better match the behavior seen on the mainnet. +- Bugs that this has exposed in the asset canister: + - after disabling aliasing for an asset, the asset canister will return an incorrect certification in the 404 response. + - after setting a custom "etag" header in .ic-assets.json, the asset canister will return an incorrect certification in the 200 response. + - assets with certain characters in the filename (example: "æ") will no longer be served correctly. The definition of "certain characters" is not yet known. + +### Candid UI + +- Module hash: 934756863c010898a24345ce4842d173b3ea7639a8eb394a0d027a9423c70b5c +- Add `merge_init_args` method in Candid UI. +- Draw flamegraph for canister upgrade. + ### Frontend canister For certification v1, if none of the requested encoding are certified but another encoding is certified, then the frontend canister once again returns the certificatie even though the response hash won't match. @@ -66,10 +112,21 @@ This allows the verifying side to try to transform the response such that it mat For example, if only the encoding `gzip` is requested but the `identity` encoding is certified, the `gzip` encoding is returned with the certificate for the `identity` encoding. The verifying side can then unzip the response and will have a valid certificate for the `identity` response. -- Module hash: cd3e7fa2b826f84cdd107eef28633b0c669b4687ae1598dd854828e82d2e4652 +- Module hash: baf9bcab2ebc2883f850b965af658e66725087933df012ebd35c03929c39efe3 +- https://github.com/dfinity/sdk/pull/3369 - https://github.com/dfinity/sdk/pull/3298 - https://github.com/dfinity/sdk/pull/3281 +Updated replica to elected commit 91bf38ff3cb927cb94027d9da513cd15f91a5b04. +This incorporates the following executed proposals: + +- [124795](https://dashboard.internetcomputer.org/proposal/124795) +- [124790](https://dashboard.internetcomputer.org/proposal/124790) +- [124538](https://dashboard.internetcomputer.org/proposal/124538) +- [124537](https://dashboard.internetcomputer.org/proposal/124537) +- [124488](https://dashboard.internetcomputer.org/proposal/124488) +- [124487](https://dashboard.internetcomputer.org/proposal/124487) + # 0.15.0 ## DFX @@ -80,6 +137,18 @@ The `use-old-metering` flag enables old metering in replica. The new metering is The flag is temporary and will be removed in a few months. +### fix: added https://icp-api.io to the default Content-Security-Policy header + +Existing projects will need to change this value in .ic-assets.json or .ic-assets.json5 to include https://icp-api.io + +All projects will need to redeploy. + +### fix: access to raw assets is now enabled by default + +The default value for `allow_raw_access` is now `true`. This means that by default, the frontend canister will no longer restrict the access of traffic to the `.raw.icp0.io` domain, and will no longer automatically redirect all requests to the certified domain (`.icp0.io`), unless configured explicitly. + +Note that existing projects that specify `"allow_raw_access": false` in .ic-assets.json5 will need to change or remove this value manually in order to allow raw access. + ### feat!: Removed dfx nns and dfx sns commands Both have now been turned into the dfx extensions. In order to obtain them, please run `dfx extension install nns` and `dfx extension install sns` respectively. After the installation, you can use them as you did before: `dfx nns ...`, and `dfx sns ...`. @@ -166,7 +235,9 @@ Updated Motoko to [0.9.7](https://github.com/dfinity/motoko/releases/tag/0.9.7) ### Frontend canister -- Module hash: 88d1e5795d29debc1ff56fa0696dcb3adfa67f82fe2739d1aa644263838174b9 +- Module hash: e20be8df2c392937a6ae0f70d20ff23b75e8c71d9085a8b8bb438b8c2d4eafe5 +- https://github.com/dfinity/sdk/pull/3337 +- https://github.com/dfinity/sdk/pull/3298 - https://github.com/dfinity/sdk/pull/3256 - https://github.com/dfinity/sdk/pull/3252 - https://github.com/dfinity/sdk/pull/3249 @@ -275,7 +346,7 @@ Previously, it would only retry when waiting for the request to complete. ### fix: now considers fewer error types to be retryable -Previously, errors were assumed to be retryable, except for a few specific error messages and 403/unauthorized responses. This could cause deployment to appear to hang until timeout. +Previously, errors were assumed to be retryable, except for a few specific error messages and 403/unauthorized responses. This could cause deployment to appear to hang until timeout. Now, only transport errors and timeout errors are considered retryable. @@ -330,7 +401,7 @@ When running `dfx canister delete` on a canister that has not been stopped, dfx ### feat: gzip option in dfx.json -`dfx` can gzip wasm module as the final step in building canisters. +`dfx` can gzip wasm module as the final step in building canisters. This behavior is disabled by default. @@ -420,7 +491,7 @@ Previously, it would only retry when waiting for the request to complete. ### fix: now considers fewer error types to be retryable -Previously, errors were assumed to be retryable, except for a few specific error messages and 403/unauthorized responses. This could cause deployment to appear to hang until timeout. +Previously, errors were assumed to be retryable, except for a few specific error messages and 403/unauthorized responses. This could cause deployment to appear to hang until timeout. Now, only transport errors and timeout errors are considered retryable. @@ -519,12 +590,12 @@ When creating a canister on non-mainnet replica, you can now specify the caniste `dfx deploy --specified-id ` You can specify the ID in the range of `[0, u64::MAX / 2]`. -If not specify the ID, the canister will be created in the range of `[u64::MAX / 2 + 1, u64::MAX]`. +If not specify the ID, the canister will be created in the range of `[u64::MAX / 2 + 1, u64::MAX]`. This canister ID allocation behavior only applies to the replica, not the emulator (ic-ref). ### feat: dfx nns install --ledger-accounts -`dfx nns install` now takes an option `--ledger-accounts` to initialize the ledger canister with these accounts. +`dfx nns install` now takes an option `--ledger-accounts` to initialize the ledger canister with these accounts. ### fix: update Rust canister template. @@ -532,7 +603,7 @@ This canister ID allocation behavior only applies to the replica, not the emulat ### chore: change the default Internet Computer gateway domain to `icp0.io` -By default, DFX now uses the `icp0.io` domain to connect to Internet Computer as opposed to using `ic0.app`. +By default, DFX now uses the `icp0.io` domain to connect to Internet Computer as opposed to using `ic0.app`. Canisters communicating with `ic0.app` will continue to function nominally. ### feat: --no-asset-upgrade @@ -711,7 +782,7 @@ This is no longer the case. See rules above for grant_permission and revoke_per ### feat(frontend-canister)!: default secure configuration for assets in frontend project template -- Secure HTTP headers, preventing several typical security vulnerabilities (e.g. XSS, clickjacking, and many more). For more details, see comments in `headers` section in [default `.ic-assets.json5`](https://raw.githubusercontent.com/dfinity/sdk/master/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5). +- Secure HTTP headers, preventing several typical security vulnerabilities (e.g. XSS, clickjacking, and many more). For more details, see comments in `headers` section in [default `.ic-assets.json5`](https://raw.githubusercontent.com/dfinity/sdk/master/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5). - Configures `allow_raw_access` option in starter `.ic-assets.json5` config files, with the value set to its default value (which is `false`). We are showing that configuration in the default starter projects for the sake of easier discoverability, even though its value is set to the default. ### feat(frontend-canister)!: add `allow_raw_access` config option @@ -729,12 +800,12 @@ By default, the frontend canister will now restrict the access of traffic to the **Important**: Note that any assets already uploaded to an asset canister will be protected by this redirection, because at present the asset synchronization process does not update the `allow_raw_access` property, or any other properties, after creating an asset. This also applies to assets that are deployed without any configuration, and later configured to allow raw access. At the present time, there are two ways to reconfigure an existing asset: 1. re-create the asset - 1. delete the asset in your project's directory + 1. delete the asset in your project's directory 1. execute `dfx deploy` 1. re-create the asset in your project's directory - 1. modify `.ic-assets.json` acordingly + 1. modify `.ic-assets.json` acordingly 1. execute `dfx deploy` -2. via manual candid call +2. via manual candid call ``` dfx canister call PROJECT_NAME_frontend set_asset_properties '( record { key="/robots.txt"; allow_raw_access=opt(opt(true)) })' ``` @@ -750,7 +821,7 @@ Callable only by a controller. Clears list of authorized principals and adds th ### feat(frontend-canister): add `get_asset_properties` and `set_asset_properties` to frontend canister -As part of creating the support for future work, it's now possible to get and set AssetProperties for assets in frontend canister. +As part of creating the support for future work, it's now possible to get and set AssetProperties for assets in frontend canister. ### feat: add `--argument-file` argument to the `dfx canister sign` command @@ -861,7 +932,7 @@ This incorporates the following executed proposals: ### feat(frontend-canister): add warning if config is provided in `.ic-assets.json` but not used -### fix(frontend-canister): Allow overwriting default HTTP Headers for assets in frontend canister +### fix(frontend-canister): Allow overwriting default HTTP Headers for assets in frontend canister Allows to overwrite `Content-Type`, `Content-Encoding`, and `Cache-Control` HTTP headers with custom values via `.ic-assets.json5` config file. Example `.ic-assets.json5` file: ```json5 @@ -875,7 +946,7 @@ Allows to overwrite `Content-Type`, `Content-Encoding`, and `Cache-Control` HTTP } ] ``` -This change will trigger the update process for frontend canister (new module hash: `2ff0513123f11c57716d889ca487083fac7d94a4c9434d5879f8d0342ad9d759`). +This change will trigger the update process for frontend canister (new module hash: `2ff0513123f11c57716d889ca487083fac7d94a4c9434d5879f8d0342ad9d759`). ### feat: warn if an unencrypted identity is used on mainnet @@ -1100,12 +1171,12 @@ The running replica port and url are generally useful information. Previously th Instead, the build process relies on `ic-wasm` to provide candid metadata for the canister, and shrinking the canister size by stripping debug symbols and unused fuctions. -Additionally, after build step, the `.wasm` file is archived with `gzip`. +Additionally, after build step, the `.wasm` file is archived with `gzip`. ### chore: Move all `frontend canister`-related code into the SDK repo | from (`repository` `path`) | to (path in `dfinity/sdk` repository) | summary | -|:--------------------------------------------|:-----------------------------------------------|:--------------------------------------------------------------------------------------------| +| :------------------------------------------ | :--------------------------------------------- | :------------------------------------------------------------------------------------------ | | `dfinity/cdk-rs` `/src/ic-certified-assets` | `/src/canisters/frontend/ic-certified-asset` | the core of the frontend canister | | `dfinity/certified-assets` `/` | `/src/canisters/frontend/ic-frontend-canister` | wraps `ic-certified-assets` to build the canister wasm | | `dfinity/agent-rs` `/ic-asset` | `/src/canisters/frontend/ic-asset` | library facilitating interactions with frontend canister (e.g. uploading or listing assets) | @@ -1122,7 +1193,7 @@ as if they were [JSON5](https://json5.org/) format. Example content of the `.ic- "match": "*", // comment /* keys below not wrapped in quotes -*/ cache: { max_age: 999 }, // trailing comma +*/ cache: { max_age: 999 }, // trailing comma }, ] ``` diff --git a/Cargo.lock b/Cargo.lock index 904154645a..36caf124d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,16 +743,17 @@ dependencies = [ [[package]] name = "candid" -version = "0.9.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df671c37a9c6168db0334f2b289dd4e02dea1bbefe1fb22c5d43b12d865aacd" +checksum = "88f6eec0ae850e006ef0fe306f362884d370624094ec55a6a26de18b251774be" dependencies = [ "anyhow", "arbitrary", "binread", "byteorder", - "candid_derive 0.6.2", + "candid_derive 0.6.3", "codespan-reporting", + "convert_case 0.6.0", "crc32fast", "data-encoding", "fake", @@ -789,9 +790,9 @@ dependencies = [ [[package]] name = "candid_derive" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810b3bd60244f282090652ffc7c30a9d23892e72dfe443e46ee55569044f7dd5" +checksum = "158403ea38fab5904ae47a5d67eb7047650a91681407f5ccbcbcabc4f4ffb489" dependencies = [ "lazy_static", "proc-macro2", @@ -835,6 +836,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ci_info" +version = "0.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eb8c8d139abf8673b1805f52f6ad398825e24c397dc9ac8283fe588dd6c80d9" +dependencies = [ + "envmnt", +] + [[package]] name = "cipher" version = "0.3.0" @@ -1330,7 +1340,8 @@ dependencies = [ "base64 0.13.1", "byte-unit", "bytes", - "candid 0.9.1", + "candid 0.9.6", + "ci_info", "clap", "console", "crc32fast", @@ -1351,7 +1362,7 @@ dependencies = [ "ic-agent", "ic-asset", "ic-identity-hsm", - "ic-utils 0.26.1", + "ic-utils 0.29.0", "ic-wasm", "indicatif", "itertools 0.10.5", @@ -1411,7 +1422,7 @@ dependencies = [ "bip32", "byte-unit", "bytes", - "candid 0.9.1", + "candid 0.9.6", "clap", "dialoguer", "directories-next", @@ -1420,7 +1431,7 @@ dependencies = [ "humantime-serde", "ic-agent", "ic-identity-hsm", - "ic-utils 0.26.1", + "ic-utils 0.29.0", "k256 0.11.6", "keyring", "lazy_static", @@ -1553,6 +1564,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + [[package]] name = "dyn-clone" version = "1.0.11" @@ -1699,6 +1716,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envmnt" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d73999a2b8871e74c8b8bc23759ee9f3d85011b24fafc91a4b3b5c8cc8185501" +dependencies = [ + "fsio", + "indexmap 1.9.3", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1874,6 +1901,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsio" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad0ce30be0cc441b325c5d705c8b613a0ca0d92b6a8953d41bd236dc09a36d0" +dependencies = [ + "dunce", +] + [[package]] name = "funty" version = "2.0.0" @@ -2350,16 +2386,17 @@ dependencies = [ [[package]] name = "ic-agent" -version = "0.26.1" -source = "git+https://github.com/dfinity/agent-rs.git?rev=4afb978efb2e3fb0bcaa2178571b81bed73ca0ae#4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +version = "0.29.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68#b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" dependencies = [ "backoff", - "candid 0.9.1", + "candid 0.9.6", "futures-util", "hex", "http", "http-body", - "ic-certification 0.26.1", + "ic-certification 1.2.0", + "ic-transport-types", "ic-verify-bls-signature", "k256 0.13.1", "leb128", @@ -2387,7 +2424,7 @@ name = "ic-asset" version = "0.20.0" dependencies = [ "backoff", - "candid 0.9.1", + "candid 0.9.6", "derivative", "dfx-core", "flate2", @@ -2396,7 +2433,7 @@ dependencies = [ "globset", "hex", "ic-agent", - "ic-utils 0.26.1", + "ic-utils 0.29.0", "itertools 0.10.5", "json5", "mime", @@ -2462,7 +2499,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d4c0b932bf454d5d60e61e13c3c944972fcfd74dc82b9ed5c8b0a75979cf50" dependencies = [ - "candid 0.9.1", + "candid 0.9.6", "ic-cdk-macros", "ic0", "serde", @@ -2475,7 +2512,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4587624e64b8db56224033ee74e5c246d39be15375d03d3df7c117d49d18487" dependencies = [ - "candid 0.9.1", + "candid 0.9.6", "proc-macro2", "quote", "serde", @@ -2495,8 +2532,9 @@ dependencies = [ [[package]] name = "ic-certification" -version = "0.26.1" -source = "git+https://github.com/dfinity/agent-rs.git?rev=4afb978efb2e3fb0bcaa2178571b81bed73ca0ae#4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a59dc342d11b2067e19d0f146bdec3674de921303ffc762f114d201ebbe0e68a" dependencies = [ "hex", "serde", @@ -2509,7 +2547,7 @@ name = "ic-certified-assets" version = "0.2.5" dependencies = [ "base64 0.13.1", - "candid 0.9.1", + "candid 0.9.6", "hex", "ic-cdk", "ic-certified-map", @@ -2714,8 +2752,8 @@ dependencies = [ [[package]] name = "ic-identity-hsm" -version = "0.26.1" -source = "git+https://github.com/dfinity/agent-rs.git?rev=4afb978efb2e3fb0bcaa2178571b81bed73ca0ae#4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +version = "0.29.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68#b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" dependencies = [ "hex", "ic-agent", @@ -2808,6 +2846,22 @@ dependencies = [ "wsl", ] +[[package]] +name = "ic-transport-types" +version = "0.29.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68#b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" +dependencies = [ + "candid 0.9.6", + "hex", + "ic-certification 1.2.0", + "leb128", + "serde", + "serde_bytes", + "serde_repr", + "sha2 0.10.7", + "thiserror", +] + [[package]] name = "ic-types" version = "0.8.0" @@ -2869,11 +2923,11 @@ dependencies = [ [[package]] name = "ic-utils" -version = "0.26.1" -source = "git+https://github.com/dfinity/agent-rs.git?rev=4afb978efb2e3fb0bcaa2178571b81bed73ca0ae#4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +version = "0.29.0" +source = "git+https://github.com/dfinity/agent-rs.git?rev=b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68#b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" dependencies = [ "async-trait", - "candid 0.9.1", + "candid 0.9.6", "ic-agent", "once_cell", "semver", @@ -2904,7 +2958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e360e45c2bf406a867c35ec5daee433f2c3bbbaf013469e6a386a322a9713885" dependencies = [ "anyhow", - "candid 0.9.1", + "candid 0.9.6", "clap", "rustc-demangle", "tempfile", @@ -2940,13 +2994,13 @@ version = "0.20.0" dependencies = [ "anstyle", "anyhow", - "candid 0.9.1", + "candid 0.9.6", "clap", "delay", "humantime", "ic-agent", "ic-asset", - "ic-utils 0.26.1", + "ic-utils 0.29.0", "libflate", "num-traits", "pem 1.1.1", diff --git a/Cargo.toml b/Cargo.toml index 48fda70fd3..4cae352f04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,11 +19,11 @@ license = "Apache-2.0" [workspace.dependencies] candid = "0.9.0" -ic-agent = "0.26.1" +ic-agent = "0.29.0" ic-asset = { path = "src/canisters/frontend/ic-asset" } ic-cdk = "0.10.0" -ic-identity-hsm = "0.26.1" -ic-utils = "0.26.1" +ic-identity-hsm = "0.29.0" +ic-utils = "0.29.0" aes-gcm = "0.9.4" anyhow = "1.0.56" @@ -69,19 +69,19 @@ url = "2.1.0" walkdir = "2.3.2" [patch.crates-io.ic-agent] -version = "0.26.1" +version = "0.29.0" git = "https://github.com/dfinity/agent-rs.git" -rev = "4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +rev = "b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" [patch.crates-io.ic-identity-hsm] -version = "0.26.1" +version = "0.29.0" git = "https://github.com/dfinity/agent-rs.git" -rev = "4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +rev = "b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" [patch.crates-io.ic-utils] -version = "0.26.1" +version = "0.29.0" git = "https://github.com/dfinity/agent-rs.git" -rev = "4afb978efb2e3fb0bcaa2178571b81bed73ca0ae" +rev = "b91b85b7b6ba6bfaf9dfd616b7c7c8f966bf8f68" [profile.release] panic = 'abort' diff --git a/docs/cli-reference/dfx-bootstrap.md b/docs/cli-reference/dfx-bootstrap.md index 94967fc1d7..430750c36e 100644 --- a/docs/cli-reference/dfx-bootstrap.md +++ b/docs/cli-reference/dfx-bootstrap.md @@ -1,4 +1,3 @@ # dfx bootstrap {#_dfx_bootstrap} -> **NOTE**: The bootstrap command has been removed. Please use the [dfx start](./dfx-start.md) command instead. If you have a good reason to use the bootstrap command, please contribute to the [discussion](https://github.com/dfinity/sdk/discussions/3163). - +> **NOTE**: The bootstrap command has been removed. Please use the [dfx start](./dfx-start.md) command instead. diff --git a/docs/cli-reference/dfx-build.md b/docs/cli-reference/dfx-build.md index 0531cb914d..f10f411db8 100644 --- a/docs/cli-reference/dfx-build.md +++ b/docs/cli-reference/dfx-build.md @@ -16,16 +16,16 @@ dfx build [flag] [option] [--all | canister_name] You can use the following optional flags with the `dfx build` command. -| Flag | Description | -|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `--check` | Builds canisters using a temporary, hard-coded, locally-defined canister identifier for testing that your program compiles without connecting to the IC. | +| Flag | Description | +| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--check` | Builds canisters using a temporary, hard-coded, locally-defined canister identifier for testing that your program compiles without connecting to the IC. | ## Options You can specify the following options for the `dfx build` command. | Option | Description | -|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--network ` | Specifies the network alias or URL you want to connect to. You can use this option to override the network specified in the `dfx.json` configuration file. | | `--output-env-file` | Writes dfx environment variables to a provided path. Overrides the `output_env_file` configuration from `dfx.json` if passed. | @@ -34,7 +34,7 @@ You can specify the following options for the `dfx build` command. You can specify the following arguments for the `dfx build` command. | Argument | Description | -|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--all` | Builds all of the canisters configured in the project’s `dfx.json` file. | | `canister_name` | Specifies the name of the canister you want to build. If you are not using the `--all` option, you can continue to use `dfx build` or provide a canister name as an argument (the canister name must match at least one name that you have configured in the `canisters` section of the `dfx.json` configuration file for your project.) | @@ -57,3 +57,7 @@ To test whether a canister compiles without connecting to the IC or the local ca ``` bash dfx build --check ``` + +## Management Canister + +If `dfx` detects that your Motoko project is importing the Management Canister (e.g. `import Management "ic:aaaaa-aa";`) it will automatically provide the Candid interface for the Management Canister during the build. \ No newline at end of file diff --git a/docs/cli-reference/dfx-canister.md b/docs/cli-reference/dfx-canister.md index 559ef2549f..555b83df78 100644 --- a/docs/cli-reference/dfx-canister.md +++ b/docs/cli-reference/dfx-canister.md @@ -216,6 +216,7 @@ You can use the following options with the `dfx canister create` command. | `-c`, `--compute-allocation ` | Specifies the canister's compute allocation. This should be a percent in the range [0..100]. | | `--controller ` | Specifies the identity name or the principal of the new controller. | | `--memory-allocation ` | Specifies how much memory the canister is allowed to use in total. This should be a value in the range [0..12 GiB]. A setting of 0 means the canister will have access to memory on a “best-effort” basis: It will only be charged for the memory it uses, but at any point in time may stop running if it tries to allocate more memory when there isn’t space available on the subnet. | +| `--reserved-cycles-limit ` | Specifies the upper limit for the canister's reserved cycles. | | `--no-wallet` | Performs the call with the user Identity as the Sender of messages. Bypasses the Wallet canister. Enabled by default. | | `--with-cycles ` | Specifies the initial cycle balance to deposit into the newly created canister. The specified amount needs to take the canister create fee into account. This amount is deducted from the wallet's cycle balance. | | `--specified-id ` | Attempts to create the canister with this Canister ID | @@ -921,6 +922,7 @@ You can specify the following options for the `dfx canister update-settings` com | `-c`, `--compute-allocation ` | Specifies the canister's compute allocation. This should be a percent in the range [0..100]. | | `--set-controller ` | Specifies the identity name or the principal of the new controller. Can be specified more than once, indicating the canister will have multiple controllers. If any controllers are set with this parameter, any other controllers will be removed. | | `--memory-allocation ` | Specifies how much memory the canister is allowed to use in total. This should be a value in the range [0..12 GiB]. A setting of 0 means the canister will have access to memory on a “best-effort” basis: It will only be charged for the memory it uses, but at any point in time may stop running if it tries to allocate more memory when there isn’t space available on the subnet. | +| `--reserved-cycles-limit ` | Specifies the upper limit of the canister's reserved cycles. | | `--remove-controller ` | Removes a principal from the list of controllers of the canister. | | `--freezing-threshold ` | Set the [freezing threshold](https://internetcomputer.org/docs/current/references/ic-interface-spec/#ic-create_canister) in seconds for a canister. This should be a value in the range [0..2^64^-1]. Very long thresholds require the `--confirm-very-long-freezing-threshold` flag. | | `-y`, `--yes` | Skips yes/no checks by answering 'yes'. Such checks can result in loss of control, so this is not recommended outside of CI. | diff --git a/docs/cli-reference/dfx-parent.md b/docs/cli-reference/dfx-parent.md index ce77795972..d8f02c3461 100644 --- a/docs/cli-reference/dfx-parent.md +++ b/docs/cli-reference/dfx-parent.md @@ -42,7 +42,7 @@ For reference information and examples, select an appropriate subcommand. | Command | Description | |---------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| bootstrap | Starts the bootstrap server (icx-proxy) | +| bootstrap | Removed. Use the `start` command instead. | | [`build`](./dfx-build.md) | Builds canister output from the source code in your project. | | [`cache`](./dfx-cache.md) | Manages the `dfx` cache on the local computer. | | [`canister`](./dfx-canister.md) | Manages deployed canisters . | | @@ -58,7 +58,7 @@ For reference information and examples, select an appropriate subcommand. | [`ping`](./dfx-ping.md) | Sends a response request to the IC or the local canister execution environment to determine network connectivity. If the connection is successful, a status reply is returned. | | quickstart | Perform initial one time setup for your identity and/or wallet. | | remote | Commands used to work with remote canisters. | -| [`replica`](./dfx-replica.md) | Starts a local canister execution environment. | +| [`replica`](./dfx-replica.md) | Removed. Use the `start` command instead. | | [`schema`](./dfx-schema.md) | Prints the schema for `dfx.json`. | | [`start`](./dfx-start.md) | Starts the local canister execution environment a web server for the current project. | | [`stop`](./dfx-stop.md) | Stops the local canister execution environment. | diff --git a/docs/cli-reference/dfx-replica.md b/docs/cli-reference/dfx-replica.md index 05fd408f0b..054d193fa1 100644 --- a/docs/cli-reference/dfx-replica.md +++ b/docs/cli-reference/dfx-replica.md @@ -1,4 +1,3 @@ # dfx replica -> **NOTE**: The replica command has been removed. Please use the [dfx start](./dfx-start.md) command instead. If you have a good reason to use the replica command, please contribute to the [discussion](https://github.com/dfinity/sdk/discussions/3163). - +> **NOTE**: The replica command has been removed. Please use the [dfx start](./dfx-start.md) command instead. \ No newline at end of file diff --git a/docs/cli-reference/dfx-start.md b/docs/cli-reference/dfx-start.md index ed8875deaa..83a399d66c 100644 --- a/docs/cli-reference/dfx-start.md +++ b/docs/cli-reference/dfx-start.md @@ -84,6 +84,4 @@ dfx stores data for the shared local network in one of the following locations, If dfx.json defines the `local` network, then `dfx start` will use this definition and store network data files under `\/.dfx/network/local`. -Such project-specific networks are deprecated, and we plan to remove support for them after February 2023. We encourage you to remove any definitions of the `local` network from your project's dfx.json file and instead use the default shared local network. - Note that for projects that define the `local` network in dfx.json, you can only run the `dfx start` and `dfx stop` commands from within the project directory structure. For example, if your project name is `hello_world`, your current working directory must be the `hello_world` top-level project directory or one of its subdirectories. diff --git a/docs/dfx-json-schema.json b/docs/dfx-json-schema.json index 42c2a2db4b..186ca4c6f7 100644 --- a/docs/dfx-json-schema.json +++ b/docs/dfx-json-schema.json @@ -372,7 +372,8 @@ "default": { "compute_allocation": null, "freezing_threshold": null, - "memory_allocation": null + "memory_allocation": null, + "reserved_cycles_limit": null }, "allOf": [ { @@ -579,6 +580,7 @@ }, "ConfigDefaultsBootstrap": { "title": "Bootstrap Server Configuration", + "description": "The bootstrap command has been removed. All of these fields are ignored.", "type": "object", "properties": { "ip": { @@ -847,6 +849,17 @@ ], "format": "uint64", "minimum": 0.0 + }, + "reserved_cycles_limit": { + "title": "Reserved Cycles Limit", + "description": "Specifies the upper limit of the canister's reserved cycles balance.\n\nReserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB.", + "default": null, + "type": [ + "integer", + "null" + ], + "format": "uint128", + "minimum": 0.0 } } }, diff --git a/docs/networks-json-schema.json b/docs/networks-json-schema.json index a8725ca836..c5d7ef47c7 100644 --- a/docs/networks-json-schema.json +++ b/docs/networks-json-schema.json @@ -59,6 +59,7 @@ }, "ConfigDefaultsBootstrap": { "title": "Bootstrap Server Configuration", + "description": "The bootstrap command has been removed. All of these fields are ignored.", "type": "object", "properties": { "ip": { diff --git a/e2e/assets/motoko_management/main.mo b/e2e/assets/motoko_management/main.mo new file mode 100644 index 0000000000..2fd2f8e820 --- /dev/null +++ b/e2e/assets/motoko_management/main.mo @@ -0,0 +1,7 @@ +import Management "ic:aaaaa-aa"; + +actor { + public func rand() : async Blob { + await Management.raw_rand(); + }; +}; diff --git a/e2e/assets/motoko_management/patch.bash b/e2e/assets/motoko_management/patch.bash new file mode 100644 index 0000000000..da753f124a --- /dev/null +++ b/e2e/assets/motoko_management/patch.bash @@ -0,0 +1 @@ +jq '.canisters.e2e_project_backend.main="main.mo"' dfx.json | sponge dfx.json diff --git a/e2e/assets/motoko_management_recursive/dependency.mo b/e2e/assets/motoko_management_recursive/dependency.mo new file mode 100644 index 0000000000..28cf8aece7 --- /dev/null +++ b/e2e/assets/motoko_management_recursive/dependency.mo @@ -0,0 +1,7 @@ +import Management "ic:aaaaa-aa"; + +module Rand { + public func rand() : async Blob { + await Management.raw_rand(); + }; +}; diff --git a/e2e/assets/motoko_management_recursive/main.mo b/e2e/assets/motoko_management_recursive/main.mo new file mode 100644 index 0000000000..8fcf9f9bde --- /dev/null +++ b/e2e/assets/motoko_management_recursive/main.mo @@ -0,0 +1,7 @@ +import Rand "dependency"; + +actor { + public func rand() : async Blob { + await Rand.rand(); + }; +}; diff --git a/e2e/assets/motoko_management_recursive/patch.bash b/e2e/assets/motoko_management_recursive/patch.bash new file mode 100644 index 0000000000..da753f124a --- /dev/null +++ b/e2e/assets/motoko_management_recursive/patch.bash @@ -0,0 +1 @@ +jq '.canisters.e2e_project_backend.main="main.mo"' dfx.json | sponge dfx.json diff --git a/e2e/assets/playground_backend/dfx.json b/e2e/assets/playground_backend/dfx.json index dbae6de715..afdd89f86b 100644 --- a/e2e/assets/playground_backend/dfx.json +++ b/e2e/assets/playground_backend/dfx.json @@ -19,7 +19,7 @@ "defaults": { "build": { "output": "build", - "packtool": "mops sources" + "packtool": "./mops-sources" } } } \ No newline at end of file diff --git a/e2e/assets/playground_backend/mops-sources b/e2e/assets/playground_backend/mops-sources new file mode 100755 index 0000000000..56be5bd50a --- /dev/null +++ b/e2e/assets/playground_backend/mops-sources @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -e + +for i in 0 2 4 8 16 32; do + if [ $i -gt 0 ]; then + echo "retrying in $i seconds" 1>&2 + sleep $i + fi + + if output=$(mops sources); then + echo "$output" + exit 0 + fi + echo "failed with output: $output" 1>&2 +done +exit 1 diff --git a/e2e/assets/playground_backend/service/pool/Logs.mo b/e2e/assets/playground_backend/service/pool/Logs.mo index 10e467328c..7e28c0af99 100644 --- a/e2e/assets/playground_backend/service/pool/Logs.mo +++ b/e2e/assets/playground_backend/service/pool/Logs.mo @@ -1,4 +1,88 @@ +import Map "mo:base/RBTree"; +import {compare} "mo:base/Text"; +import {toArray} "mo:base/Iter"; +import {now = timeNow} "mo:base/Time"; +import {toText} "mo:base/Int"; +import {get} "mo:base/Option"; + module { + public type Origin = { origin: Text; tags: [Text] }; + public type SharedStatsByOrigin = (Map.Tree, Map.Tree); + public class StatsByOrigin() { + var canisters = Map.RBTree(compare); + var installs = Map.RBTree(compare); + public func share() : SharedStatsByOrigin = (canisters.share(), installs.share()); + public func unshare(x : SharedStatsByOrigin) { + canisters.unshare(x.0); + installs.unshare(x.1); + }; + func addTags(map: Map.RBTree, list: [Text]) { + for (tag in list.vals()) { + switch (map.get(tag)) { + case null { map.put(tag, 1) }; + case (?n) { map.put(tag, n + 1) }; + }; + }; + }; + // if to is null, delete the from tag + func merge_tag_(map: Map.RBTree, from: Text, opt_to: ?Text) { + ignore do ? { + let n1 = map.remove(from)!; + let to = opt_to!; + switch (map.get(to)) { + case null { map.put(to, n1) }; + case (?n2) { map.put(to, n1 + n2) }; + }; + }; + }; + public func merge_tag(from: Text, to: ?Text) { + merge_tag_(canisters, from, to); + merge_tag_(installs, from, to); + }; + public func addCanister(origin: Origin) { + addTags(canisters, ["origin:" # origin.origin]); + addTags(canisters, origin.tags); + }; + public func addInstall(origin: Origin) { + addTags(installs, ["origin:" # origin.origin]); + addTags(installs, origin.tags); + }; + public func dump() : ([(Text, Nat)], [(Text, Nat)]) { + (toArray<(Text, Nat)>(canisters.entries()), + toArray<(Text, Nat)>(installs.entries()), + ) + }; + public func metrics() : Text { + var result = ""; + let now = timeNow() / 1_000_000; + let canister_playground = get(canisters.get("origin:playground"), 0); + let canister_dfx = get(canisters.get("origin:dfx"), 0); + let install_playground = get(installs.get("origin:playground"), 0); + let install_dfx = get(installs.get("origin:dfx"), 0); + let profiling = get(installs.get("wasm:profiling"), 0); + let asset = get(installs.get("wasm:asset"), 0); + let install = get(installs.get("mode:install"), 0); + let reinstall = get(installs.get("mode:reinstall"), 0); + let upgrade = get(installs.get("mode:upgrade"), 0); + result := result + # encode_single_value("counter", "create_from_playground", canister_playground, "Number of canisters created from playground", now) + # encode_single_value("counter", "install_from_playground", install_playground, "Number of Wasms installed from playground", now) + # encode_single_value("counter", "create_from_dfx", canister_dfx, "Number of canisters created from dfx", now) + # encode_single_value("counter", "install_from_dfx", install_dfx, "Number of Wasms installed from dfx", now) + # encode_single_value("counter", "profiling", profiling, "Number of Wasms profiled", now) + # encode_single_value("counter", "asset", asset, "Number of asset Wasms canister installed", now) + # encode_single_value("counter", "install", install, "Number of Wasms with install mode", now) + # encode_single_value("counter", "reinstall", reinstall, "Number of Wasms with reinstall mode", now) + # encode_single_value("counter", "upgrade", upgrade, "Number of Wasms with upgrad mode", now); + result; + }; + }; + public func encode_single_value(kind: Text, name: Text, number: Int, desc: Text, time: Int) : Text { + "# HELP " # name # " " # desc # "\n" # + "# TYPE " # name # " " # kind # "\n" # + name # " " # toText(number) # " " # toText(time) # "\n" + }; + public type Stats = { num_of_canisters: Nat; num_of_installs: Nat; diff --git a/e2e/assets/playground_backend/service/pool/Main.mo b/e2e/assets/playground_backend/service/pool/Main.mo index 5d42e6b8b3..ef519739b0 100644 --- a/e2e/assets/playground_backend/service/pool/Main.mo +++ b/e2e/assets/playground_backend/service/pool/Main.mo @@ -6,6 +6,7 @@ import Option "mo:base/Option"; import Nat "mo:base/Nat"; import Text "mo:base/Text"; import Array "mo:base/Array"; +import Buffer "mo:base/Buffer"; import List "mo:base/List"; import Deque "mo:base/Deque"; import Result "mo:base/Result"; @@ -17,29 +18,31 @@ import PoW "./PoW"; import Logs "./Logs"; import Metrics "./Metrics"; import Wasm "canister:wasm-utils"; -import Blob "mo:base/Blob"; -import Buffer "mo:base/Buffer"; -import Nat32 "mo:base/Nat32"; shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { let IC : ICType.Self = actor "aaaaa-aa"; let params = Option.get(opt_params, Types.defaultParams); var pool = Types.CanisterPool(params.max_num_canisters, params.canister_time_to_live, params.max_family_tree_size); let nonceCache = PoW.NonceCache(params.nonce_time_to_live); + var statsByOrigin = Logs.StatsByOrigin(); stable let controller = creator.caller; stable var stats = Logs.defaultStats; stable var stablePool : [Types.CanisterInfo] = []; stable var stableMetadata : [(Principal, (Int, Bool))] = []; stable var stableChildren : [(Principal, [Principal])] = []; + stable var stableTimers : [Types.CanisterInfo] = []; stable var previousParam : ?Types.InitParams = null; + stable var stableStatsByOrigin : Logs.SharedStatsByOrigin = (#leaf, #leaf); system func preupgrade() { - let (tree, metadata, children) = pool.share(); + let (tree, metadata, children, timers) = pool.share(); stablePool := tree; stableMetadata := metadata; stableChildren := children; + stableTimers := timers; previousParam := ?params; + stableStatsByOrigin := statsByOrigin.share(); }; system func postupgrade() { @@ -49,14 +52,19 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { }; }; pool.unshare(stablePool, stableMetadata, stableChildren); + for (info in stableTimers.vals()) { + updateTimer(info); + }; + statsByOrigin.unshare(stableStatsByOrigin); }; public query func getInitParams() : async Types.InitParams { params; }; - public query func getStats() : async Logs.Stats { - stats; + public query func getStats() : async (Logs.Stats, [(Text, Nat)], [(Text, Nat)]) { + let (canister, install) = statsByOrigin.dump(); + (stats, canister, install); }; public query func balance() : async Nat { @@ -68,7 +76,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { ignore Cycles.accept amount; }; - private func getExpiredCanisterInfo() : async Types.CanisterInfo { + private func getExpiredCanisterInfo(origin : Logs.Origin) : async Types.CanisterInfo { switch (pool.getExpiredCanisterId()) { case (#newId) { Cycles.add(params.cycles_per_canister); @@ -77,6 +85,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { let info = { id = cid.canister_id; timestamp = now }; pool.add info; stats := Logs.updateStats(stats, #getId(params.cycles_per_canister)); + statsByOrigin.addCanister(origin); info; }; case (#reuse info) { @@ -89,8 +98,9 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { Cycles.add topUpCycles; await IC.deposit_cycles cid; }; - // Lazily cleanup the reused canister - await IC.uninstall_code cid; + if (Option.isSome(status.module_hash)) { + await IC.uninstall_code cid; + }; switch (status.status) { case (#stopped or #stopping) { await IC.start_canister cid; @@ -98,6 +108,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { case _ {}; }; stats := Logs.updateStats(stats, #getId topUpCycles); + statsByOrigin.addCanister(origin); info; }; case (#outOfCapacity time) { @@ -107,8 +118,23 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { }; }; }; + func validateOrigin(origin: Logs.Origin) : Bool { + if (origin.origin == "") { + return false; + }; + for (tag in origin.tags.vals()) { + // reject server side tags + if (tag == "mode:install" or tag == "mode:reinstall" or tag == "mode:upgrade" or tag == "wasm:profiling" or tag == "wasm:asset") { + return false; + } + }; + return true; + }; - public shared ({ caller }) func getCanisterId(nonce : PoW.Nonce) : async Types.CanisterInfo { + public shared ({ caller }) func getCanisterId(nonce : PoW.Nonce, origin : Logs.Origin) : async Types.CanisterInfo { + if (not validateOrigin(origin)) { + throw Error.reject "Please specify a valid origin"; + }; if (caller != controller and not nonceCache.checkProofOfWork(nonce)) { stats := Logs.updateStats(stats, #mismatch); throw Error.reject "Proof of work check failed"; @@ -119,10 +145,14 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { throw Error.reject "Nonce already used"; }; nonceCache.add nonce; - await getExpiredCanisterInfo(); + await getExpiredCanisterInfo(origin); }; - public shared ({ caller }) func installCode(info : Types.CanisterInfo, args : Types.InstallArgs, profiling : Bool, is_whitelisted : Bool) : async Types.CanisterInfo { + type InstallConfig = { profiling: Bool; is_whitelisted: Bool; origin: Logs.Origin }; + public shared ({ caller }) func installCode(info : Types.CanisterInfo, args : Types.InstallArgs, install_config : InstallConfig) : async Types.CanisterInfo { + if (not validateOrigin(install_config.origin)) { + throw Error.reject "Please specify a valid origin"; + }; if (info.timestamp == 0) { stats := Logs.updateStats(stats, #mismatch); throw Error.reject "Cannot install removed canister"; @@ -132,14 +162,14 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { throw Error.reject "Cannot find canister"; } else { let config = { - profiling; + profiling = install_config.profiling; remove_cycles_add = true; limit_stable_memory_page = ?(16384 : Nat32); // Limit to 1G of stable memory backend_canister_id = ?Principal.fromActor(this); }; - let wasm = if (caller == controller) { + let wasm = if (caller == controller and install_config.is_whitelisted) { args.wasm_module; - } else if (is_whitelisted) { + } else if (install_config.is_whitelisted) { await Wasm.is_whitelisted(args.wasm_module); } else { await Wasm.transform(args.wasm_module, config); @@ -152,13 +182,42 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { }; await IC.install_code newArgs; stats := Logs.updateStats(stats, #install); - switch (pool.refresh(info, profiling)) { - case (?newInfo) newInfo; + + // Build tags from install arguments + let tags = Buffer.fromArray(install_config.origin.tags); + if (install_config.profiling) { + tags.add("wasm:profiling"); + }; + if (install_config.is_whitelisted) { + tags.add("wasm:asset"); + }; + switch (args.mode) { + case (#install) { tags.add("mode:install") }; + case (#upgrade) { tags.add("mode:upgrade") }; + case (#reinstall) { tags.add("mode:reinstall") }; + }; + let origin = { origin = install_config.origin.origin; tags = Buffer.toArray(tags) }; + statsByOrigin.addInstall(origin); + switch (pool.refresh(info, install_config.profiling)) { + case (?newInfo) { + updateTimer(newInfo); + newInfo; + }; case null { throw Error.reject "Cannot find canister" }; }; }; }; + func updateTimer(info: Types.CanisterInfo) { + func job() : async () { + pool.removeTimer(info.id); + // It is important that the timer job checks for the timestamp first. + // This prevents late-runner jobs from deleting newly installed code. + await removeCode(info); + }; + pool.updateTimer(info, job); + }; + public func callForward(info : Types.CanisterInfo, function : Text, args : Blob) : async Blob { if (pool.find info) { await InternetComputer.call(info.id, function, args); @@ -225,12 +284,19 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { throw Error.reject "Only called by controller"; }; stats := Logs.defaultStats; + statsByOrigin := Logs.StatsByOrigin(); + }; + public shared ({ caller }) func mergeTags(from: Text, to: ?Text) : async () { + if (caller != controller) { + throw Error.reject "Only called by controller"; + }; + statsByOrigin.merge_tag(from, to); }; // Metrics public query func http_request(req : Metrics.HttpRequest) : async Metrics.HttpResponse { if (req.url == "/metrics") { - let body = Metrics.metrics stats; + let body = Metrics.metrics(stats); { status_code = 200; headers = [("Content-Type", "text/plain; version=0.0.4"), ("Content-Length", Nat.toText(body.size()))]; @@ -278,7 +344,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { if (not pool.findId caller) { throw Error.reject "Only a canister managed by the Motoko Playground can call create_canister"; }; - let info = await getExpiredCanisterInfo(); + let info = await getExpiredCanisterInfo({origin="spawned"; tags=[]}); let result = pool.setChild(caller, info.id); if (not result) { throw Error.reject("In the Motoko Playground, each top level canister can only spawn " # Nat.toText(params.max_family_tree_size) # " descendants including itself"); @@ -299,12 +365,12 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { wasm_module : ICType.wasm_module; mode : { #reinstall; #upgrade; #install }; canister_id : ICType.canister_id; - is_whitelisted : Bool; }) : async () { switch (sanitizeInputs(caller, canister_id)) { case (#ok info) { let args = { arg; wasm_module; mode; canister_id }; - ignore await installCode(info, args, pool.profiling caller, is_whitelisted); // inherit the profiling of the parent + let config = { profiling = pool.profiling caller; is_whitelisted = false; origin = {origin = "spawned"; tags = [] } }; + ignore await installCode(info, args, config); // inherit the profiling of the parent }; case (#err makeMsg) throw Error.reject(makeMsg "install_code"); }; @@ -379,6 +445,7 @@ shared (creator) actor class Self(opt_params : ?Types.InitParams) = this { #installCode : Any; #removeCode : Any; #resetStats : Any; + #mergeTags : Any; #wallet_receive : Any; #create_canister : Any; diff --git a/e2e/assets/playground_backend/service/pool/Metrics.mo b/e2e/assets/playground_backend/service/pool/Metrics.mo index 4165615f7c..6f29605d9f 100644 --- a/e2e/assets/playground_backend/service/pool/Metrics.mo +++ b/e2e/assets/playground_backend/service/pool/Metrics.mo @@ -1,6 +1,5 @@ import Text "mo:base/Text"; import Time "mo:base/Time"; -import Int "mo:base/Int"; import Logs "./Logs"; module { @@ -15,11 +14,7 @@ module { headers: [(Text, Text)]; body: Blob; }; - func encode_single_value(kind: Text, name: Text, number: Int, desc: Text, time: Int) : Text { - "# HELP " # name # " " # desc # "\n" # - "# TYPE " # name # " " # kind # "\n" # - name # " " # Int.toText(number) # " " # Int.toText(time) # "\n" - }; + let encode_single_value = Logs.encode_single_value; public func metrics(stats: Logs.Stats) : Blob { let now = Time.now() / 1_000_000; var result = ""; diff --git a/e2e/assets/playground_backend/service/pool/PoW.mo b/e2e/assets/playground_backend/service/pool/PoW.mo index 7792b74a17..213ef29a3f 100644 --- a/e2e/assets/playground_backend/service/pool/PoW.mo +++ b/e2e/assets/playground_backend/service/pool/PoW.mo @@ -2,54 +2,41 @@ import Splay "mo:splay"; import Time "mo:base/Time"; import Text "mo:base/Text"; import Int "mo:base/Int"; -import Debug "mo:base/Debug" module { public type Nonce = { - timestamp : Int; - nonce : Nat; + timestamp: Int; + nonce: Nat; }; - func nonceCompare(x : Nonce, y : Nonce) : { #less; #equal; #greater } { - if (x.timestamp < y.timestamp) { #less } else if (x.timestamp == y.timestamp and x.nonce < y.nonce) { - #less; - } else if (x.timestamp == y.timestamp and x.nonce == y.nonce) { #equal } else { - #greater; - }; + func nonceCompare(x: Nonce, y: Nonce): {#less;#equal;#greater} { + if (x.timestamp < y.timestamp) { #less } + else if (x.timestamp == y.timestamp and x.nonce < y.nonce) { #less } + else if (x.timestamp == y.timestamp and x.nonce == y.nonce) { #equal } + else { #greater } }; - public class NonceCache(TTL : Nat) { + public class NonceCache(TTL: Nat) { let known_nonces = Splay.Splay(nonceCompare); - public func add(nonce : Nonce) { + public func add(nonce: Nonce) { known_nonces.insert(nonce); }; public func pruneExpired() { let now = Time.now(); for (info in known_nonces.entries()) { - if (info.timestamp > now - TTL) { return }; + if (info.timestamp > now - TTL) { return; }; known_nonces.remove(info); }; }; - public func contains(nonce : Nonce) : Bool { - known_nonces.find(nonce); + public func contains(nonce: Nonce) : Bool { + known_nonces.find(nonce) }; - public func checkProofOfWork(nonce : Nonce) : Bool { + public func checkProofOfWork(nonce: Nonce) : Bool { let now = Time.now(); - if (nonce.timestamp < now - TTL) { - Debug.trap("too late"); - return false; - }; - if (nonce.timestamp > now + TTL) { - Debug.trap("too early"); - return false; - }; + if (nonce.timestamp < now - TTL) return false; + if (nonce.timestamp > now + TTL) return false; let raw = "motoko-playground" # (Int.toText(nonce.timestamp)) # (Int.toText(nonce.nonce)); - Debug.print(raw); let hash = Text.hash(raw); - Debug.print("The Motoko-calculated hash is " # debug_show (hash)); - if (hash & 0xc0000000 != 0) { - Debug.trap("other stuff failed"); - return false; - }; - true; + if (hash & 0xc0000000 != 0) return false; + true }; }; -}; +} diff --git a/e2e/assets/playground_backend/service/pool/Types.mo b/e2e/assets/playground_backend/service/pool/Types.mo index 54fbf070f7..440087095e 100644 --- a/e2e/assets/playground_backend/service/pool/Types.mo +++ b/e2e/assets/playground_backend/service/pool/Types.mo @@ -9,6 +9,7 @@ import Array "mo:base/Array"; import List "mo:base/List"; import Option "mo:base/Option"; import Int "mo:base/Int"; +import Timer "mo:base/Timer"; module { public type InitParams = { @@ -51,9 +52,11 @@ module { public class CanisterPool(size: Nat, ttl: Nat, max_family_tree_size: Nat) { var len = 0; var tree = Splay.Splay(canisterInfoCompare); + // Metadata is a replicate of splay tree, which allows lookup without timestamp. Internal use only. var metadata = TrieMap.TrieMap(Principal.equal, Principal.hash); var childrens = TrieMap.TrieMap>(Principal.equal, Principal.hash); var parents = TrieMap.TrieMap(Principal.equal, Principal.hash); + let timers = TrieMap.TrieMap(Principal.equal, Principal.hash); public type NewId = { #newId; #reuse:CanisterInfo; #outOfCapacity:Nat }; @@ -123,6 +126,24 @@ module { return true; }; + public func updateTimer(info: CanisterInfo, job : () -> async ()) { + let elapsed = Time.now() - info.timestamp; + let duration = if (elapsed > ttl) { 0 } else { Int.abs(ttl - elapsed) }; + let tid = Timer.setTimer(#nanoseconds duration, job); + switch (timers.replace(info.id, tid)) { + case null {}; + case (?old_id) { + // The old job can still run when it has expired, but the future + // just started to run. To be safe, the job needs to check for timestamp. + Timer.cancelTimer(old_id); + }; + }; + }; + + public func removeTimer(cid: Principal) { + timers.delete cid; + }; + private func notExpired(info: CanisterInfo, now: Int) : Bool = (info.timestamp > now - ttl); // Return a list of canister IDs from which to uninstall code @@ -140,17 +161,22 @@ module { result }; - public func share() : ([CanisterInfo], [(Principal, (Int, Bool))], [(Principal, [Principal])]) { + public func share() : ([CanisterInfo], [(Principal, (Int, Bool))], [(Principal, [Principal])], [CanisterInfo]) { let stableInfos = Iter.toArray(tree.entries()); let stableMetadata = Iter.toArray(metadata.entries()); - let stableChildrens = + let stableChildren = Iter.toArray( Iter.map<(Principal, List.List), (Principal, [Principal])>( childrens.entries(), func((parent, children)) = (parent, List.toArray(children)) ) ); - (stableInfos, stableMetadata, stableChildrens) + let stableTimers = Iter.toArray( + Iter.filter( + tree.entries(), + func (info) = Option.isSome(timers.get(info.id)) + )); + (stableInfos, stableMetadata, stableChildren, stableTimers) }; public func unshare(stableInfos: [CanisterInfo], stableMetadata: [(Principal, (Int, Bool))], stableChildrens : [(Principal, [Principal])]) { diff --git a/e2e/assets/playground_backend/wasm-utils.wasm b/e2e/assets/playground_backend/wasm-utils.wasm index b5f605eaca..30de263cf5 100644 Binary files a/e2e/assets/playground_backend/wasm-utils.wasm and b/e2e/assets/playground_backend/wasm-utils.wasm differ diff --git a/e2e/tests-dfx/assetscanister.bash b/e2e/tests-dfx/assetscanister.bash index 8207eac768..a95795fa33 100644 --- a/e2e/tests-dfx/assetscanister.bash +++ b/e2e/tests-dfx/assetscanister.bash @@ -3,42 +3,42 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } create_batch() { - reg="batch_id = ([0-9]*) : nat" - assert_command dfx canister call e2e_project_frontend create_batch '(record { })' - # shellcheck disable=SC2154 - [[ "$stdout" =~ $reg ]] - BATCH_ID="${BASH_REMATCH[1]}" - echo "$BATCH_ID" + reg="batch_id = ([0-9]*) : nat" + assert_command dfx canister call e2e_project_frontend create_batch '(record { })' + # shellcheck disable=SC2154 + [[ "$stdout" =~ $reg ]] + BATCH_ID="${BASH_REMATCH[1]}" + echo "$BATCH_ID" } create_chunk() { - batch_id="$1" - reg="chunk_id = ([0-9]*) : nat" - assert_command dfx canister call e2e_project_frontend create_chunk "(record { batch_id = $batch_id; content = vec {} })" - # shellcheck disable=SC2154 - [[ "$stdout" =~ $reg ]] - CHUNK_ID="${BASH_REMATCH[1]}" - echo "$CHUNK_ID" + batch_id="$1" + reg="chunk_id = ([0-9]*) : nat" + assert_command dfx canister call e2e_project_frontend create_chunk "(record { batch_id = $batch_id; content = vec {} })" + # shellcheck disable=SC2154 + [[ "$stdout" =~ $reg ]] + CHUNK_ID="${BASH_REMATCH[1]}" + echo "$CHUNK_ID" } delete_batch() { - assert_command dfx canister call e2e_project_frontend delete_batch "(record { batch_id=$1; })" + assert_command dfx canister call e2e_project_frontend delete_batch "(record { batch_id=$1; })" } check_permission_failure() { - assert_contains "$1" + assert_contains "$1" } @test "commit_batch is atomic" { @@ -134,14 +134,14 @@ check_permission_failure() { dfx identity get-principal --identity prepare dfx canister call e2e_project_frontend list_permitted '(record { permission = variant { Commit }; })' assert_command dfx deploy e2e_project_frontend --by-proposal --identity prepare - assert_contains "Proposed commit of batch 2 with evidence 4301263f1fcc0d19ef92cfb6774c4da92bf1a9d2002a293a9d95d97819c02958. Either commit it by proposal, or delete it." + assert_contains "Proposed commit of batch 2 with evidence 164fcc4d933ff9992ab6ab909a4bf350010fa0f4a3e1e247bfc679d3f45254e1. Either commit it by proposal, or delete it." assert_command_fail dfx deploy e2e_project_frontend --by-proposal --identity prepare assert_contains "Batch 2 is already proposed. Delete or execute it to propose another." assert_command dfx deploy e2e_project_frontend --compute-evidence --identity anonymous # shellcheck disable=SC2154 - assert_eq "4301263f1fcc0d19ef92cfb6774c4da92bf1a9d2002a293a9d95d97819c02958" "$stdout" + assert_eq "164fcc4d933ff9992ab6ab909a4bf350010fa0f4a3e1e247bfc679d3f45254e1" "$stdout" ID=$(dfx canister id e2e_project_frontend) PORT=$(get_webserver_port) @@ -161,9 +161,9 @@ check_permission_failure() { assert_command_fail dfx canister call e2e_project_frontend commit_proposed_batch "$wrong_commit_args" --identity commit assert_match "batch computed evidence .* does not match presented evidence" - commit_args='(record { batch_id = 2; evidence = blob "\43\01\26\3f\1f\cc\0d\19\ef\92\cf\b6\77\4c\4d\a9\2b\f1\a9\d2\00\2a\29\3a\9d\95\d9\78\19\c0\29\58" } )' + commit_args='(record { batch_id = 2; evidence = blob "\16\4f\cc\4d\93\3f\f9\99\2a\b6\ab\90\9a\4b\f3\50\01\0f\a0\f4\a3\e1\e2\47\bf\c6\79\d3\f4\52\54\e1" } )' assert_command dfx canister call e2e_project_frontend validate_commit_proposed_batch "$commit_args" --identity commit - assert_contains "commit proposed batch 2 with evidence 4301" + assert_contains "commit proposed batch 2 with evidence 164f" assert_command dfx canister call e2e_project_frontend commit_proposed_batch "$commit_args" --identity commit assert_eq "()" @@ -213,11 +213,11 @@ check_permission_failure() { dfx identity get-principal --identity prepare dfx canister call e2e_project_frontend list_permitted '(record { permission = variant { Commit }; })' assert_command dfx deploy e2e_project_frontend --by-proposal --identity prepare - assert_contains "Proposed commit of batch 2 with evidence 1b45c8b1d0deec88ac032590e0f1cd9ab407f796e827aac880f4ffb035fdc200. Either commit it by proposal, or delete it." + assert_contains "Proposed commit of batch 2 with evidence 9b72eee7f0d7af2a9b41233c341b1caa0c905ef91405f5f513ffb58f68afee5b. Either commit it by proposal, or delete it." assert_command dfx deploy e2e_project_frontend --compute-evidence --identity anonymous # shellcheck disable=SC2154 - assert_eq "1b45c8b1d0deec88ac032590e0f1cd9ab407f796e827aac880f4ffb035fdc200" "$stdout" + assert_eq "9b72eee7f0d7af2a9b41233c341b1caa0c905ef91405f5f513ffb58f68afee5b" "$stdout" ID=$(dfx canister id e2e_project_frontend) PORT=$(get_webserver_port) @@ -225,9 +225,9 @@ check_permission_failure() { assert_command_fail curl --fail -vv http://localhost:"$PORT"/sample-asset.txt?canisterId="$ID" assert_contains "The requested URL returned error: 404" - commit_args='(record { batch_id = 2; evidence = blob "\1b\45\c8\b1\d0\de\ec\88\ac\03\25\90\e0\f1\cd\9a\b4\07\f7\96\e8\27\aa\c8\80\f4\ff\b0\35\fd\c2\00" } )' + commit_args='(record { batch_id = 2; evidence = blob "\9b\72\ee\e7\f0\d7\af\2a\9b\41\23\3c\34\1b\1c\aa\0c\90\5e\f9\14\05\f5\f5\13\ff\b5\8f\68\af\ee\5b" } )' assert_command dfx canister call e2e_project_frontend validate_commit_proposed_batch "$commit_args" --identity commit - assert_contains "commit proposed batch 2 with evidence 1b45c8b1d0deec88ac032590e0f1cd9ab407f796e827aac880f4ffb035fdc200" + assert_contains "commit proposed batch 2 with evidence 9b72eee7f0d7af2a9b41233c341b1caa0c905ef91405f5f513ffb58f68afee5b" assert_command dfx canister call e2e_project_frontend commit_proposed_batch "$commit_args" --identity commit assert_eq "()" @@ -694,724 +694,847 @@ check_permission_failure() { assert_command dfx deploy } +@test "can serve filenames with special characters in filename" { + # This is observed, not expected behavior + # see https://dfinity.atlassian.net/browse/SDK-1247 + install_asset assetscanister + + dfx_start + + echo "filename is an ae symbol" >'src/e2e_project_frontend/assets/æ' + + dfx deploy + + dfx canister call --query e2e_project_frontend list '(record {})' + + # decode as expected + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/%e6";headers=vec{};method="GET";body=vec{}})' + assert_match "filename is an ae symbol" # candid looks like blob "filename is \c3\a6\0a" + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + # fails with Err(InvalidExpressionPath) + assert_command_fail curl --fail -vv http://localhost:"$PORT"/%c3%a6?canisterId="$ID" + + # fails with Err(Utf8ConversionError(FromUtf8Error { bytes: [47, 230], error: Utf8Error { valid_up_to: 1, error_len: None } })) + assert_command_fail curl --fail -vv http://localhost:"$PORT"/%e6?canisterId="$ID" + # assert_match "200 OK" "$stderr" + # assert_match "filename is an ae symbol" + assert_contains "500 Internal Server Error" +} + @test "http_request percent-decodes urls" { - install_asset assetscanister - - dfx_start - - echo "contents of file with space in filename" >'src/e2e_project_frontend/assets/filename with space.txt' - echo "contents of file with plus in filename" >'src/e2e_project_frontend/assets/has+plus.txt' - echo "contents of file with percent in filename" >'src/e2e_project_frontend/assets/has%percent.txt' - echo "filename is an ae symbol" >'src/e2e_project_frontend/assets/æ' - echo "filename is percent symbol" >'src/e2e_project_frontend/assets/%' - echo "filename contains question mark" >'src/e2e_project_frontend/assets/filename?withqmark.txt' - dd if=/dev/urandom of='src/e2e_project_frontend/assets/large with spaces.bin' bs=2500000 count=1 - - - dfx deploy - - # decode as expected - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/filename%20with%20space.txt";headers=vec{};method="GET";body=vec{}})' - assert_match "contents of file with space in filename" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%2bplus.txt";headers=vec{};method="GET";body=vec{}})' - assert_match "contents of file with plus in filename" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%2Bplus.txt";headers=vec{};method="GET";body=vec{}})' - assert_match "contents of file with plus in filename" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%%percent.txt";headers=vec{};method="GET";body=vec{}})' - assert_match "contents of file with percent in filename" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/%e6";headers=vec{};method="GET";body=vec{}})' - assert_match "filename is an ae symbol" # candid looks like blob "filename is \c3\a6\0a" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/%%";headers=vec{};method="GET";body=vec{}})' - assert_match "filename is percent" - # this test ensures url decoding happens after removing the query string - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/filename%3fwithqmark.txt";headers=vec{};method="GET";body=vec{}})' - assert_match "filename contains question mark" - - # these error conditions can't be tested with curl, because something responds first with Bad Request. - # THESE TESTS WERE REMOVED BECAUSE THE RUST CANISTER DOES NOT SUPPORT REJECTING MESSAGES - # TODO: Reenable those tests. - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: % must be followed by '%' or two hex digits" - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%z";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: % must be followed by two hex digits, but only one was found" - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%zz";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: neither character after % is a hex digit" - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%e";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: % must be followed by two hex digits, but only one was found" - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%g6";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: first character after % is not a hex digit" - # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%ch";headers=vec{};method="GET";body=vec{}})' - # assert_match "error decoding url: second character after % is not a hex digit" - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - - assert_command curl --fail -vv http://localhost:"$PORT"/filename%20with%20space.txt?canisterId="$ID" - # shellcheck disable=SC2154 - assert_match "200 OK" "$stderr" - assert_match "contents of file with space in filename" - - assert_command curl --fail -vv http://localhost:"$PORT"/has%2bplus.txt?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "contents of file with plus in filename" - - assert_command curl --fail -vv http://localhost:"$PORT"/has%%percent.txt?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "contents of file with percent in filename" - - assert_command curl --fail -vv http://localhost:"$PORT"/%e6?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "filename is an ae symbol" - - assert_command curl --fail -vv http://localhost:"$PORT"/%%?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "filename is percent symbol" - - assert_command curl --fail -vv http://localhost:"$PORT"/filename%3fwithqmark.txt?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "filename contains question mark" - - assert_command curl --fail -vv --output lws-curl-output.bin "http://localhost:$PORT/large%20with%20spaces.bin?canisterId=$ID" - diff 'src/e2e_project_frontend/assets/large with spaces.bin' lws-curl-output.bin - - # curl now reports "curl: (3) URL using bad/illegal format or missing URL" so we cannot verify behavior - # assert_command_fail curl --fail -vv --path-as-is http://localhost:"$PORT"/'filename with space'.txt?canisterId="$ID" - # assert_match "400 Bad Request" "$stderr" + install_asset assetscanister + + dfx_start + + echo "contents of file with space in filename" >'src/e2e_project_frontend/assets/filename with space.txt' + echo "contents of file with plus in filename" >'src/e2e_project_frontend/assets/has+plus.txt' + echo "contents of file with percent in filename" >'src/e2e_project_frontend/assets/has%percent.txt' + echo "filename is an ae symbol" >'src/e2e_project_frontend/assets/æ' + echo "filename is percent symbol" >'src/e2e_project_frontend/assets/%' + echo "filename contains question mark" >'src/e2e_project_frontend/assets/filename?withqmark.txt' + dd if=/dev/urandom of='src/e2e_project_frontend/assets/large with spaces.bin' bs=2500000 count=1 + + + dfx deploy + + # decode as expected + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/filename%20with%20space.txt";headers=vec{};method="GET";body=vec{}})' + assert_match "contents of file with space in filename" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%2bplus.txt";headers=vec{};method="GET";body=vec{}})' + assert_match "contents of file with plus in filename" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%2Bplus.txt";headers=vec{};method="GET";body=vec{}})' + assert_match "contents of file with plus in filename" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/has%25percent.txt";headers=vec{};method="GET";body=vec{}})' + assert_match "contents of file with percent in filename" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/%e6";headers=vec{};method="GET";body=vec{}})' + assert_match "filename is an ae symbol" # candid looks like blob "filename is \c3\a6\0a" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/%25";headers=vec{};method="GET";body=vec{}})' + assert_match "filename is percent" + # this test ensures url decoding happens after removing the query string + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/filename%3fwithqmark.txt";headers=vec{};method="GET";body=vec{}})' + assert_match "filename contains question mark" + + # these error conditions can't be tested with curl, because something responds first with Bad Request. + # THESE TESTS WERE REMOVED BECAUSE THE RUST CANISTER DOES NOT SUPPORT REJECTING MESSAGES + # TODO: Reenable those tests. + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: % must be followed by '%' or two hex digits" + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%z";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: % must be followed by two hex digits, but only one was found" + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%zz";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: neither character after % is a hex digit" + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%e";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: % must be followed by two hex digits, but only one was found" + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%g6";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: first character after % is not a hex digit" + # assert_command_fail dfx canister call --query e2e_project_frontend http_request '(record{url="/%ch";headers=vec{};method="GET";body=vec{}})' + # assert_match "error decoding url: second character after % is not a hex digit" + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + assert_command curl --fail -vv http://localhost:"$PORT"/filename%20with%20space.txt?canisterId="$ID" + # shellcheck disable=SC2154 + assert_match "200 OK" "$stderr" + assert_match "contents of file with space in filename" + + assert_command curl --fail -vv http://localhost:"$PORT"/has%2bplus.txt?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "contents of file with plus in filename" + + assert_command curl --fail -vv http://localhost:"$PORT"/has%25percent.txt?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "contents of file with percent in filename" + + assert_command_fail curl --fail -vv http://localhost:"$PORT"/%e6?canisterId="$ID" + # see https://dfinity.atlassian.net/browse/SDK-1247 + # fails with Err(Utf8ConversionError(FromUtf8Error { bytes: [47, 230], error: Utf8Error { valid_up_to: 1, error_len: None } })) + assert_contains "500 Internal Server Error" + # assert_match "200 OK" "$stderr" + # assert_match "filename is an ae symbol" + + assert_command curl --fail -vv http://localhost:"$PORT"/%25?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "filename is percent symbol" + + assert_command curl --fail -vv http://localhost:"$PORT"/filename%3fwithqmark.txt?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "filename contains question mark" + + assert_command curl --fail -vv --output lws-curl-output.bin "http://localhost:$PORT/large%20with%20spaces.bin?canisterId=$ID" + diff 'src/e2e_project_frontend/assets/large with spaces.bin' lws-curl-output.bin + + # curl now reports "curl: (3) URL using bad/illegal format or missing URL" so we cannot verify behavior + # assert_command_fail curl --fail -vv --path-as-is http://localhost:"$PORT"/'filename with space'.txt?canisterId="$ID" + # assert_match "400 Bad Request" "$stderr" } @test "generates gzipped content encoding for .js files" { - install_asset assetscanister - for i in $(seq 1 400); do - echo "some easily duplicate text $i" >>src/e2e_project_frontend/assets/notreally.js - done - - dfx_start - assert_command dfx deploy - dfx canister call --query e2e_project_frontend list '(record{})' - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - - assert_command curl -v --output not-compressed http://localhost:"$PORT"/notreally.js?canisterId="$ID" - assert_not_match "content-encoding:" - diff not-compressed src/e2e_project_frontend/assets/notreally.js - - assert_command curl -v --output encoded-compressed-1.gz -H "Accept-Encoding: gzip" http://localhost:"$PORT"/notreally.js?canisterId="$ID" - assert_match "content-encoding: gzip" - gunzip encoded-compressed-1.gz - diff encoded-compressed-1 src/e2e_project_frontend/assets/notreally.js - - # should split up accept-encoding lines with more than one encoding - assert_command curl -v --output encoded-compressed-2.gz -H "Accept-Encoding: gzip, deflate, br" http://localhost:"$PORT"/notreally.js?canisterId="$ID" - assert_match "content-encoding: gzip" - gunzip encoded-compressed-2.gz - diff encoded-compressed-2 src/e2e_project_frontend/assets/notreally.js + install_asset assetscanister + for i in $(seq 1 400); do + echo "some easily duplicate text $i" >>src/e2e_project_frontend/assets/notreally.js + done + + dfx_start + assert_command dfx deploy + dfx canister call --query e2e_project_frontend list '(record{})' + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + assert_command curl -v --output not-compressed http://localhost:"$PORT"/notreally.js?canisterId="$ID" + assert_not_match "content-encoding:" + diff not-compressed src/e2e_project_frontend/assets/notreally.js + + assert_command curl -v --output encoded-compressed-1.gz -H "Accept-Encoding: gzip" http://localhost:"$PORT"/notreally.js?canisterId="$ID" + assert_match "content-encoding: gzip" + gunzip encoded-compressed-1.gz + diff encoded-compressed-1 src/e2e_project_frontend/assets/notreally.js + + # should split up accept-encoding lines with more than one encoding + assert_command curl -v --output encoded-compressed-2.gz -H "Accept-Encoding: gzip, deflate, br" http://localhost:"$PORT"/notreally.js?canisterId="$ID" + assert_match "content-encoding: gzip" + gunzip encoded-compressed-2.gz + diff encoded-compressed-2 src/e2e_project_frontend/assets/notreally.js } @test "leaves in place files that were already installed" { - install_asset assetscanister - dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset1.bin bs=400000 count=1 - dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset2.bin bs=400000 count=1 + install_asset assetscanister + dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset1.bin bs=400000 count=1 + dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset2.bin bs=400000 count=1 - dfx_start - assert_command dfx deploy + dfx_start + assert_command dfx deploy - assert_match '/asset1.bin 1/1' - assert_match '/asset2.bin 1/1' + assert_match '/asset1.bin 1/1' + assert_match '/asset2.bin 1/1' - dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset2.bin bs=400000 count=1 + dd if=/dev/urandom of=src/e2e_project_frontend/assets/asset2.bin bs=400000 count=1 - assert_command dfx deploy - assert_match '/asset1.bin.*is already installed' - assert_match '/asset2.bin 1/1' + assert_command dfx deploy + assert_match '/asset1.bin.*is already installed' + assert_match '/asset2.bin 1/1' } @test "unsets asset encodings that are removed from project" { - install_asset assetscanister + install_asset assetscanister - dfx_start - dfx deploy + dfx_start + dfx deploy - assert_command dfx canister call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="text/plain"; content_encoding="arbitrary"; content=blob "content encoded in another way!"})' + assert_command dfx canister call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="text/plain"; content_encoding="arbitrary"; content=blob "content encoded in another way!"})' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' - dfx deploy + dfx deploy - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' - assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' + assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' } @test "verifies sha256" { - install_asset assetscanister + install_asset assetscanister - dfx_start - dfx deploy + dfx_start + dfx deploy - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/text-with-newlines.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/text-with-newlines.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 243; 191; 114; 177; 83; 18; 144; 121; 131; 38; 109; 183; 89; 244; 120; 136; 53; 187; 14; 74; 8; 112; 86; 100; 115; 8; 179; 155; 69; 78; 95; 160; }})' - assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0})' - assert_match 'sha256 required' - assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 88; 87; 86; }})' - assert_match 'sha256 mismatch' + assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 243; 191; 114; 177; 83; 18; 144; 121; 131; 38; 109; 183; 89; 244; 120; 136; 53; 187; 14; 74; 8; 112; 86; 100; 115; 8; 179; 155; 69; 78; 95; 160; }})' + assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0})' + assert_match 'sha256 required' + assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 88; 87; 86; }})' + assert_match 'sha256 mismatch' - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 243; 191; 114; 177; 83; 18; 144; 121; 131; 38; 109; 183; 89; 244; 120; 136; 53; 187; 14; 74; 8; 112; 86; 100; 115; 8; 179; 155; 69; 78; 95; 160; }})' - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt blob "\f3\bf\72\b1\53\12\90\79\83\26\6d\b7\59\f4\78\88\35\bb\0e\4a\08\70\56\64\73\08\b3\9b\45\4e\5f\a0"})' - assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 88; 87; 86; }})' - assert_match 'sha256 mismatch' + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 243; 191; 114; 177; 83; 18; 144; 121; 131; 38; 109; 183; 89; 244; 120; 136; 53; 187; 14; 74; 8; 112; 86; 100; 115; 8; 179; 155; 69; 78; 95; 160; }})' + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt blob "\f3\bf\72\b1\53\12\90\79\83\26\6d\b7\59\f4\78\88\35\bb\0e\4a\08\70\56\64\73\08\b3\9b\45\4e\5f\a0"})' + assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback '(record{key="/text-with-newlines.txt";content_encoding="identity";index=0;sha256=opt vec { 88; 87; 86; }})' + assert_match 'sha256 mismatch' } @test "can store and retrieve assets by key" { - install_asset assetscanister + install_asset assetscanister - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_frontend + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_frontend - assert_command dfx canister call --query e2e_project_frontend retrieve '("/binary/noise.txt")' --output idl - assert_eq '(blob "\b8\01 \80\0aw12 \00xy\0aKL\0b\0ajk")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("/binary/noise.txt")' --output idl + assert_eq '(blob "\b8\01 \80\0aw12 \00xy\0aKL\0b\0ajk")' - assert_command dfx canister call --query e2e_project_frontend retrieve '("/text-with-newlines.txt")' --output idl - assert_eq '(blob "cherries\0ait\27s cherry season\0aCHERRIES")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("/text-with-newlines.txt")' --output idl + assert_eq '(blob "cherries\0ait\27s cherry season\0aCHERRIES")' - assert_command dfx canister call --update e2e_project_frontend store '(record{key="AA"; content_type="text/plain"; content_encoding="identity"; content=blob "hello, world!"})' - assert_eq '()' - assert_command dfx canister call --update e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' - assert_eq '()' + assert_command dfx canister call --update e2e_project_frontend store '(record{key="AA"; content_type="text/plain"; content_encoding="identity"; content=blob "hello, world!"})' + assert_eq '()' + assert_command dfx canister call --update e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' + assert_eq '()' - assert_command dfx canister call --query e2e_project_frontend retrieve '("B")' --output idl - assert_eq '(blob "XWV")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("B")' --output idl + assert_eq '(blob "XWV")' - assert_command dfx canister call --query e2e_project_frontend retrieve '("AA")' --output idl - assert_eq '(blob "hello, world!")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("AA")' --output idl + assert_eq '(blob "hello, world!")' - assert_command dfx canister call --query e2e_project_frontend retrieve '("B")' --output idl - assert_eq '(blob "XWV")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("B")' --output idl + assert_eq '(blob "XWV")' - assert_command_fail dfx canister call --query e2e_project_frontend retrieve '("C")' + assert_command_fail dfx canister call --query e2e_project_frontend retrieve '("C")' } @test "asset canister supports http requests" { - install_asset assetscanister - - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_frontend - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - assert_command curl http://localhost:"$PORT"/text-with-newlines.txt?canisterId="$ID" - # shellcheck disable=SC2154 - assert_eq "cherries + install_asset assetscanister + + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_frontend + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + assert_command curl http://localhost:"$PORT"/text-with-newlines.txt?canisterId="$ID" + # shellcheck disable=SC2154 + assert_eq "cherries it's cherry season CHERRIES" "$stdout" } @test 'can store arbitrarily large files' { - install_asset assetscanister + install_asset assetscanister - # make a big file with deterministic contents (a fixed hash) - for a in $(seq 1 20); do - echo "$a" >>src/e2e_project_frontend/assets/large-asset.bin - dd if=/dev/zero bs=300000 count=1 >> src/e2e_project_frontend/assets/large-asset.bin - done + # make a big file with deterministic contents (a fixed hash) + for a in $(seq 1 20); do + echo "$a" >>src/e2e_project_frontend/assets/large-asset.bin + dd if=/dev/zero bs=300000 count=1 >> src/e2e_project_frontend/assets/large-asset.bin + done - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_frontend + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_frontend - # retrieve() refuses to serve just part of an asset - assert_command_fail dfx canister call --query e2e_project_frontend retrieve '("/large-asset.bin")' - assert_match 'Asset too large.' + # retrieve() refuses to serve just part of an asset + assert_command_fail dfx canister call --query e2e_project_frontend retrieve '("/large-asset.bin")' + assert_match 'Asset too large.' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/large-asset.bin";accept_encodings=vec{"identity"}})' - assert_match 'total_length = 6_000_051' - assert_match 'content_type = "application/octet-stream"' - assert_match 'content_encoding = "identity"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/large-asset.bin";accept_encodings=vec{"identity"}})' + assert_match 'total_length = 6_000_051' + assert_match 'content_type = "application/octet-stream"' + assert_match 'content_encoding = "identity"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/large-asset.bin";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/large-asset.bin";accept_encodings=vec{"identity"}})' - assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=2;sha256=opt blob "\4f\a1\0f\f7\41\9f\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' - assert_contains "sha256 mismatch" + assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=2;sha256=opt blob "\4f\a1\0f\f7\41\9f\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' + assert_contains "sha256 mismatch" - assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=2;sha256=opt blob "\4f\a1\0f\f7\41\9c\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' + assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=2;sha256=opt blob "\4f\a1\0f\f7\41\9c\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' - assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=3;sha256=opt blob "\4f\a1\0f\f7\41\9c\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' - assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=4})' + assert_command dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=3;sha256=opt blob "\4f\a1\0f\f7\41\9c\0e\18\81\44\8f\d5\6e\2c\6a\a1\89\a8\f5\21\92\d4\87\f5\9b\4b\a2\3c\52\eb\e5\b7"})' + assert_command_fail dfx canister call --query e2e_project_frontend get_chunk '(record{key="/large-asset.bin";content_encoding="identity";index=4})' - PORT=$(get_webserver_port) - CANISTER_ID=$(dfx canister id e2e_project_frontend) - curl -v --output curl-output.bin "http://localhost:$PORT/large-asset.bin?canisterId=$CANISTER_ID" - diff src/e2e_project_frontend/assets/large-asset.bin curl-output.bin + PORT=$(get_webserver_port) + CANISTER_ID=$(dfx canister id e2e_project_frontend) + curl -v --output curl-output.bin "http://localhost:$PORT/large-asset.bin?canisterId=$CANISTER_ID" + diff src/e2e_project_frontend/assets/large-asset.bin curl-output.bin } @test "list() return assets" { - install_asset assetscanister - - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_frontend - - assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_match '"/binary/noise.txt"' - assert_match 'length = 19' - assert_match '"/text-with-newlines.txt"' - assert_match 'length = 36' - assert_match '"/sample-asset.txt"' - assert_match 'length = 24' + install_asset assetscanister + + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_frontend + + assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_match '"/binary/noise.txt"' + assert_match 'length = 19' + assert_match '"/text-with-newlines.txt"' + assert_match 'length = 36' + assert_match '"/sample-asset.txt"' + assert_match 'length = 24' } @test "identifies content type" { - install_asset assetscanister - - dfx_start - dfx canister create --all - - touch src/e2e_project_frontend/assets/index.html - touch src/e2e_project_frontend/assets/logo.png - touch src/e2e_project_frontend/assets/index.js - touch src/e2e_project_frontend/assets/main.css - touch src/e2e_project_frontend/assets/index.js.map - touch src/e2e_project_frontend/assets/index.js.LICENSE.txt - touch src/e2e_project_frontend/assets/index.js.LICENSE - - dfx build - dfx canister install e2e_project_frontend - - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.html";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "text/html"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/logo.png";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "image/png"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "application/javascript"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "text/plain"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/main.css";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "text/css"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.map";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "text/plain"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.LICENSE.txt";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "text/plain"' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.LICENSE";accept_encodings=vec{"identity"}})' - assert_match 'content_type = "application/octet-stream"' + install_asset assetscanister + + dfx_start + dfx canister create --all + + touch src/e2e_project_frontend/assets/index.html + touch src/e2e_project_frontend/assets/logo.png + touch src/e2e_project_frontend/assets/index.js + touch src/e2e_project_frontend/assets/main.css + touch src/e2e_project_frontend/assets/index.js.map + touch src/e2e_project_frontend/assets/index.js.LICENSE.txt + touch src/e2e_project_frontend/assets/index.js.LICENSE + + dfx build + dfx canister install e2e_project_frontend + + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.html";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "text/html"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/logo.png";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "image/png"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "application/javascript"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "text/plain"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/main.css";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "text/css"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.map";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "text/plain"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.LICENSE.txt";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "text/plain"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/index.js.LICENSE";accept_encodings=vec{"identity"}})' + assert_match 'content_type = "application/octet-stream"' } @test "deletes assets that are removed from project" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - touch src/e2e_project_frontend/assets/will-delete-this.txt - dfx deploy + touch src/e2e_project_frontend/assets/will-delete-this.txt + dfx deploy - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_match '"/will-delete-this.txt"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_match '"/will-delete-this.txt"' - rm src/e2e_project_frontend/assets/will-delete-this.txt - dfx deploy + rm src/e2e_project_frontend/assets/will-delete-this.txt + dfx deploy - assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_not_match '"/will-delete-this.txt"' + assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_not_match '"/will-delete-this.txt"' } @test "asset configuration via .ic-assets.json5" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - touch src/e2e_project_frontend/assets/ignored.txt - touch src/e2e_project_frontend/assets/index.html - touch src/e2e_project_frontend/assets/.hidden.txt + touch src/e2e_project_frontend/assets/ignored.txt + touch src/e2e_project_frontend/assets/index.html + touch src/e2e_project_frontend/assets/.hidden.txt - mkdir src/e2e_project_frontend/assets/.well-known - touch src/e2e_project_frontend/assets/.well-known/thing.json - touch src/e2e_project_frontend/assets/.well-known/file.txt + mkdir src/e2e_project_frontend/assets/.well-known + touch src/e2e_project_frontend/assets/.well-known/thing.json + touch src/e2e_project_frontend/assets/.well-known/file.txt - echo '[ - { - "match": "ignored.txt", - "ignore": true - }, - { - "match": "*", - "cache": { - "max_age": 500 - }, - "headers": { - "x-header": "x-value" - } - }, - { - "match": ".*", - "ignore": false, - "cache": { - "max_age": 888 - }, - "headers": { - "x-extra-header": "x-extra-value" - } + echo '[ + { + "match": "ignored.txt", + "ignore": true + }, + { + "match": "*", + "cache": { + "max_age": 500 }, - { - "match": "ignored.txt", - "ignore": true + "headers": { + "x-header": "x-value" } - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - echo '[ - { - "match": "*", - "headers": { - "x-well-known-header": "x-well-known-value" - } - }, - { - "match": "*.json", - "cache": { - "max_age": 1000 - } + }, + { + "match": ".*", + "ignore": false, + "cache": { + "max_age": 888 }, - { - "match": "file.txt", - "headers": null + "headers": { + "x-extra-header": "x-extra-value" + } + }, + { + "match": "ignored.txt", + "ignore": true + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + echo '[ + { + "match": "*", + "headers": { + "x-well-known-header": "x-well-known-value" } - ]' > src/e2e_project_frontend/assets/.well-known/.ic-assets.json5 - - dfx deploy - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - - assert_command curl --head "http://localhost:$PORT/.well-known/thing.json?canisterId=$ID" - assert_match "x-extra-header: x-extra-value" - assert_match "x-header: x-value" - assert_match "x-well-known-header: x-well-known-value" - assert_match "cache-control: max-age=1000" - - assert_command curl --head "http://localhost:$PORT/.well-known/file.txt?canisterId=$ID" - assert_match "cache-control: max-age=888" - assert_not_match "x-well-known-header: x-well-known-value" - assert_not_match "x-header: x-value" - assert_not_match "x-extra-header: x-extra-value" - - assert_command curl --head "http://localhost:$PORT/index.html?canisterId=$ID" - assert_match "cache-control: max-age=500" - assert_match "x-header: x-value" - assert_not_match "x-extra-header: x-extra-value" - - assert_command curl --head "http://localhost:$PORT/.hidden.txt?canisterId=$ID" - assert_match "cache-control: max-age=888" - assert_match "x-header: x-value" - assert_match "x-extra-header: x-extra-value" - - # assert_command curl -vv "http://localhost:$PORT/ignored.txt?canisterId=$ID" - # assert_match "404 Not Found" - # from logs: - # Staging contents of new and changed assets: - # /sample-asset.txt 1/1 (24 bytes) - # /text-with-newlines.txt 1/1 (36 bytes) - # /.well-known/file.txt 1/1 (0 bytes) - # /index.html 1/1 (0 bytes) - # /.hidden.txt 1/1 (0 bytes) - # /binary/noise.txt 1/1 (19 bytes) - # /.well-known/thing.json 1/1 (0 bytes) + }, + { + "match": "*.json", + "cache": { + "max_age": 1000 + } + }, + { + "match": "file.txt", + "headers": null + } + ]' > src/e2e_project_frontend/assets/.well-known/.ic-assets.json5 + + dfx deploy + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + assert_command curl --head "http://localhost:$PORT/.well-known/thing.json?canisterId=$ID" + assert_match "x-extra-header: x-extra-value" + assert_match "x-header: x-value" + assert_match "x-well-known-header: x-well-known-value" + assert_match "cache-control: max-age=1000" + + assert_command curl --head "http://localhost:$PORT/.well-known/file.txt?canisterId=$ID" + assert_match "cache-control: max-age=888" + assert_not_match "x-well-known-header: x-well-known-value" + assert_not_match "x-header: x-value" + assert_not_match "x-extra-header: x-extra-value" + + assert_command curl --head "http://localhost:$PORT/index.html?canisterId=$ID" + assert_match "cache-control: max-age=500" + assert_match "x-header: x-value" + assert_not_match "x-extra-header: x-extra-value" + + assert_command curl --head "http://localhost:$PORT/.hidden.txt?canisterId=$ID" + assert_match "cache-control: max-age=888" + assert_match "x-header: x-value" + assert_match "x-extra-header: x-extra-value" + + # assert_command curl -vv "http://localhost:$PORT/ignored.txt?canisterId=$ID" + # assert_match "404 Not Found" + # from logs: + # Staging contents of new and changed assets: + # /sample-asset.txt 1/1 (24 bytes) + # /text-with-newlines.txt 1/1 (36 bytes) + # /.well-known/file.txt 1/1 (0 bytes) + # /index.html 1/1 (0 bytes) + # /.hidden.txt 1/1 (0 bytes) + # /binary/noise.txt 1/1 (19 bytes) + # /.well-known/thing.json 1/1 (0 bytes) } @test "asset configuration via .ic-assets.json5 - nested dot directories" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - touch src/e2e_project_frontend/assets/thing.json - touch src/e2e_project_frontend/assets/.ignored-by-defualt.txt + touch src/e2e_project_frontend/assets/thing.json + touch src/e2e_project_frontend/assets/.ignored-by-defualt.txt - mkdir src/e2e_project_frontend/assets/.well-known - touch src/e2e_project_frontend/assets/.well-known/thing.json + mkdir src/e2e_project_frontend/assets/.well-known + touch src/e2e_project_frontend/assets/.well-known/thing.json - mkdir src/e2e_project_frontend/assets/.well-known/.hidden - touch src/e2e_project_frontend/assets/.well-known/.hidden/ignored.txt + mkdir src/e2e_project_frontend/assets/.well-known/.hidden + touch src/e2e_project_frontend/assets/.well-known/.hidden/ignored.txt - mkdir src/e2e_project_frontend/assets/.well-known/.another-hidden - touch src/e2e_project_frontend/assets/.well-known/.another-hidden/ignored.txt + mkdir src/e2e_project_frontend/assets/.well-known/.another-hidden + touch src/e2e_project_frontend/assets/.well-known/.another-hidden/ignored.txt - echo '[ - { - "match": ".well-known", - "ignore": false - }, - { - "match": "**/*", - "cache": { "max_age": 2000 } - } + echo '[ + { + "match": ".well-known", + "ignore": false + }, + { + "match": "**/*", + "cache": { "max_age": 2000 } + } ]' > src/e2e_project_frontend/assets/.ic-assets.json5 echo '[ - { - "match": "*", - "headers": { - "x-header": "x-value" - } - }, - { - "match": ".hidden", - "ignore": true - } - ]' > src/e2e_project_frontend/assets/.well-known/.ic-assets.json5 - echo '[ - { - "match": "*", - "ignore": false + { + "match": "*", + "headers": { + "x-header": "x-value" } + }, + { + "match": ".hidden", + "ignore": true + } + ]' > src/e2e_project_frontend/assets/.well-known/.ic-assets.json5 + echo '[ + { + "match": "*", + "ignore": false + } ]' > src/e2e_project_frontend/assets/.well-known/.hidden/.ic-assets.json5 echo '[ - { - "match": "*", - "ignore": false - } - ]' > src/e2e_project_frontend/assets/.well-known/.another-hidden/.ic-assets.json5 + { + "match": "*", + "ignore": false + } + ]' > src/e2e_project_frontend/assets/.well-known/.another-hidden/.ic-assets.json5 + + dfx deploy + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + assert_command curl --head "http://localhost:$PORT/thing.json?canisterId=$ID" + assert_match "cache-control: max-age=2000" + assert_command curl --head "http://localhost:$PORT/.well-known/thing.json?canisterId=$ID" + assert_match "cache-control: max-age=2000" + assert_match "x-header: x-value" + + assert_command curl -vv "http://localhost:$PORT/.ignored-by-defualt.txt?canisterId=$ID" + assert_match "404 Not Found" + assert_command curl -vv "http://localhost:$PORT/.well-known/.hidden/ignored.txt?canisterId=$ID" + assert_match "404 Not Found" + assert_command curl -vv "http://localhost:$PORT/.well-known/.another-hidden/ignored.txt?canisterId=$ID" + assert_match "404 Not Found" +} + +@test "asset configuration via .ic-assets.json5 - overwriting etag breaks certification" { + # this is observed behavior, not expected behavior + # https://dfinity.atlassian.net/browse/SDK-1245 + install_asset assetscanister + + dfx_start + + touch src/e2e_project_frontend/assets/thing.json - dfx deploy + dfx deploy + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + dfx canister call --query e2e_project_frontend http_request '(record{url="/thing.json";headers=vec{};method="GET";body=vec{}})' + assert_command curl --fail --head "http://localhost:$PORT/thing.json?canisterId=$ID" - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) + echo '[ + { + "match": "thing.json", + "headers": { + "etag": "my-etag" + } + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - assert_command curl --head "http://localhost:$PORT/thing.json?canisterId=$ID" - assert_match "cache-control: max-age=2000" - assert_command curl --head "http://localhost:$PORT/.well-known/thing.json?canisterId=$ID" - assert_match "cache-control: max-age=2000" - assert_match "x-header: x-value" + dfx deploy - assert_command curl -vv "http://localhost:$PORT/.ignored-by-defualt.txt?canisterId=$ID" - assert_match "404 Not Found" - assert_command curl -vv "http://localhost:$PORT/.well-known/.hidden/ignored.txt?canisterId=$ID" - assert_match "404 Not Found" - assert_command curl -vv "http://localhost:$PORT/.well-known/.another-hidden/ignored.txt?canisterId=$ID" - assert_match "404 Not Found" + dfx canister call --query e2e_project_frontend http_request '(record{url="/thing.json";headers=vec{};method="GET";body=vec{}})' + assert_command_fail curl --fail --head "http://localhost:$PORT/thing.json?canisterId=$ID" + assert_contains "500 Internal Server Error" } + @test "asset configuration via .ic-assets.json5 - overwriting default headers" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - touch src/e2e_project_frontend/assets/thing.json + touch src/e2e_project_frontend/assets/thing.json - echo '[ - { - "match": "thing.json", - "cache": { "max_age": 2000 }, - "headers": { - "Content-Encoding": "my-encoding", - "Content-Type": "x-type", - "Cache-Control": "custom", - "etag": "my-etag" - } + # this test used to also set etag, but that breaks certification + # see https://dfinity.atlassian.net/browse/SDK-1245 + echo '[ + { + "match": "thing.json", + "cache": { "max_age": 2000 }, + "headers": { + "Content-Encoding": "my-encoding", + "Content-Type": "x-type", + "Cache-Control": "custom" } - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - dfx deploy + dfx deploy - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) - assert_command curl --head "http://localhost:$PORT/thing.json?canisterId=$ID" - assert_match "cache-control: custom" - assert_match "content-encoding: my-encoding" - assert_match "content-type: x-type" - assert_not_match "etag: my-etag" - assert_match "etag: \"[a-z0-9]{64}\"" + dfx canister call --query e2e_project_frontend http_request '(record{url="/thing.json";headers=vec{};method="GET";body=vec{}})' + + assert_command curl --fail --head "http://localhost:$PORT/thing.json?canisterId=$ID" + assert_match "cache-control: custom" + assert_match "content-encoding: my-encoding" + assert_match "content-type: x-type" + # https://dfinity.atlassian.net/browse/SDK-1245 assert_not_match "etag: my-etag" + assert_match "etag: \"[a-z0-9]{64}\"" } @test "aliasing rules: to .html or /index.html" { - echo "test alias file" >'src/e2e_project_frontend/assets/test_alias_file.html' - mkdir 'src/e2e_project_frontend/assets/index_test' - echo "test index file" >'src/e2e_project_frontend/assets/index_test/index.html' - - TEST_ALIAS_SHA256="blob \"\67\fb\58\e3\ea\45\56\10\5d\d5\a4\08\0e\8d\38\6e\0c\5f\9b\f5\f5\05\dd\0f\4a\2b\d8\65\ec\27\c6\06\"" - TEST_INDEX_SHA256="blob \"\2c\0c\c1\2a\96\c6\79\8c\34\be\fd\8f\6f\df\ba\2f\39\57\8e\15\c0\f8\69\2f\54\da\df\06\ee\98\08\f5\"" - - dfx_start - dfx deploy - - # decode as expected - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file.html";headers=vec{};method="GET";body=vec{}})' - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file";headers=vec{};method="GET";body=vec{}})' - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/index_test";headers=vec{};method="GET";body=vec{}})' - assert_match "test index file" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" - assert_match "test index file" - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" - # shellcheck disable=SC2154 - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test index file" - assert_command curl --fail -vv http://localhost:"$PORT"/index_test/index?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test index file" - - # toggle aliasing on and off using `set_asset_properties` - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/test_alias_file.html"; is_aliased=opt(opt(false)) })' - assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "404" "$stderr" - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/test_alias_file.html"; is_aliased=opt(opt(true)) })' - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "200 OK" "$stderr" - - # redirect survives upgrade - assert_command dfx deploy --upgrade-unchanged - assert_match "is already installed" - - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" - # shellcheck disable=SC2154 - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test index file" - - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" - assert_match "test index file" - - # disabling redirect works - echo "DISABLING NOW" - echo '[ - { - "match": "test_alias_file.html", - "enable_aliasing": false - } - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - dfx deploy e2e_project_frontend - - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" - # shellcheck disable=SC2154 - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "404 Not Found" "$stderr" - assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test index file" - - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "key not found" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" - assert_match "test index file" - - # disabled redirect survives canister upgrade - echo "UPGRADE" - assert_command dfx deploy --upgrade-unchanged - - assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" - # shellcheck disable=SC2154 - assert_match "200 OK" "$stderr" - assert_match "test alias file" - assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" - assert_match "404 Not Found" "$stderr" - assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" - assert_match "200 OK" "$stderr" - assert_match "test index file" - - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "test alias file" - assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" - assert_match "key not found" - assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" - assert_match "test index file" - - # + echo "test alias file" >'src/e2e_project_frontend/assets/test_alias_file.html' + mkdir 'src/e2e_project_frontend/assets/index_test' + echo "test index file" >'src/e2e_project_frontend/assets/index_test/index.html' + + TEST_ALIAS_SHA256="blob \"\67\fb\58\e3\ea\45\56\10\5d\d5\a4\08\0e\8d\38\6e\0c\5f\9b\f5\f5\05\dd\0f\4a\2b\d8\65\ec\27\c6\06\"" + TEST_INDEX_SHA256="blob \"\2c\0c\c1\2a\96\c6\79\8c\34\be\fd\8f\6f\df\ba\2f\39\57\8e\15\c0\f8\69\2f\54\da\df\06\ee\98\08\f5\"" + + dfx_start + dfx deploy + + # decode as expected + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file.html";headers=vec{};method="GET";body=vec{}})' + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file";headers=vec{};method="GET";body=vec{}})' + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request '(record{url="/index_test";headers=vec{};method="GET";body=vec{}})' + assert_match "test index file" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" + assert_match "test index file" + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" + # shellcheck disable=SC2154 + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test index file" + assert_command curl --fail -vv http://localhost:"$PORT"/index_test/index?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test index file" + + # toggle aliasing on and off using `set_asset_properties` + # this doesn't work, see https://dfinity.atlassian.net/browse/SDK-1246 + dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file";headers=vec{};method="GET";body=vec{}})' + # output looks like this: + # ( + # record { + # body = blob "test alias file\0a"; + # headers = vec { + # record { + # "etag"; + # "\"67fb58e3ea4556105dd5a4080e8d386e0c5f9bf5f505dd0f4a2bd865ec27c606\""; + # }; + # record { "content-type"; "text/html" }; + # record { + # "IC-Certificate"; + # "certificate=:2dn3omR0cmVlgwGDAYMBgwJIY2FuaXN0ZXKDAYIEWCB6TamgcIBRRI9+Lfe6n1mQhfqXmFKDoOihriXzPhQ8YIMBgwJKgAAAAAAQAAIBAYMBgwGDAk5jZXJ0aWZpZWRfZGF0YYIDWCDclbSRbugItvQEwCwQDt2dGNnmSWXaDfyenwBDFGQev4IEWCDN7xKU70bQDnAGR8er6+SatQSi07lHyqye6YJDNdfzOYIEWCB326K/dXJismN6beQyTC9LeKBpzbOmOZPkRjpu4uIkt4IEWCCsjlsVa3dA4Inoj23KaUsHLZTOoZT/Uqzog9gmkovo34IEWCB9LBGrUHBg7qBYleBnVlVc672alvi9zWYf8D/2CKvHMoIEWCCMpFgCT+gbsEX08W0sB8h46ysJg+clCHOjW/qmKqWCYIMBggRYIIUC8ZdiVnT/LeyjAp84wEB9JBYPO1U4tZCYvNVw1eM3gwJEdGltZYIDScD5rc7w2aTEF2lzaWduYXR1cmVYMIQdy8BFvQjET2yjcKYQ47d62tiCtV4lfcjNiyDYWGM3FXOgjORzH5MnoSIY1HwJrA==:, tree=:2dn3gwGDAktodHRwX2Fzc2V0c4MBggRYINCuA0oUZ982kzKVIIrBrs5i693coETpgd/s0JEVMozsgwGDAlAvdGVzdF9hbGlhc19maWxlggNYIGf7WOPqRVYQXdWkCA6NOG4MX5v19QXdD0or2GXsJ8YGggRYICTVHJF+Vk5ccI2w7iQhhyiMkcffMLtaKScYnwtlBC+BggRYIFce2lClRDSsnfeLc7YS3j0JsELBY5a3bbUPh+luB7dD:"; + # }; + # }; + # streaming_strategy = null; + # status_code = 200 : nat16; + # }, + # ) + + + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/test_alias_file.html"; is_aliased=opt(opt(false)) })' + + dfx canister call --query e2e_project_frontend http_request '(record{url="/test_alias_file";headers=vec{};method="GET";body=vec{}})' + # output looks like this. Notice that the body and status code have changed, but the certificate is exactly the same. + # ( + # record { + # body = blob "not found"; + # headers = vec { + # record { "content-type"; "text/plain" }; + # record { + # "IC-Certificate"; + # "certificate=:2dn3omR0cmVlgwGDAYMBgwJIY2FuaXN0ZXKDAYIEWCB6TamgcIBRRI9+Lfe6n1mQhfqXmFKDoOihriXzPhQ8YIMBgwJKgAAAAAAQAAIBAYMBgwGDAk5jZXJ0aWZpZWRfZGF0YYIDWCDclbSRbugItvQEwCwQDt2dGNnmSWXaDfyenwBDFGQev4IEWCDN7xKU70bQDnAGR8er6+SatQSi07lHyqye6YJDNdfzOYIEWCB326K/dXJismN6beQyTC9LeKBpzbOmOZPkRjpu4uIkt4IEWCCsjlsVa3dA4Inoj23KaUsHLZTOoZT/Uqzog9gmkovo34IEWCCoBmzNJBeJUuPksGWjOP0JgQStmSri6XGg+//B8CS+PoIEWCApbis9cAc4Tv7tS97Vk+Dc14EE/styzggrRvkalYlD1YMBggRYIDG3uAnEPmC4tIPj0xDqUySASclS7unWnd6oG+IqIqGzgwJEdGltZYIDSaiiuufz2aTEF2lzaWduYXR1cmVYMLMbF+o1s///LvhWJ4sylt9/07OokAlX8L6+Dfd8L2KDUGgCzuPvmIy/3NC0+LMpEw==:, tree=:2dn3gwGDAktodHRwX2Fzc2V0c4MBggRYINCuA0oUZ982kzKVIIrBrs5i693coETpgd/s0JEVMozsgwGDAlAvdGVzdF9hbGlhc19maWxlggNYIGf7WOPqRVYQXdWkCA6NOG4MX5v19QXdD0or2GXsJ8YGggRYICTVHJF+Vk5ccI2w7iQhhyiMkcffMLtaKScYnwtlBC+BggRYIFce2lClRDSsnfeLc7YS3j0JsELBY5a3bbUPh+luB7dD:"; + # }; + # }; + # streaming_strategy = null; + # status_code = 404 : nat16; + # }, + # ) + + assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + + # this should succeed, with output 404, like so: + # assert_match "404" "$stderr" + + # However, due to returning the wrong certificate, it fails with Err(InvalidResponseHashes) + # see https://dfinity.atlassian.net/browse/SDK-1246 + assert_contains "500 Internal Server Error" + + + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/test_alias_file.html"; is_aliased=opt(opt(true)) })' + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + assert_match "200 OK" "$stderr" + + # redirect survives upgrade + assert_command dfx deploy --upgrade-unchanged + assert_match "is already installed" + + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" + # shellcheck disable=SC2154 + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test index file" + + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" + assert_match "test index file" + + # disabling redirect works + echo "DISABLING NOW" + echo '[ + { + "match": "test_alias_file.html", + "enable_aliasing": false + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + dfx deploy e2e_project_frontend + + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" + # shellcheck disable=SC2154 + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + + # again see # see https://dfinity.atlassian.net/browse/SDK-1246, this should be 404 + # assert_match "404 Not Found" "$stderr" + assert_contains "500 Internal Server Error" + + assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test index file" + + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "key not found" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" + assert_match "test index file" + + # disabled redirect survives canister upgrade + echo "UPGRADE" + assert_command dfx deploy --upgrade-unchanged + + assert_command curl --fail -vv http://localhost:"$PORT"/test_alias_file.html?canisterId="$ID" + # shellcheck disable=SC2154 + assert_match "200 OK" "$stderr" + assert_match "test alias file" + assert_command_fail curl --fail -vv http://localhost:"$PORT"/test_alias_file?canisterId="$ID" + assert_match "404 Not Found" "$stderr" + assert_command curl --fail -vv http://localhost:"$PORT"/index_test?canisterId="$ID" + assert_match "200 OK" "$stderr" + assert_match "test index file" + + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file.html\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "test alias file" + assert_command_fail dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/test_alias_file\";content_encoding=\"identity\";index=0;sha256=opt $TEST_ALIAS_SHA256})" + assert_match "key not found" + assert_command dfx canister call --query e2e_project_frontend http_request_streaming_callback "(record{key=\"/index_test\";content_encoding=\"identity\";index=0;sha256=opt $TEST_INDEX_SHA256})" + assert_match "test index file" + + # } @test "asset configuration via .ic-assets.json5 - detect unused config" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - mkdir src/e2e_project_frontend/assets/somedir - touch src/e2e_project_frontend/assets/somedir/upload-me.txt - echo '[ - { - "match": "nevermatchme", - "cache": { "max_age": 2000 } - } - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - echo '[ - { - "match": "upload-me.txt", - "headers": { "key": "value" } - }, - { - "match": "nevermatchme", - "headers": {}, - "ignore": false - }, - { - "match": "nevermatchmetoo", - "headers": null, - "ignore": false - }, - { - "match": "non-matcher", - "headers": {"x-header": "x-value"}, - "ignore": false - }, - { - "match": "/thanks-for-not-stripping-forward-slash", - "headers": {"x-header": "x-value"}, - "ignore": false - } - ]' > src/e2e_project_frontend/assets/somedir/.ic-assets.json5 + mkdir src/e2e_project_frontend/assets/somedir + touch src/e2e_project_frontend/assets/somedir/upload-me.txt + echo '[ + { + "match": "nevermatchme", + "cache": { "max_age": 2000 } + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + echo '[ + { + "match": "upload-me.txt", + "headers": { "key": "value" } + }, + { + "match": "nevermatchme", + "headers": {}, + "ignore": false + }, + { + "match": "nevermatchmetoo", + "headers": null, + "ignore": false + }, + { + "match": "non-matcher", + "headers": {"x-header": "x-value"}, + "ignore": false + }, + { + "match": "/thanks-for-not-stripping-forward-slash", + "headers": {"x-header": "x-value"}, + "ignore": false + } + ]' > src/e2e_project_frontend/assets/somedir/.ic-assets.json5 - assert_command dfx deploy - assert_match 'WARN: 1 unmatched configuration in .*/src/e2e_project_frontend/assets/.ic-assets.json config file:' - assert_contains 'WARN: { + assert_command dfx deploy + assert_match 'WARN: 1 unmatched configuration in .*/src/e2e_project_frontend/assets/.ic-assets.json config file:' + assert_contains 'WARN: { "match": "nevermatchme", "cache": { "max_age": 2000 }, - "allow_raw_access": false + "allow_raw_access": true }' - assert_match 'WARN: 4 unmatched configurations in .*/src/e2e_project_frontend/assets/somedir/.ic-assets.json config file:' - assert_contains 'WARN: { + assert_match 'WARN: 4 unmatched configurations in .*/src/e2e_project_frontend/assets/somedir/.ic-assets.json config file:' + assert_contains 'WARN: { "match": "nevermatchme", "headers": {}, "ignore": false, - "allow_raw_access": false + "allow_raw_access": true } WARN: { "match": "nevermatchmetoo", "headers": {}, "ignore": false, - "allow_raw_access": false + "allow_raw_access": true } WARN: { "match": "non-matcher", @@ -1419,86 +1542,86 @@ WARN: { "x-header": "x-value" }, "ignore": false, - "allow_raw_access": false + "allow_raw_access": true }' - # splitting this up into two checks, because the order is different on macos vs ubuntu - assert_contains 'WARN: { + # splitting this up into two checks, because the order is different on macos vs ubuntu + assert_contains 'WARN: { "match": "/thanks-for-not-stripping-forward-slash", "headers": { "x-header": "x-value" }, "ignore": false, - "allow_raw_access": false + "allow_raw_access": true }' } @test "asset configuration via .ic-assets.json5 - get and set asset properties" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - mkdir src/e2e_project_frontend/assets/somedir - touch src/e2e_project_frontend/assets/somedir/upload-me.txt - echo '[ - { - "match": "**/*", - "cache": { "max_age": 2000 }, - "headers": { "x-key": "x-value" }, - "enable_aliasing": true - } - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + mkdir src/e2e_project_frontend/assets/somedir + touch src/e2e_project_frontend/assets/somedir/upload-me.txt + echo '[ + { + "match": "**/*", + "cache": { "max_age": 2000 }, + "headers": { "x-key": "x-value" }, + "enable_aliasing": true + } + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - dfx deploy + dfx deploy - # read properties - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # read properties + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = opt vec { record { "x-key"; "x-value" } }; is_aliased = opt true; - allow_raw_access = opt false; + allow_raw_access = opt true; max_age = opt (2_000 : nat64); }, )' - # access required to update - assert_command_fail dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' --identity anonymous - assert_match "Caller does not have Commit permission" - dfx identity new other --storage-mode plaintext - assert_command_fail dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' --identity other - assert_match "Caller does not have Commit permission" - - # set max_age property and read it back - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' - assert_contains '()' - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # access required to update + assert_command_fail dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' --identity anonymous + assert_match "Caller does not have Commit permission" + dfx identity new other --storage-mode plaintext + assert_command_fail dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' --identity other + assert_match "Caller does not have Commit permission" + + # set max_age property and read it back + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; max_age=opt(opt(5:nat64)) })' + assert_contains '()' + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = opt vec { record { "x-key"; "x-value" } }; is_aliased = opt true; - allow_raw_access = opt false; + allow_raw_access = opt true; max_age = opt (5 : nat64); }, )' - # set headers property and read it back - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; headers=opt(opt(vec{record {"new-key"; "new-value"}}))})' - assert_contains '()' - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # set headers property and read it back + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; headers=opt(opt(vec{record {"new-key"; "new-value"}}))})' + assert_contains '()' + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = opt vec { record { "new-key"; "new-value" } }; is_aliased = opt true; - allow_raw_access = opt false; + allow_raw_access = opt true; max_age = opt (5 : nat64); }, )' - # set allow_raw_access property and read it back - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; allow_raw_access=opt(opt(true))})' - assert_contains '()' - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # set allow_raw_access property and read it back + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; allow_raw_access=opt(opt(true))})' + assert_contains '()' + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = opt vec { record { "new-key"; "new-value" } }; is_aliased = opt true; @@ -1507,11 +1630,11 @@ WARN: { }, )' - # set is_aliased property and read it back - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; is_aliased=opt(opt(false))})' - assert_contains '()' - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # set is_aliased property and read it back + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; is_aliased=opt(opt(false))})' + assert_contains '()' + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = opt vec { record { "new-key"; "new-value" } }; is_aliased = opt false; @@ -1520,11 +1643,11 @@ WARN: { }, )' - # set all properties to None and read them back - assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; headers=opt(null); max_age=opt(null); allow_raw_access=opt(null); is_aliased=opt(null)})' - assert_contains '()' - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' - assert_contains '( + # set all properties to None and read them back + assert_command dfx canister call e2e_project_frontend set_asset_properties '( record { key="/somedir/upload-me.txt"; headers=opt(null); max_age=opt(null); allow_raw_access=opt(null); is_aliased=opt(null)})' + assert_contains '()' + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/somedir/upload-me.txt")' + assert_contains '( record { headers = null; is_aliased = null; @@ -1535,75 +1658,75 @@ WARN: { } @test "asset configuration via .ic-assets.json5 - pretty printing when deploying" { - install_asset assetscanister + install_asset assetscanister - dfx_start + dfx_start - mkdir src/e2e_project_frontend/assets/somedir - echo "content" > src/e2e_project_frontend/assets/somedir/upload-me.txt - echo '[ - { - "match": "**/*", - "cache": { "max_age": 2000 }, - "headers": { - "x-header": "x-value" - }, - "enable_aliasing": true + mkdir src/e2e_project_frontend/assets/somedir + echo "content" > src/e2e_project_frontend/assets/somedir/upload-me.txt + echo '[ + { + "match": "**/*", + "cache": { "max_age": 2000 }, + "headers": { + "x-header": "x-value" }, - ]' > src/e2e_project_frontend/assets/somedir/.ic-assets.json5 + "enable_aliasing": true + }, + ]' > src/e2e_project_frontend/assets/somedir/.ic-assets.json5 - assert_command dfx deploy - assert_match '/somedir/upload-me.txt 1/1 \(8 bytes\) sha [0-9a-z]* \(with cache and 1 header\)' + assert_command dfx deploy + assert_match '/somedir/upload-me.txt 1/1 \(8 bytes\) sha [0-9a-z]* \(with cache and 1 header\)' } @test "uses selected canister wasm" { - dfx_start - use_asset_wasm 0.12.1 - assert_command dfx deploy - assert_command dfx canister info e2e_project_frontend - assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 - use_default_asset_wasm - assert_command dfx deploy - assert_command dfx canister info e2e_project_frontend - assert_not_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 + dfx_start + use_asset_wasm 0.12.1 + assert_command dfx deploy + assert_command dfx canister info e2e_project_frontend + assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 + use_default_asset_wasm + assert_command dfx deploy + assert_command dfx canister info e2e_project_frontend + assert_not_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 } @test "api version endpoint" { - install_asset assetscanister - dfx_start - assert_command dfx deploy - assert_command dfx canister call e2e_project_frontend api_version '()' - assert_match '\([0-9]* : nat16\)' + install_asset assetscanister + dfx_start + assert_command dfx deploy + assert_command dfx canister call e2e_project_frontend api_version '()' + assert_match '\([0-9]* : nat16\)' } @test "syncs asset properties when redeploying" { - install_asset assetscanister - dfx_start - assert_command dfx deploy - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/text-with-newlines.txt")' - assert_contains '( + install_asset assetscanister + dfx_start + assert_command dfx deploy + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/text-with-newlines.txt")' + assert_contains '( record { headers = null; is_aliased = null; - allow_raw_access = opt false; + allow_raw_access = opt true; max_age = null; }, )' - echo '[ - { - "match": "**/*", - "cache": { "max_age": 2000 }, - "headers": { - "x-header": "x-value" - }, - "allow_raw_access": true, - "enable_aliasing": false + echo '[ + { + "match": "**/*", + "cache": { "max_age": 2000 }, + "headers": { + "x-header": "x-value" }, - ]' > src/e2e_project_frontend/assets/.ic-assets.json5 - assert_command dfx deploy - assert_command dfx canister call e2e_project_frontend get_asset_properties '("/text-with-newlines.txt")' - assert_contains '( + "allow_raw_access": true, + "enable_aliasing": false + }, + ]' > src/e2e_project_frontend/assets/.ic-assets.json5 + assert_command dfx deploy + assert_command dfx canister call e2e_project_frontend get_asset_properties '("/text-with-newlines.txt")' + assert_contains '( record { headers = opt vec { record { "x-header"; "x-value" } }; is_aliased = opt false; diff --git a/e2e/tests-dfx/base.bash b/e2e/tests-dfx/base.bash index 274a1e607c..5903d4244f 100644 --- a/e2e/tests-dfx/base.bash +++ b/e2e/tests-dfx/base.bash @@ -3,38 +3,38 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "provides base library location by default" { - install_asset base + install_asset base - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_backend + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_backend - assert_command dfx canister call --query e2e_project_backend is_digit '("5")' - assert_eq '(true)' + assert_command dfx canister call --query e2e_project_backend is_digit '("5")' + assert_eq '(true)' - assert_command dfx canister call --query e2e_project_backend is_digit '("w")' - assert_eq '(false)' + assert_command dfx canister call --query e2e_project_backend is_digit '("w")' + assert_eq '(false)' } @test "does not provide base library if there is a packtool" { - install_asset base - jq '.defaults.build.packtool="echo"' dfx.json | sponge dfx.json + install_asset base + jq '.defaults.build.packtool="echo"' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match 'import error \[M0010\], package "base" not defined' + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match 'import error \[M0010\], package "base" not defined' } diff --git a/e2e/tests-dfx/basic-project.bash b/e2e/tests-dfx/basic-project.bash index f298c29947..6dd4ab76f8 100644 --- a/e2e/tests-dfx/basic-project.bash +++ b/e2e/tests-dfx/basic-project.bash @@ -3,48 +3,48 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "build + install + call + request-status -- greet_mo" { - dfx_new hello - install_asset greet - dfx_start - dfx canister create --all - dfx build - # INSTALL_REQUEST_ID=$(dfx canister install hello_backend --async) - # dfx canister request-status $INSTALL_REQUEST_ID - dfx canister install hello_backend - - assert_command dfx canister call hello_backend greet '("Banzai")' - assert_eq '("Hello, Banzai!")' - - assert_command dfx canister call hello_backend greet --type raw '4449444c00017103e29883' - assert_eq '("Hello, ☃!")' - - assert_command dfx canister call --query hello_backend greet '("Bongalo")' - assert_eq '("Hello, Bongalo!")' - - # Using call --async and request-status. - # Call with user Identity as Sender - assert_command dfx canister call --async hello_backend greet Blueberry - # At this point $output is the request ID. - # shellcheck disable=SC2154 - assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" - assert_eq '("Hello, Blueberry!")' - - # Call using the wallet's call forwarding - assert_command dfx canister call --async hello_backend greet Blueberry --wallet="$(dfx identity get-wallet)" - # At this point $output is the request ID. - # shellcheck disable=SC2154 - assert_command dfx canister request-status "$stdout" "$(dfx identity get-wallet)" - assert_eq \ + dfx_new hello + install_asset greet + dfx_start + dfx canister create --all + dfx build + # INSTALL_REQUEST_ID=$(dfx canister install hello_backend --async) + # dfx canister request-status $INSTALL_REQUEST_ID + dfx canister install hello_backend + + assert_command dfx canister call hello_backend greet '("Banzai")' + assert_eq '("Hello, Banzai!")' + + assert_command dfx canister call hello_backend greet --type raw '4449444c00017103e29883' + assert_eq '("Hello, ☃!")' + + assert_command dfx canister call --query hello_backend greet '("Bongalo")' + assert_eq '("Hello, Bongalo!")' + + # Using call --async and request-status. + # Call with user Identity as Sender + assert_command dfx canister call --async hello_backend greet Blueberry + # At this point $output is the request ID. + # shellcheck disable=SC2154 + assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" + assert_eq '("Hello, Blueberry!")' + + # Call using the wallet's call forwarding + assert_command dfx canister call --async hello_backend greet Blueberry --wallet="$(dfx identity get-wallet)" + # At this point $output is the request ID. + # shellcheck disable=SC2154 + assert_command dfx canister request-status "$stdout" "$(dfx identity get-wallet)" + assert_eq \ '( variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01q\11Hello, Blueberry!" } @@ -53,79 +53,79 @@ teardown() { } @test "build + install + call + request-status -- counter_mo" { - dfx_new hello - install_asset counter - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend + dfx_new hello + install_asset counter + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend - assert_command dfx canister call hello_backend read - assert_eq "(0 : nat)" + assert_command dfx canister call hello_backend read + assert_eq "(0 : nat)" - assert_command dfx canister call hello_backend inc - assert_eq "()" + assert_command dfx canister call hello_backend inc + assert_eq "()" - assert_command dfx canister call hello_backend read - assert_eq "(1 : nat)" + assert_command dfx canister call hello_backend read + assert_eq "(1 : nat)" - dfx canister call hello_backend inc - assert_command dfx canister call hello_backend read - assert_eq "(2 : nat)" + dfx canister call hello_backend inc + assert_command dfx canister call hello_backend read + assert_eq "(2 : nat)" - assert_command dfx canister call hello_backend read --output raw - assert_eq "4449444c00017d02" + assert_command dfx canister call hello_backend read --output raw + assert_eq "4449444c00017d02" - assert_command_fail dfx canister call --query hello_backend inc - assert_match "Not a query method." + assert_command_fail dfx canister call --query hello_backend inc + assert_match "Not a query method." - dfx canister call hello_backend inc - assert_command dfx canister call --query hello_backend read - assert_eq "(3 : nat)" + dfx canister call hello_backend inc + assert_command dfx canister call --query hello_backend read + assert_eq "(3 : nat)" - assert_command dfx canister call hello_backend inc --async - assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" + assert_command dfx canister call hello_backend inc --async + assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" - # Call write. - assert_command dfx canister call hello_backend write 1337 - assert_eq "()" + # Call write. + assert_command dfx canister call hello_backend write 1337 + assert_eq "()" - # Write has no return value. But we can _call_ read too. - # Call with user Identity as Sender - assert_command dfx canister call hello_backend read --async - assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" - assert_eq "(1_337 : nat)" + # Write has no return value. But we can _call_ read too. + # Call with user Identity as Sender + assert_command dfx canister call hello_backend read --async + assert_command dfx canister request-status "$stdout" "$(dfx canister id hello_backend)" + assert_eq "(1_337 : nat)" - # Call using the wallet's call forwarding - assert_command dfx canister call hello_backend read --async --wallet="$(dfx identity get-wallet)" - assert_command dfx canister request-status "$stdout" "$(dfx identity get-wallet)" - assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01}\b9\0a" } })' + # Call using the wallet's call forwarding + assert_command dfx canister call hello_backend read --async --wallet="$(dfx identity get-wallet)" + assert_command dfx canister request-status "$stdout" "$(dfx identity get-wallet)" + assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01}\b9\0a" } })' } @test "build + install + call -- counter_idl_mo" { - dfx_new hello - install_asset counter_idl - dfx_start - dfx canister create --all - dfx build - dfx canister install --all - - assert_command dfx canister call hello_backend inc '(42,false,"testzZ",vec{1;2;3},opt record{head=42; tail=opt record{head=+43; tail=null}}, variant { cons=record{ 42; variant { cons=record{43; variant { nil }} } } })' --output idl - assert_eq "(43 : int, true, \"uftu{[\", vec { 2 : nat; 3 : nat; 4 : nat;}, opt record { head = 43 : int; tail = opt record { head = 44 : int; tail = null;};}, variant { cons = record { 43 : int; variant { cons = record { 44 : int; variant { nil };} };} })" + dfx_new hello + install_asset counter_idl + dfx_start + dfx canister create --all + dfx build + dfx canister install --all + + assert_command dfx canister call hello_backend inc '(42,false,"testzZ",vec{1;2;3},opt record{head=42; tail=opt record{head=+43; tail=null}}, variant { cons=record{ 42; variant { cons=record{43; variant { nil }} } } })' --output idl + assert_eq "(43 : int, true, \"uftu{[\", vec { 2 : nat; 3 : nat; 4 : nat;}, opt record { head = 43 : int; tail = opt record { head = 44 : int; tail = null;};}, variant { cons = record { 43 : int; variant { cons = record { 44 : int; variant { nil };} };} })" } @test "build + install + call -- matrix_multiply_mo" { - dfx_new hello - install_asset matrix_multiply - dfx_start - dfx canister create --all - dfx build - dfx canister install --all - - assert_command dfx canister call hello_backend multiply '(vec{vec{1;2};vec{3;4};vec{5;6}},vec{vec{1;2;3};vec{4;5;6}})' - assert_eq \ + dfx_new hello + install_asset matrix_multiply + dfx_start + dfx canister create --all + dfx build + dfx canister install --all + + assert_command dfx canister call hello_backend multiply '(vec{vec{1;2};vec{3;4};vec{5;6}},vec{vec{1;2;3};vec{4;5;6}})' + assert_eq \ "( vec { vec { 9 : int; 12 : int; 15 : int }; @@ -136,27 +136,27 @@ teardown() { } @test "inspect message - motoko" { - dfx_new hello - install_asset inspect_message - dfx_start - dfx deploy + dfx_new hello + install_asset inspect_message + dfx_start + dfx deploy - assert_command dfx canister call hello_backend always_accepted + assert_command dfx canister call hello_backend always_accepted - assert_command_fail dfx canister call hello_backend always_rejected - assert_contains "canister_inspect_message explicitly refused message" + assert_command_fail dfx canister call hello_backend always_rejected + assert_contains "canister_inspect_message explicitly refused message" } @test "inspect message - rust" { - dfx_new_rust hello + dfx_new_rust hello - install_asset inspect_message_rs + install_asset inspect_message_rs - dfx_start - dfx deploy + dfx_start + dfx deploy - assert_command dfx canister call hello_backend always_accepted + assert_command dfx canister call hello_backend always_accepted - assert_command_fail dfx canister call hello_backend always_rejected - assert_contains "Canister $(dfx canister id hello_backend) rejected the message" + assert_command_fail dfx canister call hello_backend always_rejected + assert_contains "Canister $(dfx canister id hello_backend) rejected the message" } diff --git a/e2e/tests-dfx/bitcoin.bash b/e2e/tests-dfx/bitcoin.bash index 08048c2da5..58dbe31a2d 100644 --- a/e2e/tests-dfx/bitcoin.bash +++ b/e2e/tests-dfx/bitcoin.bash @@ -2,264 +2,153 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - BITCOIN_CANISTER_ID="g4xu7-jiaaa-aaaan-aaaaq-cai" setup() { - standard_setup + standard_setup - bitcoind -regtest -daemonwait + bitcoind -regtest -daemonwait } teardown() { - bitcoin-cli -regtest stop + bitcoin-cli -regtest stop - dfx_stop - # stop_dfx_replica - # stop_dfx_bootstrap - standard_teardown + dfx_stop + standard_teardown } set_project_default_bitcoin_enabled() { - jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json + jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json } set_shared_local_network_bitcoin_enabled() { - create_networks_json - jq '.local.bitcoin.enabled=true' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + create_networks_json + jq '.local.bitcoin.enabled=true' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" } set_local_network_bitcoin_enabled() { - jq '.networks.local.bitcoin.enabled=true' dfx.json | sponge dfx.json + jq '.networks.local.bitcoin.enabled=true' dfx.json | sponge dfx.json } @test "noop" { - assert_command bitcoin-cli -regtest createwallet "test" - ADDRESS="$(bitcoin-cli -regtest getnewaddress)" - assert_command bitcoin-cli -regtest generatetoaddress 101 "$ADDRESS" + assert_command bitcoin-cli -regtest createwallet "test" + ADDRESS="$(bitcoin-cli -regtest getnewaddress)" + assert_command bitcoin-cli -regtest generatetoaddress 101 "$ADDRESS" } @test "dfx restarts replica when ic-btc-adapter restarts" { - dfx_new hello - dfx_start --enable-bitcoin - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' - - REPLICA_PID=$(get_replica_pid) - BTC_ADAPTER_PID=$(get_btc_adapter_pid) - - echo "replica pid is $REPLICA_PID" - echo "ic-btc-adapter pid is $BTC_ADAPTER_PID" - - kill -KILL "$BTC_ADAPTER_PID" - assert_process_exits "$BTC_ADAPTER_PID" 15s - assert_process_exits "$REPLICA_PID" 15s - - timeout 15s sh -c \ - 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ - || (echo "replica did not restart" && ps aux && exit 1) - wait_until_replica_healthy - - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - # even after the above, still sometimes fails with - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' - - ID=$(dfx canister id hello_frontend) - - timeout 15s sh -c \ - "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ - || (echo "icx-proxy did not restart" && ps aux && exit 1) - - assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" -} - -@test "dfx restarts replica when ic-btc-adapter restarts - replica and bootstrap" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - dfx_replica --enable-bitcoin - dfx_bootstrap - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' - - REPLICA_PID=$(get_replica_pid) - BTC_ADAPTER_PID=$(get_btc_adapter_pid) - - echo "replica pid is $REPLICA_PID" - echo "replica port is $(get_replica_port)" - echo "ic-btc-adapter pid is $BTC_ADAPTER_PID" - - echo "killing btc adapter" - - kill -KILL "$BTC_ADAPTER_PID" - assert_process_exits "$BTC_ADAPTER_PID" 15s - assert_process_exits "$REPLICA_PID" 15s - - timeout 15s sh -x -c \ - "until curl --fail --verbose -o /dev/null http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port\")/api/v2/status; do echo \"waiting for replica to restart on port \$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port\")\"; sleep 1; done" \ - || (echo "replica did not restart" && echo "last replica port was $(get_replica_port)" && ps aux && exit 1) - - # bootstrap doesn't detect the new replica port, so we have to restart it - stop_dfx_bootstrap - dfx_bootstrap - - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - # even after the above, still sometimes fails with - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' -} - -@test "dfx start --bitcoin-node implies --enable-bitcoin" { - dfx_new hello - dfx_start "--bitcoin-node" "127.0.0.1:18444" - - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" -} + dfx_new hello + dfx_start --enable-bitcoin -@test "dfx replica --bitcoin-node implies --enable-bitcoin" { - skip "dfx replica and bootstrap are deprecated" + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' - dfx_new hello - dfx_replica "--bitcoin-node" "127.0.0.1:18444" - dfx_bootstrap + REPLICA_PID=$(get_replica_pid) + BTC_ADAPTER_PID=$(get_btc_adapter_pid) - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" -} + echo "replica pid is $REPLICA_PID" + echo "ic-btc-adapter pid is $BTC_ADAPTER_PID" + kill -KILL "$BTC_ADAPTER_PID" + assert_process_exits "$BTC_ADAPTER_PID" 15s + assert_process_exits "$REPLICA_PID" 15s -@test "dfx start --enable-bitcoin with no other configuration succeeds" { - dfx_new hello + timeout 15s sh -c \ + 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ + || (echo "replica did not restart" && ps aux && exit 1) + wait_until_replica_healthy - dfx_start --enable-bitcoin + # Sometimes initially get an error like: + # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module + # but the condition clears. + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting + # even after the above, still sometimes fails with + # IC0515: Certified state is not available yet. Please try again... + sleep 10 + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" -} + assert_command dfx canister call hello_backend greet '("Omega")' + assert_eq '("Hello, Omega!")' -@test "dfx start --enable-bitcoin --background waits until bitcoin canister is installed" { - dfx_new hello + ID=$(dfx canister id hello_frontend) - dfx_start --enable-bitcoin + timeout 15s sh -c \ + "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ + || (echo "icx-proxy did not restart" && ps aux && exit 1) - assert_command dfx canister info "$BITCOIN_CANISTER_ID" - assert_contains "Controllers: 2vxsx-fae" - assert_contains "Module hash: 0x" + assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" } -@test "dfx replica --enable-bitcoin with no other configuration succeeds" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - - dfx_replica --enable-bitcoin +@test "dfx start --bitcoin-node implies --enable-bitcoin" { + dfx_new hello + dfx_start "--bitcoin-node" "127.0.0.1:18444" - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" } -@test "can enable bitcoin through default configuration - dfx start" { - dfx_new hello - define_project_network - set_project_default_bitcoin_enabled +@test "dfx start --enable-bitcoin with no other configuration succeeds" { + dfx_new hello - dfx_start + dfx_start --enable-bitcoin - assert_file_not_empty .dfx/network/local/ic-btc-adapter-pid + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" } -@test "can enable bitcoin through shared local network - dfx start" { - dfx_new hello - set_shared_local_network_bitcoin_enabled +@test "dfx start --enable-bitcoin --background waits until bitcoin canister is installed" { + dfx_new hello - dfx_start + dfx_start --enable-bitcoin - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" + assert_command dfx canister info "$BITCOIN_CANISTER_ID" + assert_contains "Controllers: 2vxsx-fae" + assert_contains "Module hash: 0x" } -@test "can enable bitcoin through local network configuration - dfx start" { - dfx_new hello - set_local_network_bitcoin_enabled +@test "can enable bitcoin through default configuration - dfx start" { + dfx_new hello + define_project_network + set_project_default_bitcoin_enabled - dfx_start + dfx_start - assert_file_not_empty .dfx/network/local/ic-btc-adapter-pid + assert_file_not_empty .dfx/network/local/ic-btc-adapter-pid } -@test "can enable bitcoin through default configuration - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - define_project_network - set_project_default_bitcoin_enabled +@test "can enable bitcoin through shared local network - dfx start" { + dfx_new hello + set_shared_local_network_bitcoin_enabled - dfx_replica + dfx_start - assert_file_not_empty .dfx/network/local/ic-btc-adapter-pid + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" } -@test "can enable bitcoin through shared local network - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - set_shared_local_network_bitcoin_enabled +@test "can enable bitcoin through local network configuration - dfx start" { + dfx_new hello + set_local_network_bitcoin_enabled - dfx_replica + dfx_start - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" + assert_file_not_empty .dfx/network/local/ic-btc-adapter-pid } @test "dfx start with both bitcoin and canister http enabled" { - dfx_new hello - - dfx_start --enable-bitcoin --enable-canister-http - - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' -} - -@test "dfx replica+bootstrap with both bitcoin and canister http enabled" { - skip "dfx replica and bootstrap are deprecated" - - dfx_new hello + dfx_new hello - dfx_replica --enable-bitcoin --enable-canister-http - dfx_bootstrap + dfx_start --enable-bitcoin --enable-canister-http - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-btc-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' } diff --git a/e2e/tests-dfx/bootstrap.bash b/e2e/tests-dfx/bootstrap.bash deleted file mode 100644 index d782394907..0000000000 --- a/e2e/tests-dfx/bootstrap.bash +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bats - -load ../utils/_ - -setup() { - standard_setup - - dfx_new hello -} - -teardown() { - # stop_dfx_replica - # stop_dfx_bootstrap - - standard_teardown -} - -@test "forbid starting webserver with a forwarded port" { - skip "dfx replica and bootstrap are deprecated" - - assert_command_fail dfx bootstrap --port 4943 - assert_match "Cannot forward API calls to the same bootstrap server" -} - -@test "bootstrap supports http requests" { - skip "dfx replica and bootstrap are deprecated" - - dfx_replica - dfx_bootstrap - - dfx canister create --all - dfx build - dfx canister install hello_frontend - - ID=$(dfx canister id hello_frontend) - PORT=$(get_webserver_port) - assert_command curl http://localhost:"$PORT"/sample-asset.txt?canisterId="$ID" --max-time 60 - # shellcheck disable=SC2154 - assert_eq "This is a sample asset!" "$stdout" -} diff --git a/e2e/tests-dfx/build.bash b/e2e/tests-dfx/build.bash index a3ff9bc835..2be504a37d 100644 --- a/e2e/tests-dfx/build.bash +++ b/e2e/tests-dfx/build.bash @@ -3,165 +3,183 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - stop_webserver - dfx_stop + stop_webserver + dfx_stop - standard_teardown + standard_teardown } @test "can build a custom canister with wasm and/or candid from a url" { - install_asset wasm/identity - mkdir -p www/wasm - mv main.wasm www/wasm/ - mv main.did www/wasm - start_webserver --directory www - dfx_start + install_asset wasm/identity + mkdir -p www/wasm + mv main.wasm www/wasm/ + mv main.did www/wasm + start_webserver --directory www + dfx_start - dfx_new + dfx_new - jq '.canisters={}' dfx.json | sponge dfx.json + jq '.canisters={}' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.candid="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.did"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.wasm="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.wasm"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.type="custom"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.candid="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.did"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.wasm="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.wasm"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.type="custom"' dfx.json | sponge dfx.json - dfx deploy + dfx deploy - ID=$(dfx canister id e2e_project) - assert_command dfx canister call e2e_project getCanisterId - assert_match "$ID" + ID=$(dfx canister id e2e_project) + assert_command dfx canister call e2e_project getCanisterId + assert_match "$ID" } @test "report an error if a canister defines both a wasm url and a build step" { - install_asset wasm/identity - mkdir -p www/wasm - mv main.wasm www/wasm/ - mv main.did www/wasm - start_webserver --directory www - dfx_start + install_asset wasm/identity + mkdir -p www/wasm + mv main.wasm www/wasm/ + mv main.did www/wasm + start_webserver --directory www + dfx_start - dfx_new + dfx_new - jq '.canisters={}' dfx.json | sponge dfx.json + jq '.canisters={}' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.candid="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.did"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.wasm="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.wasm"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.type="custom"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project.build="echo nope"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.candid="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.did"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.wasm="http://localhost:'"$E2E_WEB_SERVER_PORT"'/wasm/main.wasm"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.type="custom"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project.build="echo nope"' dfx.json | sponge dfx.json - assert_command_fail dfx deploy - assert_contains "Canister 'e2e_project' defines its wasm field as a URL, and has a build step." + assert_command_fail dfx deploy + assert_contains "Canister 'e2e_project' defines its wasm field as a URL, and has a build step." } @test "build uses default build args" { - install_asset default_args - dfx_start - dfx canister create --all - assert_command_fail dfx build --check - assert_match "unknown option" - assert_match "compacting-gcX" + install_asset default_args + dfx_start + dfx canister create --all + assert_command_fail dfx build --check + assert_match "unknown option" + assert_match "compacting-gcX" } @test "build uses canister build args" { - install_asset canister_args - dfx_start - dfx canister create --all - assert_command_fail dfx build --check - assert_match "unknown option" - assert_match "compacting-gcY" - assert_not_match "compacting-gcX" + install_asset canister_args + dfx_start + dfx canister create --all + assert_command_fail dfx build --check + assert_match "unknown option" + assert_match "compacting-gcY" + assert_not_match "compacting-gcX" } @test "empty canister build args don't shadow default" { - install_asset empty_canister_args - dfx_start - dfx canister create --all - assert_command_fail dfx build --check - assert_match '"--error-detail" "5"' - assert_match "unknown option" - assert_match "compacting-gcX" + install_asset empty_canister_args + dfx_start + dfx canister create --all + assert_command_fail dfx build --check + assert_match '"--error-detail" "5"' + assert_match "unknown option" + assert_match "compacting-gcX" } @test "build fails on invalid motoko" { - install_asset invalid - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match "syntax error" + install_asset invalid + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match "syntax error" } @test "build supports relative imports" { - install_asset import + install_asset import + dfx_start + dfx canister create --all + assert_command dfx build + dfx canister install --all + assert_command dfx canister call e2e_project_backend greet World + assert_match "10World" +} + +@test "build supports auto-generated idl for management canister imports in motoko" { + install_asset motoko_management dfx_start dfx canister create --all assert_command dfx build - dfx canister install --all - assert_command dfx canister call e2e_project_backend greet World - assert_match "10World" + dfx deploy + assert_command dfx canister call e2e_project_backend rand } -@test "build succeeds on default project" { +@test "build supports auto-generated idl for recursive management canister imports in motoko" { + install_asset motoko_management_recursive dfx_start dfx canister create --all assert_command dfx build + dfx deploy + assert_command dfx canister call e2e_project_backend rand +} + +@test "build succeeds on default project" { + dfx_start + dfx canister create --all + assert_command dfx build } @test "build succeeds if enable optimize" { - jq '.canisters.e2e_project_backend.optimize="cycles"' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - assert_command dfx build + jq '.canisters.e2e_project_backend.optimize="cycles"' dfx.json | sponge dfx.json + dfx_start + dfx canister create --all + assert_command dfx build } @test "build custom canister default no shrink" { - install_asset custom_canister - install_asset wasm/identity + install_asset custom_canister + install_asset wasm/identity - dfx_start - dfx canister create --all - assert_command dfx build custom -vvv - assert_not_match "Shrinking WASM" + dfx_start + dfx canister create --all + assert_command dfx build custom -vvv + assert_not_match "Shrinking WASM" - jq '.canisters.custom.shrink=true' dfx.json | sponge dfx.json - assert_command dfx build custom -vvv - assert_match "Shrinking WASM" + jq '.canisters.custom.shrink=true' dfx.json | sponge dfx.json + assert_command dfx build custom -vvv + assert_match "Shrinking WASM" } @test "build custom canister default no optimize" { - install_asset custom_canister - install_asset wasm/identity + install_asset custom_canister + install_asset wasm/identity - dfx_start - dfx canister create --all - assert_command dfx build custom -vvv - assert_not_match "Optimizing" + dfx_start + dfx canister create --all + assert_command dfx build custom -vvv + assert_not_match "Optimizing" - jq '.canisters.custom.optimize="size"' dfx.json | sponge dfx.json - assert_command dfx build custom -vvv - assert_match "Optimizing WASM at level" + jq '.canisters.custom.optimize="size"' dfx.json | sponge dfx.json + assert_command dfx build custom -vvv + assert_match "Optimizing WASM at level" } @test "build succeeds if enable gzip" { - install_asset base - jq '.canisters.e2e_project_backend.gzip=true' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - assert_command dfx build - assert_file_exists .dfx/local/canisters/e2e_project_backend/e2e_project_backend.wasm.gz + install_asset base + jq '.canisters.e2e_project_backend.gzip=true' dfx.json | sponge dfx.json + dfx_start + dfx canister create --all + assert_command dfx build + assert_file_exists .dfx/local/canisters/e2e_project_backend/e2e_project_backend.wasm.gz } @test "build succeeds if specify gzip wasm" { - install_asset gzip - install_asset wasm/identity - dfx_start - dfx canister create --all - assert_command dfx build + install_asset gzip + install_asset wasm/identity + dfx_start + dfx canister create --all + assert_command dfx build } # TODO: Before Tungsten, we need to update this test for code with inter-canister calls. @@ -176,19 +194,19 @@ teardown() { } @test "build outputs warning" { - install_asset warning - dfx_start - dfx canister create --all - assert_command dfx build - assert_match "warning \[M0145\], this pattern of type" + install_asset warning + dfx_start + dfx canister create --all + assert_command dfx build + assert_match "warning \[M0145\], this pattern of type" } @test "build fails on unknown imports" { - install_asset import_error - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match 'import error \[M0011\], canister alias "random" not defined' + install_asset import_error + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match 'import error \[M0011\], canister alias "random" not defined' } @test "build fails if canister type is not supported" { @@ -260,10 +278,10 @@ teardown() { } @test "build succeeds with URL as network parameter" { - dfx_start - webserver_port=$(get_webserver_port) - dfx canister create --all --network "http://127.0.0.1:$webserver_port" - assert_command dfx build --network "http://127.0.0.1:$webserver_port" + dfx_start + webserver_port=$(get_webserver_port) + dfx canister create --all --network "http://127.0.0.1:$webserver_port" + assert_command dfx build --network "http://127.0.0.1:$webserver_port" } @test "build succeeds when requested network is configured" { diff --git a/e2e/tests-dfx/build_granular.bash b/e2e/tests-dfx/build_granular.bash index 930b7fa5a4..04101028c8 100644 --- a/e2e/tests-dfx/build_granular.bash +++ b/e2e/tests-dfx/build_granular.bash @@ -3,101 +3,101 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "direct dependencies are built" { - dfx_start - dfx canister create --all - #specify build for only assets_canister - dfx build e2e_project_frontend - - #validate direct dependency built and is callable - assert_command dfx canister install e2e_project_backend - assert_command dfx canister call e2e_project_backend greet World + dfx_start + dfx canister create --all + #specify build for only assets_canister + dfx build e2e_project_frontend + + #validate direct dependency built and is callable + assert_command dfx canister install e2e_project_backend + assert_command dfx canister call e2e_project_backend greet World } @test "transitive dependencies are built" { - install_asset transitive_deps_canisters - dfx_start - dfx canister create --all - #install of tertiary dependency canister will fail since its not built - assert_command_fail dfx canister install canister_a - #specify build for primary canister - dfx build canister_c - - #validate tertiary transitive dependency is built and callable - assert_command dfx canister install canister_a - assert_command dfx canister call canister_a greet World - assert_match '("Namaste, World!")' + install_asset transitive_deps_canisters + dfx_start + dfx canister create --all + #install of tertiary dependency canister will fail since its not built + assert_command_fail dfx canister install canister_a + #specify build for primary canister + dfx build canister_c + + #validate tertiary transitive dependency is built and callable + assert_command dfx canister install canister_a + assert_command dfx canister call canister_a greet World + assert_match '("Namaste, World!")' } @test "unspecified dependencies are not built" { - dfx_start - dfx canister create --all - # only build motoko canister - dfx build e2e_project_backend - # validate assets canister wasn't built and can't be installed - assert_command_fail dfx canister install e2e_project_frontend - assert_match "No such file or directory" + dfx_start + dfx canister create --all + # only build motoko canister + dfx build e2e_project_backend + # validate assets canister wasn't built and can't be installed + assert_command_fail dfx canister install e2e_project_frontend + assert_match "No such file or directory" } @test "manual build of specified canisters succeeds" { - install_asset assetscanister + install_asset assetscanister - dfx_start - dfx canister create e2e_project_backend - dfx build e2e_project_backend - assert_command dfx canister install e2e_project_backend - assert_command dfx canister call e2e_project_backend greet World + dfx_start + dfx canister create e2e_project_backend + dfx build e2e_project_backend + assert_command dfx canister install e2e_project_backend + assert_command dfx canister call e2e_project_backend greet World - assert_command_fail dfx canister install e2e_project_frontend - assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project_frontend'." - dfx canister create e2e_project_frontend - dfx build e2e_project_frontend - dfx canister install e2e_project_frontend + assert_command_fail dfx canister install e2e_project_frontend + assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project_frontend'." + dfx canister create e2e_project_frontend + dfx build e2e_project_frontend + dfx canister install e2e_project_frontend - assert_command dfx canister call --query e2e_project_frontend retrieve '("/binary/noise.txt")' --output idl - assert_eq '(blob "\b8\01 \80\0aw12 \00xy\0aKL\0b\0ajk")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("/binary/noise.txt")' --output idl + assert_eq '(blob "\b8\01 \80\0aw12 \00xy\0aKL\0b\0ajk")' - assert_command dfx canister call --query e2e_project_frontend retrieve '("/text-with-newlines.txt")' --output idl - assert_eq '(blob "cherries\0ait\27s cherry season\0aCHERRIES")' + assert_command dfx canister call --query e2e_project_frontend retrieve '("/text-with-newlines.txt")' --output idl + assert_eq '(blob "cherries\0ait\27s cherry season\0aCHERRIES")' } @test "cyclic dependencies are detected" { - install_asset transitive_deps_canisters - dfx_start - dfx canister create --all - assert_command_fail dfx build canister_e - assert_match "Circular canister dependencies: canister_e -> canister_d -> canister_e" + install_asset transitive_deps_canisters + dfx_start + dfx canister create --all + assert_command_fail dfx build canister_e + assert_match "Circular canister dependencies: canister_e -> canister_d -> canister_e" } @test "multiple non-cyclic dependency paths to the same canister are ok" { - install_asset transitive_deps_canisters - dfx_start - dfx canister create --all - assert_command dfx build canister_f + install_asset transitive_deps_canisters + dfx_start + dfx canister create --all + assert_command dfx build canister_f } @test "the all flag builds everything" { - dfx_start - dfx canister create --all - assert_command dfx build --all - assert_command dfx canister install --all + dfx_start + dfx canister create --all + assert_command dfx build --all + assert_command dfx canister install --all } @test "the all flags conflicts with canister name" { - dfx_start - dfx canister create --all - assert_command_fail dfx build e2e_project --all + dfx_start + dfx canister create --all + assert_command_fail dfx build e2e_project --all } diff --git a/e2e/tests-dfx/build_rust.bash b/e2e/tests-dfx/build_rust.bash index 0f30045528..e07a3dc101 100644 --- a/e2e/tests-dfx/build_rust.bash +++ b/e2e/tests-dfx/build_rust.bash @@ -3,15 +3,15 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "build without cargo-audit installed cannot check for vulnerabilities" { diff --git a/e2e/tests-dfx/call.bash b/e2e/tests-dfx/call.bash index d6ba70735f..0b2e54b8df 100644 --- a/e2e/tests-dfx/call.bash +++ b/e2e/tests-dfx/call.bash @@ -3,130 +3,130 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "call --candid " { - install_asset call - cat dfx.json + install_asset call + cat dfx.json - dfx_start - dfx deploy - assert_command dfx canister call hello_backend make_struct '("A", "B")' - assert_eq '(record { c = "A"; d = "B" })' + dfx_start + dfx deploy + assert_command dfx canister call hello_backend make_struct '("A", "B")' + assert_eq '(record { c = "A"; d = "B" })' - CANISTER_ID=$(dfx canister id hello_backend) - rm .dfx/local/canister_ids.json + CANISTER_ID=$(dfx canister id hello_backend) + rm .dfx/local/canister_ids.json - # if no candid file known, then no field names - assert_command dfx canister call "$CANISTER_ID" make_struct '("A", "B")' - assert_eq '(record { 99 = "A"; 100 = "B" })' + # if no candid file known, then no field names + assert_command dfx canister call "$CANISTER_ID" make_struct '("A", "B")' + assert_eq '(record { 99 = "A"; 100 = "B" })' - # if passing the candid file, field names available - assert_command dfx canister call --candid .dfx/local/canisters/hello_backend/hello_backend.did "$CANISTER_ID" make_struct '("A", "B")' - assert_eq '(record { c = "A"; d = "B" })' + # if passing the candid file, field names available + assert_command dfx canister call --candid .dfx/local/canisters/hello_backend/hello_backend.did "$CANISTER_ID" make_struct '("A", "B")' + assert_eq '(record { c = "A"; d = "B" })' } @test "call subcommand accepts canister identifier as canister name" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - assert_command dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' - assert_match '("Hello, Names are difficult!")' + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + assert_command dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' + assert_match '("Hello, Names are difficult!")' } @test "call subcommand accepts argument from a file" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - TMP_NAME_FILE="$(mktemp)" - printf '("Names can be very long")' > "$TMP_NAME_FILE" - assert_command dfx canister call --argument-file "$TMP_NAME_FILE" hello_backend greet - assert_match '("Hello, Names can be very long!")' - rm "$TMP_NAME_FILE" + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + TMP_NAME_FILE="$(mktemp)" + printf '("Names can be very long")' > "$TMP_NAME_FILE" + assert_command dfx canister call --argument-file "$TMP_NAME_FILE" hello_backend greet + assert_match '("Hello, Names can be very long!")' + rm "$TMP_NAME_FILE" } @test "call subcommand accepts argument from stdin" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - TMP_NAME_FILE="$(mktemp)" - printf '("stdin")' > "$TMP_NAME_FILE" - assert_command dfx canister call --argument-file - hello_backend greet < "$TMP_NAME_FILE" - assert_match '("Hello, stdin!")' - rm "$TMP_NAME_FILE" + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + TMP_NAME_FILE="$(mktemp)" + printf '("stdin")' > "$TMP_NAME_FILE" + assert_command dfx canister call --argument-file - hello_backend greet < "$TMP_NAME_FILE" + assert_match '("Hello, stdin!")' + rm "$TMP_NAME_FILE" } @test "call random value (pattern)" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - assert_command dfx canister call hello_backend greet --random '{ value = Some ["\"DFINITY\""] }' - assert_match '("Hello, DFINITY!")' + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + assert_command dfx canister call hello_backend greet --random '{ value = Some ["\"DFINITY\""] }' + assert_match '("Hello, DFINITY!")' } @test "error on empty arguments when the method requires some" { - install_asset greet - dfx_start - dfx deploy - assert_command_fail dfx canister call hello_backend greet + install_asset greet + dfx_start + dfx deploy + assert_command_fail dfx canister call hello_backend greet } @test "call random value (empty)" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - assert_command dfx canister call hello_backend greet --random '' - assert_match '("Hello, .*!")' + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + assert_command dfx canister call hello_backend greet --random '' + assert_match '("Hello, .*!")' } @test "long call" { - install_asset recurse - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - assert_command dfx canister call hello_backend recurse 100 + install_asset recurse + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + assert_command dfx canister call hello_backend recurse 100 } @test "call with cycles" { - dfx_start - dfx deploy - assert_command_fail dfx canister call hello_backend greet '' --with-cycles 100 - assert_command dfx canister call hello_backend greet '' --with-cycles 100 --wallet "$(dfx identity get-wallet)" + dfx_start + dfx deploy + assert_command_fail dfx canister call hello_backend greet '' --with-cycles 100 + assert_command dfx canister call hello_backend greet '' --with-cycles 100 --wallet "$(dfx identity get-wallet)" } @test "call by canister id outside of a project" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - ID="$(dfx canister id hello_backend)" - NETWORK="http://localhost:$(get_webserver_port)" - ( - cd "$E2E_TEMP_DIR" - mkdir "not-a-project-dir" - cd "not-a-project-dir" - assert_command dfx canister call "$ID" greet '("you")' --network "$NETWORK" - assert_match '("Hello, you!")' - ) + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + ID="$(dfx canister id hello_backend)" + NETWORK="http://localhost:$(get_webserver_port)" + ( + cd "$E2E_TEMP_DIR" + mkdir "not-a-project-dir" + cd "not-a-project-dir" + assert_command dfx canister call "$ID" greet '("you")' --network "$NETWORK" + assert_match '("Hello, you!")' + ) } diff --git a/e2e/tests-dfx/candid_ui.bash b/e2e/tests-dfx/candid_ui.bash index 166606dfbb..eb98beb583 100644 --- a/e2e/tests-dfx/candid_ui.bash +++ b/e2e/tests-dfx/candid_ui.bash @@ -3,22 +3,22 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "Candid UI" { - dfx_start - dfx deploy - ID=$(dfx canister id __Candid_UI) - PORT=$(get_webserver_port) - assert_command curl http://localhost:"$PORT"/?canisterId="$ID" - assert_match "Candid UI" + dfx_start + dfx deploy + ID=$(dfx canister id __Candid_UI) + PORT=$(get_webserver_port) + assert_command curl http://localhost:"$PORT"/?canisterId="$ID" + assert_match "Candid UI" } diff --git a/e2e/tests-dfx/canister_http.bash b/e2e/tests-dfx/canister_http.bash index 701604bd2a..feb45c7b6b 100644 --- a/e2e/tests-dfx/canister_http.bash +++ b/e2e/tests-dfx/canister_http.bash @@ -2,342 +2,216 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - setup() { - standard_setup + standard_setup } teardown() { - dfx_stop - # stop_dfx_replica - # stop_dfx_bootstrap - standard_teardown + dfx_stop + standard_teardown } set_project_default_canister_http_enabled() { - jq ".defaults.canister_http.enabled=${1:-true}" dfx.json | sponge dfx.json + jq ".defaults.canister_http.enabled=${1:-true}" dfx.json | sponge dfx.json } set_project_local_network_canister_http_enabled() { - jq ".networks.local.canister_http.enabled=${1:-true}" dfx.json | sponge dfx.json + jq ".networks.local.canister_http.enabled=${1:-true}" dfx.json | sponge dfx.json } set_shared_local_network_canister_http_enabled() { - create_networks_json + create_networks_json - jq ".local.canister_http.enabled=${1:-true}" "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + jq ".local.canister_http.enabled=${1:-true}" "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" } set_shared_local_network_canister_http_empty() { - create_networks_json + create_networks_json - jq '.local.canister_http={}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + jq '.local.canister_http={}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" } @test "canister http feature is enabled by default" { - dfx_start + dfx_start - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" } @test "canister http feature is enabled by default with empty json element" { - set_shared_local_network_canister_http_empty + set_shared_local_network_canister_http_empty - dfx_start + dfx_start - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" } @test "dfx restarts replica when ic-https-outcalls-adapter restarts" { - dfx_new hello - dfx_start - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' + dfx_new hello + dfx_start - REPLICA_PID=$(get_replica_pid) - CANISTER_HTTP_ADAPTER_PID=$(get_canister_http_adapter_pid) + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' - echo "replica pid is $REPLICA_PID" - echo "ic-https-outcalls-adapter pid is $CANISTER_HTTP_ADAPTER_PID" + REPLICA_PID=$(get_replica_pid) + CANISTER_HTTP_ADAPTER_PID=$(get_canister_http_adapter_pid) - kill -KILL "$CANISTER_HTTP_ADAPTER_PID" - assert_process_exits "$CANISTER_HTTP_ADAPTER_PID" 15s - assert_process_exits "$REPLICA_PID" 15s + echo "replica pid is $REPLICA_PID" + echo "ic-https-outcalls-adapter pid is $CANISTER_HTTP_ADAPTER_PID" - timeout 15s sh -c \ - 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ - || (echo "replica did not restart" && ps aux && exit 1) - wait_until_replica_healthy + kill -KILL "$CANISTER_HTTP_ADAPTER_PID" + assert_process_exits "$CANISTER_HTTP_ADAPTER_PID" 15s + assert_process_exits "$REPLICA_PID" 15s - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting + timeout 15s sh -c \ + 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ + || (echo "replica did not restart" && ps aux && exit 1) + wait_until_replica_healthy - # Even so, after that passes, sometimes this happens: - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting + # Sometimes initially get an error like: + # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module + # but the condition clears. + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' + # Even so, after that passes, sometimes this happens: + # IC0515: Certified state is not available yet. Please try again... + sleep 10 + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting - ID=$(dfx canister id hello_frontend) + assert_command dfx canister call hello_backend greet '("Omega")' + assert_eq '("Hello, Omega!")' - timeout 15s sh -c \ - "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ - || (echo "icx-proxy did not restart" && ps aux && exit 1) + ID=$(dfx canister id hello_frontend) - assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" -} + timeout 15s sh -c \ + "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ + || (echo "icx-proxy did not restart" && ps aux && exit 1) -@test "dfx restarts replica when ic-https-outcalls-adapter restarts - replica and bootstrap" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - dfx_replica - dfx_bootstrap - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' - - REPLICA_PID=$(get_replica_pid) - CANISTER_HTTP_ADAPTER_PID=$(get_canister_http_adapter_pid) - - echo "replica pid is $REPLICA_PID" - echo "replica port is $(get_replica_port)" - echo "ic-https-outcalls-adapter pid is $CANISTER_HTTP_ADAPTER_PID" - - kill -KILL "$CANISTER_HTTP_ADAPTER_PID" - assert_process_exits "$CANISTER_HTTP_ADAPTER_PID" 15s - assert_process_exits "$REPLICA_PID" 15s - - timeout 15s sh -x -c \ - "until curl --fail --verbose -o /dev/null http://localhost:\$(cat '$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port')/api/v2/status; do echo \"waiting for replica to restart on port \$(cat '$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port')\"; sleep 1; done" \ - || (echo "replica did not restart" && echo "last replica port was $(get_replica_port)" && ps aux && exit 1) - - # bootstrap doesn't detect the new replica port, so we have to restart it - stop_dfx_bootstrap - dfx_bootstrap - - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 1\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - # Even so, after that passes, sometimes this happens: - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait 2\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' + assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" } @test "dfx start --enable-canister-http with no other configuration succeeds" { - dfx_new hello - - dfx_start --enable-canister-http + dfx_new hello - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" -} - -@test "dfx replica --enable-canister-http with no other configuration succeeds" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello + dfx_start --enable-canister-http - dfx_replica --enable-canister-http - - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" } @test "can enable http through project default configuration - dfx start" { - dfx_new hello - define_project_network - set_project_default_canister_http_enabled + dfx_new hello + define_project_network + set_project_default_canister_http_enabled - dfx_start + dfx_start - assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid + assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid } @test "can disable http through project default configuration - dfx start" { - dfx_new hello - define_project_network - set_project_default_canister_http_enabled false + dfx_new hello + define_project_network + set_project_default_canister_http_enabled false - dfx_start + dfx_start - assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid + assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid } @test "can enable http through project local network - dfx start" { - dfx_new hello - define_project_network - set_project_local_network_canister_http_enabled + dfx_new hello + define_project_network + set_project_local_network_canister_http_enabled - dfx_start + dfx_start - assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid + assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid } @test "can disable http through project local network - dfx start" { - dfx_new hello - define_project_network - set_project_local_network_canister_http_enabled false + dfx_new hello + define_project_network + set_project_local_network_canister_http_enabled false - dfx_start + dfx_start - assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid + assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid } @test "can enable http through shared local network - dfx start" { - dfx_new hello - set_shared_local_network_canister_http_enabled + dfx_new hello + set_shared_local_network_canister_http_enabled - dfx_start + dfx_start - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" } @test "can disable http through shared local network - dfx start" { - dfx_new hello - set_shared_local_network_canister_http_enabled false - - dfx_start - - assert_file_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" -} - - -@test "can enable http through project default configuration - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - define_project_network - set_project_default_canister_http_enabled - - dfx_replica - - assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid -} - -@test "can disable http through project default configuration - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - define_project_network - set_project_default_canister_http_enabled false - - dfx_replica - - assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid -} - -@test "can enable http through project local network - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - define_project_network - set_project_local_network_canister_http_enabled - - dfx_replica - - assert_file_not_empty .dfx/network/local/ic-https-outcalls-adapter-pid -} - -@test "can disable http through project local network - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - define_project_network - set_project_local_network_canister_http_enabled false - - dfx_replica - - assert_file_empty .dfx/network/local/ic-https-outcalls-adapter-pid -} - -@test "can enable http through shared local network - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - set_shared_local_network_canister_http_enabled - - dfx_replica - - assert_file_not_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" -} - -@test "can disable http through shared local network - dfx replica" { - skip "dfx replica and bootstrap are deprecated" - dfx_new hello - set_shared_local_network_canister_http_enabled false + dfx_new hello + set_shared_local_network_canister_http_enabled false - dfx_replica + dfx_start - assert_file_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" + assert_file_empty "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/ic-https-outcalls-adapter-pid" } @test "dfx starts http adapter with correct log level - project defaults" { - dfx_new - jq '.defaults.canister_http.log_level="warning"' dfx.json | sponge dfx.json - define_project_network + dfx_new + jq '.defaults.canister_http.log_level="warning"' dfx.json | sponge dfx.json + define_project_network - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.defaults.canister_http.log_level="critical"' dfx.json | sponge dfx.json - assert_command dfx start --background --verbose - assert_match "log level: Critical" + jq '.defaults.canister_http.log_level="critical"' dfx.json | sponge dfx.json + assert_command dfx start --background --verbose + assert_match "log level: Critical" } @test "dfx starts http adapter with correct log level - local network" { - dfx_new - jq '.networks.local.canister_http.log_level="warning"' dfx.json | sponge dfx.json - define_project_network + dfx_new + jq '.networks.local.canister_http.log_level="warning"' dfx.json | sponge dfx.json + define_project_network - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.networks.local.canister_http.log_level="critical"' dfx.json | sponge dfx.json - assert_command dfx start --background --verbose --clean - assert_match "log level: Critical" + jq '.networks.local.canister_http.log_level="critical"' dfx.json | sponge dfx.json + assert_command dfx start --background --verbose --clean + assert_match "log level: Critical" } @test "dfx starts http adapter with correct log level - shared network" { - dfx_new - create_networks_json - jq '.local.canister_http.log_level="warning"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + dfx_new + create_networks_json + jq '.local.canister_http.log_level="warning"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.local.canister_http.log_level="critical"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose - assert_match "log level: Critical" + jq '.local.canister_http.log_level="critical"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + assert_command dfx start --background --verbose + assert_match "log level: Critical" } @test "can query a website" { - dfx_start + dfx_start - dfx_new - install_asset canister_http + dfx_new + install_asset canister_http - dfx deploy + dfx deploy - assert_command dfx canister call e2e_project_backend get_url '("www.githubstatus.com:443","https://www.githubstatus.com:443")' - assert_contains "Git Operations" - assert_contains "API Requests" + assert_command dfx canister call e2e_project_backend get_url '("www.githubstatus.com:443","https://www.githubstatus.com:443")' + assert_contains "Git Operations" + assert_contains "API Requests" } diff --git a/e2e/tests-dfx/certificate.bash b/e2e/tests-dfx/certificate.bash index 3c6ba232d5..1fe5d66269 100644 --- a/e2e/tests-dfx/certificate.bash +++ b/e2e/tests-dfx/certificate.bash @@ -3,76 +3,76 @@ load ../utils/_ setup() { - standard_setup - - dfx_new certificate - - install_asset certificate - dfx_start - - dfx deploy - - BACKEND="127.0.0.1:$(get_webserver_port)" - - # In github workflows, at the time of this writing, we get: - # macos-latest: mitmproxy 7.0.4 - # ubuntu-latest: mitmproxy 4.x - if [ "$(mitmdump --version | grep Mitmproxy | cut -d ' ' -f 2 | cut -c 1-2)" = "4." ]; then - MODIFY_BODY_ARG="--replacements" - else - MODIFY_BODY_ARG="--modify-body" + standard_setup + + dfx_new certificate + + install_asset certificate + dfx_start + + dfx deploy + + BACKEND="127.0.0.1:$(get_webserver_port)" + + # In github workflows, at the time of this writing, we get: + # macos-latest: mitmproxy 7.0.4 + # ubuntu-latest: mitmproxy 4.x + if [ "$(mitmdump --version | grep Mitmproxy | cut -d ' ' -f 2 | cut -c 1-2)" = "4." ]; then + MODIFY_BODY_ARG="--replacements" + else + MODIFY_BODY_ARG="--modify-body" + fi + + # Sometimes, something goes wrong with mitmdump's initialization. + # It reports that it is listening, and the `nc` call succeeds, + # but it does not actually respond to requests. + # + # This happens whether using a fixed port or a dynamic port. + # For this reason, we retry initialization until `dfx ping` succeeds. + # + # I have seen this process take as many as 9 iterations to succeed, + # so across a large number of CI runs, it could take even more. + # The overall CI timeout will limit the maximum time taken. + + while true + do + MITM_PORT=$(get_ephemeral_port) + overwrite_webserver_port "$MITM_PORT" + + mitmdump -p "$MITM_PORT" --mode "reverse:http://$BACKEND" "$MODIFY_BODY_ARG" '/~s/Hello,/Hullo,' & + MITMDUMP_PID=$! + + timeout 5 sh -c \ + "until nc -z localhost $MITM_PORT; do echo waiting for mitmdump; sleep 1; done" \ + || (echo "mitmdump did not start on port $MITM_PORT" && exit 1) + + if timeout 10 dfx ping; then + break fi - # Sometimes, something goes wrong with mitmdump's initialization. - # It reports that it is listening, and the `nc` call succeeds, - # but it does not actually respond to requests. - # - # This happens whether using a fixed port or a dynamic port. - # For this reason, we retry initialization until `dfx ping` succeeds. - # - # I have seen this process take as many as 9 iterations to succeed, - # so across a large number of CI runs, it could take even more. - # The overall CI timeout will limit the maximum time taken. - - while true - do - MITM_PORT=$(get_ephemeral_port) - overwrite_webserver_port "$MITM_PORT" - - mitmdump -p "$MITM_PORT" --mode "reverse:http://$BACKEND" "$MODIFY_BODY_ARG" '/~s/Hello,/Hullo,' & - MITMDUMP_PID=$! - - timeout 5 sh -c \ - "until nc -z localhost $MITM_PORT; do echo waiting for mitmdump; sleep 1; done" \ - || (echo "mitmdump did not start on port $MITM_PORT" && exit 1) - - if timeout 10 dfx ping; then - break - fi - - kill -9 $MITMDUMP_PID - done + kill -9 $MITMDUMP_PID + done } teardown() { - # Kill child processes of mitmdump. Otherwise they hang around way too long - pkill -P $MITMDUMP_PID - kill $MITMDUMP_PID + # Kill child processes of mitmdump. Otherwise they hang around way too long + pkill -P $MITMDUMP_PID + kill $MITMDUMP_PID - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "mitm attack - update: attack fails because certificate verification fails" { - assert_command_fail dfx canister call certificate_backend hello_update '("Buckaroo")' - assert_match 'Certificate verification failed.' + assert_command_fail dfx canister call certificate_backend hello_update '("Buckaroo")' + assert_match 'Certificate verification failed.' } @test "mitm attack - query: attack succeeds because there is no certificate to verify" { - # The wallet does not have a query call forward method (currently calls forward from wallet's update method) - # So call with users Identity as sender here - # There may need to be a query version of wallet_call - assert_command dfx canister call certificate_backend hello_query '("Buckaroo")' - assert_eq '("Hullo, Buckaroo!")' + # The wallet does not have a query call forward method (currently calls forward from wallet's update method) + # So call with users Identity as sender here + # There may need to be a query version of wallet_call + assert_command dfx canister call certificate_backend hello_query '("Buckaroo")' + assert_eq '("Hullo, Buckaroo!")' } diff --git a/e2e/tests-dfx/certified_info.bash b/e2e/tests-dfx/certified_info.bash index 718dd20794..0765760081 100644 --- a/e2e/tests-dfx/certified_info.bash +++ b/e2e/tests-dfx/certified_info.bash @@ -3,37 +3,37 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "get certified-info" { - dfx_start - dfx canister create hello_backend - assert_command dfx canister info "$(dfx canister id hello_backend)" - WALLET_ID=$(dfx identity get-wallet) - SELF_ID=$(dfx identity get-principal) - assert_match \ + dfx_start + dfx canister create hello_backend + assert_command dfx canister info "$(dfx canister id hello_backend)" + WALLET_ID=$(dfx identity get-wallet) + SELF_ID=$(dfx identity get-principal) + assert_match \ "Controllers: ($WALLET_ID $SELF_ID|$SELF_ID $WALLET_ID) Module hash: None" - dfx build hello_backend - RESULT="$(openssl dgst -sha256 .dfx/local/canisters/hello_backend/hello_backend.wasm)" - # shellcheck disable=SC2034 - HASH="0x" - HASH+=$(echo "${RESULT}" | cut -d' ' -f 2) + dfx build hello_backend + RESULT="$(openssl dgst -sha256 .dfx/local/canisters/hello_backend/hello_backend.wasm)" + # shellcheck disable=SC2034 + HASH="0x" + HASH+=$(echo "${RESULT}" | cut -d' ' -f 2) - dfx canister install hello_backend - assert_command dfx canister info "$(dfx canister id hello_backend)" - assert_match \ + dfx canister install hello_backend + assert_command dfx canister info "$(dfx canister id hello_backend)" + assert_match \ "Controllers: ($WALLET_ID $SELF_ID|$SELF_ID $WALLET_ID) Module hash: $(HASH)" } diff --git a/e2e/tests-dfx/create.bash b/e2e/tests-dfx/create.bash index 28c82adaa7..bd16462e84 100644 --- a/e2e/tests-dfx/create.bash +++ b/e2e/tests-dfx/create.bash @@ -3,259 +3,274 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } -@test "create succeeds on default project" { +@test "create with reserved cycles limit" { dfx_start - assert_command dfx canister create --all + + assert_command_fail dfx canister create e2e_project_backend --reserved-cycles-limit 470000 + assert_contains "Cannot create a canister using a wallet if the reserved_cycles_limit is set. Please create with --no-wallet or use dfx canister update-settings instead." + + assert_command dfx canister create e2e_project_frontend --no-wallet + assert_command dfx canister status e2e_project_frontend + assert_contains "Reserved Cycles Limit: 5_000_000_000_000 Cycles" + + assert_command dfx canister create e2e_project_backend --reserved-cycles-limit 470000 --no-wallet + assert_command dfx canister status e2e_project_backend + assert_contains "Reserved Cycles Limit: 470_000 Cycles" +} + +@test "create succeeds on default project" { + dfx_start + assert_command dfx canister create --all } @test "create succeeds with --specified-id" { - dfx_start - assert_command dfx canister create e2e_project_backend --specified-id n5n4y-3aaaa-aaaaa-p777q-cai - assert_command dfx canister id e2e_project_backend - assert_match n5n4y-3aaaa-aaaaa-p777q-cai + dfx_start + assert_command dfx canister create e2e_project_backend --specified-id n5n4y-3aaaa-aaaaa-p777q-cai + assert_command dfx canister id e2e_project_backend + assert_match n5n4y-3aaaa-aaaaa-p777q-cai } @test "create succeeds when specify large canister ID" { - dfx_start - # hhn2s-5l777-77777-7777q-cai is the canister ID of (u64::MAX / 2) - assert_command dfx canister create e2e_project_backend --specified-id hhn2s-5l777-77777-7777q-cai - assert_command dfx canister id e2e_project_backend - assert_match hhn2s-5l777-77777-7777q-cai + dfx_start + # hhn2s-5l777-77777-7777q-cai is the canister ID of (u64::MAX / 2) + assert_command dfx canister create e2e_project_backend --specified-id hhn2s-5l777-77777-7777q-cai + assert_command dfx canister id e2e_project_backend + assert_match hhn2s-5l777-77777-7777q-cai } @test "create fails when specify out of range canister ID" { - dfx_start - # nojwb-ieaaa-aaaaa-aaaaa-cai is the canister ID of (u64::MAX / 2 + 1) - assert_command_fail dfx canister create e2e_project_backend --specified-id nojwb-ieaaa-aaaaa-aaaaa-cai + dfx_start + # nojwb-ieaaa-aaaaa-aaaaa-cai is the canister ID of (u64::MAX / 2 + 1) + assert_command_fail dfx canister create e2e_project_backend --specified-id nojwb-ieaaa-aaaaa-aaaaa-cai - assert_match "Specified CanisterId nojwb-ieaaa-aaaaa-aaaaa-cai is not hosted by subnet" + assert_match "Specified CanisterId nojwb-ieaaa-aaaaa-aaaaa-cai is not hosted by subnet" } @test "create fails if set both --all and --specified-id" { - dfx_start - assert_command_fail dfx canister create --all --specified-id xbgkv-fyaaa-aaaaa-aaava-cai - assert_match "error: the argument '--all' cannot be used with '--specified-id '" + dfx_start + assert_command_fail dfx canister create --all --specified-id xbgkv-fyaaa-aaaaa-aaava-cai + assert_match "error: the argument '--all' cannot be used with '--specified-id '" } @test "create generates the canister_ids.json" { - dfx_start - assert_command dfx canister create --all - [[ -f .dfx/local/canister_ids.json ]] + dfx_start + assert_command dfx canister create --all + [[ -f .dfx/local/canister_ids.json ]] } @test "create without parameters sets wallet and self as controller" { - dfx_start - PRINCIPAL=$(dfx identity get-principal) - WALLET=$(dfx identity get-wallet) - assert_command dfx canister create --all - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: ($PRINCIPAL $WALLET|$WALLET $PRINCIPAL)" + dfx_start + PRINCIPAL=$(dfx identity get-principal) + WALLET=$(dfx identity get-wallet) + assert_command dfx canister create --all + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: ($PRINCIPAL $WALLET|$WALLET $PRINCIPAL)" } @test "create with --no-wallet sets only self as controller" { - dfx_start - PRINCIPAL=$(dfx identity get-principal) - WALLET=$(dfx identity get-wallet) - assert_command dfx canister create --all --no-wallet - assert_command dfx canister info e2e_project_backend - assert_not_match "Controllers: ($PRINCIPAL $WALLET|$WALLET $PRINCIPAL)" - assert_match "Controllers: $PRINCIPAL" + dfx_start + PRINCIPAL=$(dfx identity get-principal) + WALLET=$(dfx identity get-wallet) + assert_command dfx canister create --all --no-wallet + assert_command dfx canister info e2e_project_backend + assert_not_match "Controllers: ($PRINCIPAL $WALLET|$WALLET $PRINCIPAL)" + assert_match "Controllers: $PRINCIPAL" } @test "build fails without create" { - dfx_start - assert_command_fail dfx build - assert_match "Cannot find canister id." + dfx_start + assert_command_fail dfx build + assert_match "Cannot find canister id." } @test "build fails if all canisters in project are not created" { - dfx_start - assert_command dfx canister create e2e_project_backend - assert_command_fail dfx build - assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project_frontend'" + dfx_start + assert_command dfx canister create e2e_project_backend + assert_command_fail dfx build + assert_match "Cannot find canister id. Please issue 'dfx canister create e2e_project_frontend'" } @test "create succeeds with network parameter" { - dfx_start - assert_command dfx canister create --all --network local + dfx_start + assert_command dfx canister create --all --network local } @test "create fails with incorrect network" { - dfx_start - assert_command_fail dfx canister create --all --network nosuch - assert_match "Network not found" + dfx_start + assert_command_fail dfx canister create --all --network nosuch + assert_match "Network not found" } @test "create succeeds when requested network is configured" { - dfx_start + dfx_start - setup_actuallylocal_shared_network - assert_command dfx canister create --all --network actuallylocal + setup_actuallylocal_shared_network + assert_command dfx canister create --all --network actuallylocal } @test "create with wallet succeeds when requested network is configured" { - dfx_start - setup_actuallylocal_shared_network + dfx_start + setup_actuallylocal_shared_network - assert_command dfx_set_wallet - assert_command dfx canister create --all --network actuallylocal + assert_command dfx_set_wallet + assert_command dfx canister create --all --network actuallylocal } @test "create fails if selected network exists but has no providers" { - dfx_start + dfx_start - jq '.networks.actuallylocal.providers=[]' dfx.json | sponge dfx.json - assert_command_fail dfx canister create --all --network actuallylocal - assert_match "Did not find any providers for network 'actuallylocal'" + jq '.networks.actuallylocal.providers=[]' dfx.json | sponge dfx.json + assert_command_fail dfx canister create --all --network actuallylocal + assert_match "Did not find any providers for network 'actuallylocal'" } @test "create fails with network parameter when network does not exist" { - dfx_start - jq '.networks.actuallylocal.providers=["http://not-real.nowhere.test."]' dfx.json | sponge dfx.json - assert_command_fail dfx canister create --all --network actuallylocal - assert_match "dns error: failed to lookup address information" + dfx_start + jq '.networks.actuallylocal.providers=["http://not-real.nowhere.test."]' dfx.json | sponge dfx.json + assert_command_fail dfx canister create --all --network actuallylocal + assert_match "dns error: failed to lookup address information" } @test "create accepts --controller named parameter, with controller by identity name" { - dfx_start - dfx identity new --storage-mode plaintext alice - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - - - assert_command dfx canister create --all --controller alice - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: $ALICE_PRINCIPAL" - - assert_command_fail dfx deploy - assert_command dfx deploy --identity alice -} + dfx_start + dfx identity new --storage-mode plaintext alice + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) -@test "create accepts --controller named parameter, with controller by identity principal" { - dfx_start - dfx identity new --storage-mode plaintext alice - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - assert_command dfx canister create --all --controller "${ALICE_PRINCIPAL}" - assert_command dfx canister info e2e_project_backend - assert_not_match "Controllers: ($ALICE_WALLET $ALICE_PRINCIPAL|$ALICE_PRINCIPAL $ALICE_WALLET)" - assert_match "Controllers: $ALICE_PRINCIPAL" + assert_command dfx canister create --all --controller alice + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: $ALICE_PRINCIPAL" - assert_command_fail dfx deploy - assert_command dfx deploy --identity alice + assert_command_fail dfx deploy + assert_command dfx deploy --identity alice +} + +@test "create accepts --controller named parameter, with controller by identity principal" { + dfx_start + dfx identity new --storage-mode plaintext alice + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + + assert_command dfx canister create --all --controller "${ALICE_PRINCIPAL}" + assert_command dfx canister info e2e_project_backend + assert_not_match "Controllers: ($ALICE_WALLET $ALICE_PRINCIPAL|$ALICE_PRINCIPAL $ALICE_WALLET)" + assert_match "Controllers: $ALICE_PRINCIPAL" + + assert_command_fail dfx deploy + assert_command dfx deploy --identity alice } @test "create accepts --controller named parameter, with controller by wallet principal" { - dfx_start - dfx identity new --storage-mode plaintext alice - ALICE_WALLET=$(dfx identity get-wallet --identity alice) + dfx_start + dfx identity new --storage-mode plaintext alice + ALICE_WALLET=$(dfx identity get-wallet --identity alice) - assert_command dfx canister create --all --controller "${ALICE_WALLET}" - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: $ALICE_WALLET" + assert_command dfx canister create --all --controller "${ALICE_WALLET}" + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: $ALICE_WALLET" - assert_command_fail dfx deploy - assert_command_fail dfx deploy --identity alice - assert_command dfx deploy --identity alice --wallet "${ALICE_WALLET}" + assert_command_fail dfx deploy + assert_command_fail dfx deploy --identity alice + assert_command dfx deploy --identity alice --wallet "${ALICE_WALLET}" } @test "create accepts --controller named parameter, with controller by name of selected identity" { - # there is a different code path if the specified controller happens to be - # the currently selected identity. - dfx_start - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + # there is a different code path if the specified controller happens to be + # the currently selected identity. + dfx_start + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - dfx identity use bob + dfx identity use bob - assert_command dfx canister create --all --controller bob + assert_command dfx canister create --all --controller bob - dfx identity use alice - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: $BOB_PRINCIPAL" + dfx identity use alice + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: $BOB_PRINCIPAL" - assert_command_fail dfx deploy - assert_command dfx deploy --identity bob + assert_command_fail dfx deploy + assert_command dfx deploy --identity bob } @test "create single controller accepts --controller named parameter, with controller by identity name" { - dfx_start - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + dfx_start + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - assert_command dfx canister create --controller alice e2e_project_backend - assert_command dfx canister create --controller bob e2e_project_frontend + assert_command dfx canister create --controller alice e2e_project_backend + assert_command dfx canister create --controller bob e2e_project_frontend - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: $ALICE_PRINCIPAL" + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: $ALICE_PRINCIPAL" - assert_command dfx canister info e2e_project_frontend - assert_match "Controllers: $BOB_PRINCIPAL" + assert_command dfx canister info e2e_project_frontend + assert_match "Controllers: $BOB_PRINCIPAL" - # check this first, because alice will deploy e2e_project in the next step - assert_command_fail dfx deploy e2e_project_backend --identity bob - # this actually deploys e2e_project before failing, because it is a dependency - assert_command_fail dfx deploy e2e_project_frontend --identity alice + # check this first, because alice will deploy e2e_project in the next step + assert_command_fail dfx deploy e2e_project_backend --identity bob + # this actually deploys e2e_project before failing, because it is a dependency + assert_command_fail dfx deploy e2e_project_frontend --identity alice - assert_command dfx deploy e2e_project_backend --identity alice - assert_command dfx deploy e2e_project_frontend --identity bob + assert_command dfx deploy e2e_project_backend --identity alice + assert_command dfx deploy e2e_project_frontend --identity bob } @test "create canister with multiple controllers" { - dfx_start - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - # awk step is to avoid trailing space - PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) - - assert_command dfx canister create --all --controller alice --controller bob --identity alice - assert_command dfx canister info e2e_project_backend - assert_match "Controllers: ${PRINCIPALS_SORTED}" - - assert_command dfx deploy --identity alice - assert_command_fail dfx deploy --identity bob - - # The certified assets canister will have added alice as an authorized user, because she was the caller - # at initialization time. Bob has to be added separately. BUT, the canister has to be deployed first - # in order to call the authorize method. - assert_command dfx canister call e2e_project_frontend authorize "(principal \"$BOB_PRINCIPAL\")" --identity alice - - assert_command dfx deploy --identity bob + dfx_start + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + # awk step is to avoid trailing space + PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) + + assert_command dfx canister create --all --controller alice --controller bob --identity alice + assert_command dfx canister info e2e_project_backend + assert_match "Controllers: ${PRINCIPALS_SORTED}" + + assert_command dfx deploy --identity alice + assert_command_fail dfx deploy --identity bob + + # The certified assets canister will have added alice as an authorized user, because she was the caller + # at initialization time. Bob has to be added separately. BUT, the canister has to be deployed first + # in order to call the authorize method. + assert_command dfx canister call e2e_project_frontend authorize "(principal \"$BOB_PRINCIPAL\")" --identity alice + + assert_command dfx deploy --identity bob } @test "reports wallet must be upgraded if attempting to create a canister with multiple controllers through an old wallet" { - use_wallet_wasm 0.7.2 + use_wallet_wasm 0.7.2 - dfx_start - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob + dfx_start + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob - assert_command_fail dfx canister create --all --controller alice --controller bob --identity alice - assert_match "The wallet canister must be upgraded: The installed wallet does not support multiple controllers." - assert_match "To upgrade, run dfx wallet upgrade" + assert_command_fail dfx canister create --all --controller alice --controller bob --identity alice + assert_match "The wallet canister must be upgraded: The installed wallet does not support multiple controllers." + assert_match "To upgrade, run dfx wallet upgrade" - use_wallet_wasm 0.8.2 - assert_command dfx wallet upgrade --identity alice - assert_command dfx canister create --all --controller alice --controller bob --identity alice + use_wallet_wasm 0.8.2 + assert_command dfx wallet upgrade --identity alice + assert_command dfx canister create --all --controller alice --controller bob --identity alice } @test "canister-create on mainnet without wallet does not propagate the 404" { - assert_command_fail dfx deploy --network ic --no-wallet - assert_match 'dfx ledger create-canister' + assert_command_fail dfx deploy --network ic --no-wallet + assert_match 'dfx ledger create-canister' } diff --git a/e2e/tests-dfx/delete.bash b/e2e/tests-dfx/delete.bash index c11792fa3c..d87e08bcfd 100755 --- a/e2e/tests-dfx/delete.bash +++ b/e2e/tests-dfx/delete.bash @@ -3,38 +3,38 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop - - standard_teardown + dfx_stop + + standard_teardown } @test "delete can be used to delete a canister" { - dfx_start - dfx deploy e2e_project_backend - id=$(dfx canister id e2e_project_backend) - dfx canister stop e2e_project_backend - assert_command dfx canister delete e2e_project_backend - assert_command_fail dfx canister info e2e_project_backend - assert_contains "Cannot find canister id. Please issue 'dfx canister create e2e_project_backend'." - assert_command_fail dfx canister status "$id" - assert_contains "Canister $id not found" + dfx_start + dfx deploy e2e_project_backend + id=$(dfx canister id e2e_project_backend) + dfx canister stop e2e_project_backend + assert_command dfx canister delete e2e_project_backend + assert_command_fail dfx canister info e2e_project_backend + assert_contains "Cannot find canister id. Please issue 'dfx canister create e2e_project_backend'." + assert_command_fail dfx canister status "$id" + assert_contains "Canister $id not found" } @test "delete requires confirmation if the canister is not stopped" { - dfx_start - dfx deploy e2e_project_backend - id=$(dfx canister id e2e_project_backend) - assert_command_fail timeout -s9 20s dfx canister delete e2e_project_backend - assert_command dfx canister info e2e_project_backend - assert_command dfx canister delete e2e_project_backend -y - assert_command_fail dfx canister info e2e_project_backend - assert_contains "Cannot find canister id. Please issue 'dfx canister create e2e_project_backend'." - assert_command_fail dfx canister status "$id" - assert_contains "Canister $id not found" + dfx_start + dfx deploy e2e_project_backend + id=$(dfx canister id e2e_project_backend) + assert_command_fail timeout -s9 20s dfx canister delete e2e_project_backend + assert_command dfx canister info e2e_project_backend + assert_command dfx canister delete e2e_project_backend -y + assert_command_fail dfx canister info e2e_project_backend + assert_contains "Cannot find canister id. Please issue 'dfx canister create e2e_project_backend'." + assert_command_fail dfx canister status "$id" + assert_contains "Canister $id not found" } diff --git a/e2e/tests-dfx/deploy.bash b/e2e/tests-dfx/deploy.bash index b8ac6692f1..87a69c4a32 100644 --- a/e2e/tests-dfx/deploy.bash +++ b/e2e/tests-dfx/deploy.bash @@ -3,144 +3,157 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } -@test "deploy --upgrade-unchanged upgrades even if the .wasm did not change" { +@test "deploy with reserved cycles limit" { dfx_start - assert_command dfx deploy + cat dfx.json + jq '.canisters.hello_backend.initialization_values.reserved_cycles_limit=860000' dfx.json | sponge dfx.json + assert_command_fail dfx deploy + assert_contains "Cannot create a canister using a wallet if the reserved_cycles_limit is set. Please create with --no-wallet or use dfx canister update-settings instead." - assert_command dfx deploy - assert_match "Module hash.*is already installed" + assert_command dfx deploy --no-wallet + + assert_command dfx canister status hello_backend + assert_contains "Reserved Cycles Limit: 860_000 Cycles" +} + +@test "deploy --upgrade-unchanged upgrades even if the .wasm did not change" { + dfx_start + assert_command dfx deploy + + assert_command dfx deploy + assert_match "Module hash.*is already installed" - assert_command dfx deploy --upgrade-unchanged - assert_not_match "Module hash.*is already installed" + assert_command dfx deploy --upgrade-unchanged + assert_not_match "Module hash.*is already installed" } @test "deploy without arguments sets wallet and self as the controllers" { - dfx_start - WALLET=$(dfx identity get-wallet) - PRINCIPAL=$(dfx identity get-principal) - assert_command dfx deploy hello_backend - assert_command dfx canister info hello_backend - assert_match "Controllers: ($WALLET $PRINCIPAL|$PRINCIPAL $WALLET)" + dfx_start + WALLET=$(dfx identity get-wallet) + PRINCIPAL=$(dfx identity get-principal) + assert_command dfx deploy hello_backend + assert_command dfx canister info hello_backend + assert_match "Controllers: ($WALLET $PRINCIPAL|$PRINCIPAL $WALLET)" } @test "deploy --no-wallet sets only self as the controller" { - dfx_start - WALLET=$(dfx identity get-wallet) - PRINCIPAL=$(dfx identity get-principal) - assert_command dfx deploy hello_backend --no-wallet - assert_command dfx canister info hello_backend - assert_not_match "Controllers: ($WALLET $PRINCIPAL|$PRINCIPAL $WALLET)" - assert_match "Controllers: $PRINCIPAL" + dfx_start + WALLET=$(dfx identity get-wallet) + PRINCIPAL=$(dfx identity get-principal) + assert_command dfx deploy hello_backend --no-wallet + assert_command dfx canister info hello_backend + assert_not_match "Controllers: ($WALLET $PRINCIPAL|$PRINCIPAL $WALLET)" + assert_match "Controllers: $PRINCIPAL" } @test "deploy from a subdirectory" { - dfx_new hello - dfx_start - install_asset greet + dfx_new hello + dfx_start + install_asset greet - ( - cd src - assert_command dfx deploy - assert_match "Installing code for" - ) + ( + cd src + assert_command dfx deploy + assert_match "Installing code for" + ) - assert_command dfx canister call hello_backend greet '("Banzai")' - assert_eq '("Hello, Banzai!")' + assert_command dfx canister call hello_backend greet '("Banzai")' + assert_eq '("Hello, Banzai!")' - assert_command dfx deploy - assert_not_match "Installing code for" - assert_match "is already installed" + assert_command dfx deploy + assert_not_match "Installing code for" + assert_match "is already installed" } @test "deploying a dependent doesn't require already-installed dependencies to take args" { - install_asset deploy_deps - dfx_start - assert_command dfx deploy dependency --argument '("dfx")' - touch dependency.mo - assert_command dfx deploy dependent - assert_command dfx canister call dependency greet - assert_match "Hello, dfx!" + install_asset deploy_deps + dfx_start + assert_command dfx deploy dependency --argument '("dfx")' + touch dependency.mo + assert_command dfx deploy dependent + assert_command dfx canister call dependency greet + assert_match "Hello, dfx!" } @test "reinstalling a single Motoko canister with imported dependency works" { - install_asset import_canister - dfx_start - assert_command dfx deploy - assert_command dfx deploy importer --mode reinstall --yes + install_asset import_canister + dfx_start + assert_command dfx deploy + assert_command dfx deploy importer --mode reinstall --yes } @test "deploy succeeds with --specified-id" { - dfx_start - assert_command dfx deploy hello_backend --specified-id n5n4y-3aaaa-aaaaa-p777q-cai - assert_command dfx canister id hello_backend - assert_match n5n4y-3aaaa-aaaaa-p777q-cai + dfx_start + assert_command dfx deploy hello_backend --specified-id n5n4y-3aaaa-aaaaa-p777q-cai + assert_command dfx canister id hello_backend + assert_match n5n4y-3aaaa-aaaaa-p777q-cai } @test "deploy fails if --specified-id without canister_name" { - dfx_start - assert_command_fail dfx deploy --specified-id n5n4y-3aaaa-aaaaa-p777q-cai - assert_match \ + dfx_start + assert_command_fail dfx deploy --specified-id n5n4y-3aaaa-aaaaa-p777q-cai + assert_match \ "error: the following required arguments were not provided: " } @test "deploy does not require wallet if all canisters are created" { - dfx_start - dfx canister create --all --no-wallet - assert_command dfx deploy - assert_not_contains "Creating a wallet canister" - assert_command dfx identity get-wallet - assert_contains "Creating a wallet canister" + dfx_start + dfx canister create --all --no-wallet + assert_command dfx deploy + assert_not_contains "Creating a wallet canister" + assert_command dfx identity get-wallet + assert_contains "Creating a wallet canister" } @test "deploying multiple canisters with arguments fails" { - assert_command_fail dfx deploy --argument hello - assert_contains \ + assert_command_fail dfx deploy --argument hello + assert_contains \ "error: the following required arguments were not provided: " } @test "can deploy gzip wasm" { - jq '.canisters.hello_backend.gzip=true' dfx.json | sponge dfx.json - dfx_start - assert_command dfx deploy - BUILD_HASH="0x$(sha256sum .dfx/local/canisters/hello_backend/hello_backend.wasm.gz | cut -d " " -f 1)" - ONCHAIN_HASH="$(dfx canister info hello_backend | tail -n 1 | cut -d " " -f 3)" - assert_eq "$BUILD_HASH" "$ONCHAIN_HASH" + jq '.canisters.hello_backend.gzip=true' dfx.json | sponge dfx.json + dfx_start + assert_command dfx deploy + BUILD_HASH="0x$(sha256sum .dfx/local/canisters/hello_backend/hello_backend.wasm.gz | cut -d " " -f 1)" + ONCHAIN_HASH="$(dfx canister info hello_backend | tail -n 1 | cut -d " " -f 3)" + assert_eq "$BUILD_HASH" "$ONCHAIN_HASH" } @test "prints the frontend url after deploy" { - dfx_new_frontend hello - dfx_start - assert_command dfx deploy - assert_contains "hello_frontend: http://127.0.0.1" + dfx_new_frontend hello + dfx_start + assert_command dfx deploy + assert_contains "hello_frontend: http://127.0.0.1" } @test "prints the frontend url if 'frontend' section is not present in dfx.json" { - dfx_new_frontend hello - jq 'del(.canisters.hello_frontend.frontend)' dfx.json | sponge dfx.json - dfx_start - assert_command dfx deploy - assert_contains "hello_frontend: http://127.0.0.1" + dfx_new_frontend hello + jq 'del(.canisters.hello_frontend.frontend)' dfx.json | sponge dfx.json + dfx_start + assert_command dfx deploy + assert_contains "hello_frontend: http://127.0.0.1" } @test "prints the frontend url if the frontend section has been removed after initial deployment" { - dfx_new_frontend hello - dfx_start - assert_command dfx deploy - assert_contains "hello_frontend: http://127.0.0.1" - jq 'del(.canisters.hello_frontend.frontend)' dfx.json | sponge dfx.json - assert_command dfx deploy - assert_contains "hello_frontend: http://127.0.0.1" + dfx_new_frontend hello + dfx_start + assert_command dfx deploy + assert_contains "hello_frontend: http://127.0.0.1" + jq 'del(.canisters.hello_frontend.frontend)' dfx.json | sponge dfx.json + assert_command dfx deploy + assert_contains "hello_frontend: http://127.0.0.1" } diff --git a/e2e/tests-dfx/deps.bash b/e2e/tests-dfx/deps.bash index e85ca10a9b..540aa1329a 100644 --- a/e2e/tests-dfx/deps.bash +++ b/e2e/tests-dfx/deps.bash @@ -3,13 +3,13 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - stop_webserver - dfx_stop - standard_teardown + stop_webserver + dfx_stop + standard_teardown } CANISTER_ID_A="yofga-2qaaa-aaaaa-aabsq-cai" @@ -17,441 +17,441 @@ CANISTER_ID_B="yhgn4-myaaa-aaaaa-aabta-cai" CANISTER_ID_C="yahli-baaaa-aaaaa-aabtq-cai" setup_onchain() { - install_asset deps + install_asset deps - # start a webserver to host wasm files - mkdir www - start_webserver --directory www + # start a webserver to host wasm files + mkdir www + start_webserver --directory www - cd onchain || exit + cd onchain || exit - jq '.canisters.a.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/a.wasm"'"' dfx.json | sponge dfx.json - jq '.canisters.b.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/b.wasm.gz"'"' dfx.json | sponge dfx.json - jq '.canisters.c.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/c.wasm"'"' dfx.json | sponge dfx.json + jq '.canisters.a.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/a.wasm"'"' dfx.json | sponge dfx.json + jq '.canisters.b.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/b.wasm.gz"'"' dfx.json | sponge dfx.json + jq '.canisters.c.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/c.wasm"'"' dfx.json | sponge dfx.json - dfx canister create a --specified-id "$CANISTER_ID_A" - dfx canister create b --specified-id "$CANISTER_ID_B" - dfx canister create c --specified-id "$CANISTER_ID_C" - dfx build + dfx canister create a --specified-id "$CANISTER_ID_A" + dfx canister create b --specified-id "$CANISTER_ID_B" + dfx canister create c --specified-id "$CANISTER_ID_C" + dfx build - dfx canister install a --argument 1 - dfx canister install b - dfx canister install c --argument 3 + dfx canister install a --argument 1 + dfx canister install b + dfx canister install c --argument 3 - # copy wasm files to web server dir - cp .dfx/local/canisters/a/a.wasm ../www/a.wasm - cp .dfx/local/canisters/b/b.wasm.gz ../www/b.wasm.gz - cp .dfx/local/canisters/c/c.wasm ../www/c.wasm + # copy wasm files to web server dir + cp .dfx/local/canisters/a/a.wasm ../www/a.wasm + cp .dfx/local/canisters/b/b.wasm.gz ../www/b.wasm.gz + cp .dfx/local/canisters/c/c.wasm ../www/c.wasm - cd .. || exit + cd .. || exit } @test "dfx build can write required metadata for pullable" { - dfx_start - - install_asset deps - - cd onchain - assert_command dfx canister create --all - assert_command dfx build - assert_command ic-wasm .dfx/local/canisters/c/c.wasm metadata - assert_match "icp:public candid:service" - assert_match "icp:public dfx" - - ic-wasm .dfx/local/canisters/c/c.wasm metadata dfx > c_dfx.json - assert_command jq -r '.pullable.wasm_url' c_dfx.json - assert_eq "http://example.com/c.wasm" "$output" - assert_command jq -r '.pullable.dependencies | length' c_dfx.json - assert_eq 1 "$output" - assert_command jq -r '.pullable.dependencies | first' c_dfx.json - assert_eq "$CANISTER_ID_A" "$output" - assert_command jq -r '.pullable.init_guide' c_dfx.json - assert_eq "A natural number, e.g. 20." "$output" + dfx_start + + install_asset deps + + cd onchain + assert_command dfx canister create --all + assert_command dfx build + assert_command ic-wasm .dfx/local/canisters/c/c.wasm metadata + assert_match "icp:public candid:service" + assert_match "icp:public dfx" + + ic-wasm .dfx/local/canisters/c/c.wasm metadata dfx > c_dfx.json + assert_command jq -r '.pullable.wasm_url' c_dfx.json + assert_eq "http://example.com/c.wasm" "$output" + assert_command jq -r '.pullable.dependencies | length' c_dfx.json + assert_eq 1 "$output" + assert_command jq -r '.pullable.dependencies | first' c_dfx.json + assert_eq "$CANISTER_ID_A" "$output" + assert_command jq -r '.pullable.init_guide' c_dfx.json + assert_eq "A natural number, e.g. 20." "$output" } @test "dfx deps pull can resolve dependencies from on-chain canister metadata" { - # ic-ref has different behavior than the replica: - # it doesn't differ whether the canister not exist or the metadata not exist - dfx_start + # ic-ref has different behavior than the replica: + # it doesn't differ whether the canister not exist or the metadata not exist + dfx_start - install_asset deps + install_asset deps - # 1. success path - ## 1.1. prepare "onchain" canisters - # a -> [] - # b -> [a] - # c -> [a] - # app -> [a, b] + # 1. success path + ## 1.1. prepare "onchain" canisters + # a -> [] + # b -> [a] + # c -> [a] + # app -> [a, b] - cd onchain + cd onchain - dfx canister create a --specified-id "$CANISTER_ID_A" - dfx canister create b --specified-id "$CANISTER_ID_B" - dfx canister create c --specified-id "$CANISTER_ID_C" + dfx canister create a --specified-id "$CANISTER_ID_A" + dfx canister create b --specified-id "$CANISTER_ID_B" + dfx canister create c --specified-id "$CANISTER_ID_C" - dfx deploy a --argument 1 - dfx deploy b - dfx deploy c --argument 3 + dfx deploy a --argument 1 + dfx deploy b + dfx deploy c --argument 3 - ## 1.2. pull onchain canisters in "app" project - cd ../app + ## 1.2. pull onchain canisters in "app" project + cd ../app - assert_command_fail dfx deps pull --network local # the overall pull fail but succeed to fetch and parse `dfx:deps` recursively - assert_contains "Fetching dependencies of canister $CANISTER_ID_B... + assert_command_fail dfx deps pull --network local # the overall pull fail but succeed to fetch and parse `dfx:deps` recursively + assert_contains "Fetching dependencies of canister $CANISTER_ID_B... Fetching dependencies of canister $CANISTER_ID_C... Fetching dependencies of canister $CANISTER_ID_A... Found 3 dependencies: $CANISTER_ID_A $CANISTER_ID_B $CANISTER_ID_C" - assert_occurs 1 "Fetching dependencies of canister $CANISTER_ID_A..." # common dependency onchain_a is pulled only once - assert_contains "Pulling canister $CANISTER_ID_A... + assert_occurs 1 "Fetching dependencies of canister $CANISTER_ID_A..." # common dependency onchain_a is pulled only once + assert_contains "Pulling canister $CANISTER_ID_A... ERROR: Failed to pull canister $CANISTER_ID_A. Failed to download from url: http://example.com/a.wasm." - assert_contains "Pulling canister $CANISTER_ID_B... + assert_contains "Pulling canister $CANISTER_ID_B... ERROR: Failed to pull canister $CANISTER_ID_B. Failed to download from url: http://example.com/b.wasm.gz." - assert_contains "Pulling canister $CANISTER_ID_C... + assert_contains "Pulling canister $CANISTER_ID_C... ERROR: Failed to pull canister $CANISTER_ID_C. Failed to download from url: http://example.com/c.wasm." - # 3. sad path: if the canister is not present on-chain - cd ../onchain - dfx build c - dfx canister install c --argument 3 --mode=reinstall --yes # reinstall the correct canister c - dfx canister uninstall-code a + # 3. sad path: if the canister is not present on-chain + cd ../onchain + dfx build c + dfx canister install c --argument 3 --mode=reinstall --yes # reinstall the correct canister c + dfx canister uninstall-code a - cd ../app - assert_command_fail dfx deps pull --network local - assert_contains "Failed to get dependencies of canister $CANISTER_ID_A." + cd ../app + assert_command_fail dfx deps pull --network local + assert_contains "Failed to get dependencies of canister $CANISTER_ID_A." - cd ../onchain - dfx canister stop a - dfx canister delete a + cd ../onchain + dfx canister stop a + dfx canister delete a - cd ../app - assert_command_fail dfx deps pull --network local - assert_contains "Failed to get dependencies of canister $CANISTER_ID_A." + cd ../app + assert_command_fail dfx deps pull --network local + assert_contains "Failed to get dependencies of canister $CANISTER_ID_A." } @test "dfx deps pull can download wasm and candids to shared cache and generate pulled.json" { - use_test_specific_cache_root # dfx deps pull will download files to cache - - PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_B/canister.wasm.gz" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_A/canister.wasm" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_C/canister.wasm" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_B/service.did" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_A/service.did" - assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_C/service.did" - - # start a "mainnet" replica which host the onchain canisters - dfx_start - - setup_onchain - - # TODO: test gzipped wasm can be pulled when we have "gzip" option in dfx.json (SDK-1102) - - # pull canisters in app project - cd app - assert_file_not_exists "deps/pulled.json" - - assert_command dfx deps pull --network local - assert_file_exists "$PULLED_DIR/$CANISTER_ID_B/canister.wasm.gz" - assert_file_exists "$PULLED_DIR/$CANISTER_ID_A/canister.wasm" - assert_file_exists "$PULLED_DIR/$CANISTER_ID_C/canister.wasm" - assert_file_exists "$PULLED_DIR/$CANISTER_ID_B/service.did" - assert_file_exists "$PULLED_DIR/$CANISTER_ID_A/service.did" - assert_file_exists "$PULLED_DIR/$CANISTER_ID_C/service.did" - - cd deps - assert_file_exists "pulled.json" - assert_file_exists "candid/$CANISTER_ID_B.did" - assert_file_exists "candid/$CANISTER_ID_C.did" - assert_eq 5 "$(jq -r '.canisters | keys' pulled.json | wc -l | tr -d ' ')" # 3 canisters + 2 lines of '[' and ']' - assert_command jq -r '.canisters."'"$CANISTER_ID_A"'".init_guide' pulled.json - assert_eq "A natural number, e.g. 10." "$output" - assert_command jq -r '.canisters."'"$CANISTER_ID_B"'".name' pulled.json - assert_eq "dep_b" "$output" - assert_command jq -r '.canisters."'"$CANISTER_ID_C"'".name' pulled.json - assert_eq "dep_c" "$output" - cd ../ - - assert_command dfx deps pull --network local -vvv - assert_contains "The canister wasm was found in the cache." # cache hit - - # sad path 1: wasm hash doesn't match on chain - rm -r "${PULLED_DIR:?}/" - cd ../onchain - cp .dfx/local/canisters/c/c.wasm ../www/a.wasm - - cd ../app - assert_command_fail dfx deps pull --network local - assert_contains "Failed to pull canister $CANISTER_ID_A." - assert_contains "Hash mismatch." - - # sad path 2: url server doesn't have the file - rm -r "${PULLED_DIR:?}/" - rm ../www/a.wasm - - assert_command_fail dfx deps pull --network local - assert_contains "Failed to pull canister $CANISTER_ID_A." - assert_contains "Failed to download from url:" + use_test_specific_cache_root # dfx deps pull will download files to cache + + PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_B/canister.wasm.gz" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_A/canister.wasm" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_C/canister.wasm" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_B/service.did" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_A/service.did" + assert_file_not_exists "$PULLED_DIR/$CANISTER_ID_C/service.did" + + # start a "mainnet" replica which host the onchain canisters + dfx_start + + setup_onchain + + # TODO: test gzipped wasm can be pulled when we have "gzip" option in dfx.json (SDK-1102) + + # pull canisters in app project + cd app + assert_file_not_exists "deps/pulled.json" + + assert_command dfx deps pull --network local + assert_file_exists "$PULLED_DIR/$CANISTER_ID_B/canister.wasm.gz" + assert_file_exists "$PULLED_DIR/$CANISTER_ID_A/canister.wasm" + assert_file_exists "$PULLED_DIR/$CANISTER_ID_C/canister.wasm" + assert_file_exists "$PULLED_DIR/$CANISTER_ID_B/service.did" + assert_file_exists "$PULLED_DIR/$CANISTER_ID_A/service.did" + assert_file_exists "$PULLED_DIR/$CANISTER_ID_C/service.did" + + cd deps + assert_file_exists "pulled.json" + assert_file_exists "candid/$CANISTER_ID_B.did" + assert_file_exists "candid/$CANISTER_ID_C.did" + assert_eq 5 "$(jq -r '.canisters | keys' pulled.json | wc -l | tr -d ' ')" # 3 canisters + 2 lines of '[' and ']' + assert_command jq -r '.canisters."'"$CANISTER_ID_A"'".init_guide' pulled.json + assert_eq "A natural number, e.g. 10." "$output" + assert_command jq -r '.canisters."'"$CANISTER_ID_B"'".name' pulled.json + assert_eq "dep_b" "$output" + assert_command jq -r '.canisters."'"$CANISTER_ID_C"'".name' pulled.json + assert_eq "dep_c" "$output" + cd ../ + + assert_command dfx deps pull --network local -vvv + assert_contains "The canister wasm was found in the cache." # cache hit + + # sad path 1: wasm hash doesn't match on chain + rm -r "${PULLED_DIR:?}/" + cd ../onchain + cp .dfx/local/canisters/c/c.wasm ../www/a.wasm + + cd ../app + assert_command_fail dfx deps pull --network local + assert_contains "Failed to pull canister $CANISTER_ID_A." + assert_contains "Hash mismatch." + + # sad path 2: url server doesn't have the file + rm -r "${PULLED_DIR:?}/" + rm ../www/a.wasm + + assert_command_fail dfx deps pull --network local + assert_contains "Failed to pull canister $CANISTER_ID_A." + assert_contains "Failed to download from url:" } @test "dfx deps pull can check hash when dfx:wasm_hash specified" { - use_test_specific_cache_root # dfx deps pull will download files to cache + use_test_specific_cache_root # dfx deps pull will download files to cache - # start a "mainnet" replica which host the onchain canisters - dfx_start + # start a "mainnet" replica which host the onchain canisters + dfx_start - install_asset deps + install_asset deps - # start a webserver to host wasm files - mkdir www - start_webserver --directory www + # start a webserver to host wasm files + mkdir www + start_webserver --directory www - cd onchain + cd onchain - jq '.canisters.a.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/a.wasm"'"' dfx.json | sponge dfx.json - jq '.canisters.b.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/b.wasm.gz"'"' dfx.json | sponge dfx.json - jq '.canisters.c.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/c.wasm"'"' dfx.json | sponge dfx.json + jq '.canisters.a.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/a.wasm"'"' dfx.json | sponge dfx.json + jq '.canisters.b.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/b.wasm.gz"'"' dfx.json | sponge dfx.json + jq '.canisters.c.pullable.wasm_url="'"http://localhost:$E2E_WEB_SERVER_PORT/c.wasm"'"' dfx.json | sponge dfx.json - dfx canister create a --specified-id "$CANISTER_ID_A" - dfx canister create b --specified-id "$CANISTER_ID_B" - dfx canister create c --specified-id "$CANISTER_ID_C" - dfx build + dfx canister create a --specified-id "$CANISTER_ID_A" + dfx canister create b --specified-id "$CANISTER_ID_B" + dfx canister create c --specified-id "$CANISTER_ID_C" + dfx build - # copy wasm files to web server dir - cp .dfx/local/canisters/a/a.wasm ../www/a.wasm - cp .dfx/local/canisters/b/b.wasm.gz ../www/b.wasm.gz - cp .dfx/local/canisters/c/c.wasm ../www/c.wasm + # copy wasm files to web server dir + cp .dfx/local/canisters/a/a.wasm ../www/a.wasm + cp .dfx/local/canisters/b/b.wasm.gz ../www/b.wasm.gz + cp .dfx/local/canisters/c/c.wasm ../www/c.wasm - CUSTOM_HASH="$(sha256sum .dfx/local/canisters/a/a.wasm | cut -d " " -f 1)" - jq '.canisters.a.pullable.wasm_hash="'"$CUSTOM_HASH"'"' dfx.json | sponge dfx.json - dfx build a # .dfx/local/canisters/a/a.wasm is replaced. The new wasm has wasm_hash defined and will be installed. + CUSTOM_HASH="$(sha256sum .dfx/local/canisters/a/a.wasm | cut -d " " -f 1)" + jq '.canisters.a.pullable.wasm_hash="'"$CUSTOM_HASH"'"' dfx.json | sponge dfx.json + dfx build a # .dfx/local/canisters/a/a.wasm is replaced. The new wasm has wasm_hash defined and will be installed. - # cd ../../../ - dfx canister install a --argument 1 - dfx canister install b - dfx canister install c --argument 3 + # cd ../../../ + dfx canister install a --argument 1 + dfx canister install b + dfx canister install c --argument 3 - # pull canisters in app project - cd ../app - assert_file_not_exists "deps/pulled.json" + # pull canisters in app project + cd ../app + assert_file_not_exists "deps/pulled.json" - assert_command dfx deps pull --network local -vvv - assert_contains "Canister $CANISTER_ID_A specified a custom hash:" + assert_command dfx deps pull --network local -vvv + assert_contains "Canister $CANISTER_ID_A specified a custom hash:" - # error case: hash mismatch - PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" - rm -r "${PULLED_DIR:?}/" - cd ../onchain - cp .dfx/local/canisters/a/a.wasm ../www/a.wasm # now the webserver has the onchain version of canister_a which won't match wasm_hash + # error case: hash mismatch + PULLED_DIR="$DFX_CACHE_ROOT/.cache/dfinity/pulled/" + rm -r "${PULLED_DIR:?}/" + cd ../onchain + cp .dfx/local/canisters/a/a.wasm ../www/a.wasm # now the webserver has the onchain version of canister_a which won't match wasm_hash - cd ../app - assert_command_fail dfx deps pull --network local -vvv - assert_contains "Canister $CANISTER_ID_A specified a custom hash:" - assert_contains "Failed to pull canister $CANISTER_ID_A." - assert_contains "Hash mismatch." + cd ../app + assert_command_fail dfx deps pull --network local -vvv + assert_contains "Canister $CANISTER_ID_A specified a custom hash:" + assert_contains "Failed to pull canister $CANISTER_ID_A." + assert_contains "Hash mismatch." } @test "dfx deps init works" { - use_test_specific_cache_root # dfx deps pull will download files to cache + use_test_specific_cache_root # dfx deps pull will download files to cache - # start a "mainnet" replica which host the onchain canisters - dfx_start + # start a "mainnet" replica which host the onchain canisters + dfx_start - setup_onchain + setup_onchain - # pull canisters in app project - cd app - assert_command dfx deps pull --network local + # pull canisters in app project + cd app + assert_command dfx deps pull --network local - # stop the "mainnet" replica - dfx_stop + # stop the "mainnet" replica + dfx_stop - assert_command dfx deps init - assert_contains "The following canister(s) require an init argument. Please run \`dfx deps init \` to set them individually:" - assert_contains "$CANISTER_ID_A" - assert_contains "$CANISTER_ID_C (dep_c)" + assert_command dfx deps init + assert_contains "The following canister(s) require an init argument. Please run \`dfx deps init \` to set them individually:" + assert_contains "$CANISTER_ID_A" + assert_contains "$CANISTER_ID_C (dep_c)" - assert_command dfx deps init "$CANISTER_ID_A" --argument 11 - assert_command dfx deps init dep_c --argument 33 + assert_command dfx deps init "$CANISTER_ID_A" --argument 11 + assert_command dfx deps init dep_c --argument 33 - # The argument is the hex string of '("abc")' which doesn't type check - # However, passing raw argument will bypass the type check so following command succeed - assert_command dfx deps init "$CANISTER_ID_A" --argument "4449444c00017103616263" --argument-type raw - assert_command jq -r '.canisters."'"$CANISTER_ID_A"'".arg_raw' deps/init.json - assert_eq "4449444c00017103616263" "$output" + # The argument is the hex string of '("abc")' which doesn't type check + # However, passing raw argument will bypass the type check so following command succeed + assert_command dfx deps init "$CANISTER_ID_A" --argument "4449444c00017103616263" --argument-type raw + assert_command jq -r '.canisters."'"$CANISTER_ID_A"'".arg_raw' deps/init.json + assert_eq "4449444c00017103616263" "$output" - # error cases - ## require init arguments but not provide - assert_command_fail dfx deps init "$CANISTER_ID_A" - assert_contains "Canister $CANISTER_ID_A requires an init argument" + # error cases + ## require init arguments but not provide + assert_command_fail dfx deps init "$CANISTER_ID_A" + assert_contains "Canister $CANISTER_ID_A requires an init argument" - ## wrong type - assert_command_fail dfx deps init "$CANISTER_ID_A" --argument '("abc")' - assert_contains "Invalid data: Unable to serialize Candid values: type mismatch: \"abc\" cannot be of type nat" + ## wrong type + assert_command_fail dfx deps init "$CANISTER_ID_A" --argument '("abc")' + assert_contains "Invalid data: Unable to serialize Candid values: type mismatch: \"abc\" cannot be of type nat" - ## require no init argument but provide - assert_command_fail dfx deps init dep_b --argument 1 - assert_contains "Canister $CANISTER_ID_B (dep_b) takes no init argument. Please rerun without \`--argument\`" + ## require no init argument but provide + assert_command_fail dfx deps init dep_b --argument 1 + assert_contains "Canister $CANISTER_ID_B (dep_b) takes no init argument. Please rerun without \`--argument\`" - ## require init arguments but not provide - assert_command_fail dfx deps init dep_c - assert_contains "Canister $CANISTER_ID_C (dep_c) requires an init argument. The following info might be helpful: + ## require init arguments but not provide + assert_command_fail dfx deps init dep_c + assert_contains "Canister $CANISTER_ID_C (dep_c) requires an init argument. The following info might be helpful: init => A natural number, e.g. 20. candid:args => (nat)" - ## canister ID not in pulled.json - assert_command_fail dfx deps init aaaaa-aa - assert_contains "Could not find aaaaa-aa in pulled.json" + ## canister ID not in pulled.json + assert_command_fail dfx deps init aaaaa-aa + assert_contains "Could not find aaaaa-aa in pulled.json" } @test "dfx deps deploy works" { - use_test_specific_cache_root # dfx deps pull will download files to cache - - # start a "mainnet" replica which host the onchain canisters - dfx_start - - setup_onchain - - # pull canisters in app project - cd app - assert_command dfx deps pull --network local - - # delete onchain canisters so that the replica has no canisters as a clean local replica - cd ../onchain - dfx canister stop a - dfx canister delete a - dfx canister stop b - dfx canister delete b - dfx canister stop c - dfx canister delete c - - cd ../app - assert_command dfx deps init # b is set here - assert_command dfx deps init "$CANISTER_ID_A" --argument 11 - assert_command dfx deps init "$CANISTER_ID_C" --argument 33 - - # deploy all - assert_command dfx deps deploy - assert_contains "Creating canister: $CANISTER_ID_A + use_test_specific_cache_root # dfx deps pull will download files to cache + + # start a "mainnet" replica which host the onchain canisters + dfx_start + + setup_onchain + + # pull canisters in app project + cd app + assert_command dfx deps pull --network local + + # delete onchain canisters so that the replica has no canisters as a clean local replica + cd ../onchain + dfx canister stop a + dfx canister delete a + dfx canister stop b + dfx canister delete b + dfx canister stop c + dfx canister delete c + + cd ../app + assert_command dfx deps init # b is set here + assert_command dfx deps init "$CANISTER_ID_A" --argument 11 + assert_command dfx deps init "$CANISTER_ID_C" --argument 33 + + # deploy all + assert_command dfx deps deploy + assert_contains "Creating canister: $CANISTER_ID_A Installing canister: $CANISTER_ID_A" - assert_contains "Creating canister: $CANISTER_ID_B (dep_b) + assert_contains "Creating canister: $CANISTER_ID_B (dep_b) Installing canister: $CANISTER_ID_B (dep_b)" - assert_contains "Creating canister: $CANISTER_ID_C (dep_c) + assert_contains "Creating canister: $CANISTER_ID_C (dep_c) Installing canister: $CANISTER_ID_C (dep_c)" - # by name in dfx.json - assert_command dfx deps deploy dep_b - assert_contains "Installing canister: $CANISTER_ID_B (dep_b)" # dep_p has been created before, so we only see "Installing ..." here - - # by canister id - assert_command dfx deps deploy $CANISTER_ID_A - assert_contains "Installing canister: $CANISTER_ID_A" - - # deployed pull dependencies can be stopped and deleted - assert_command dfx canister stop dep_b --identity anonymous - assert_command dfx canister delete dep_b --identity anonymous - - assert_command dfx canister stop $CANISTER_ID_A --identity anonymous - assert_command dfx canister delete $CANISTER_ID_A --identity anonymous - - # error cases - ## set wrong init argument - assert_command dfx deps init "$CANISTER_ID_A" --argument "4449444c00017103616263" --argument-type raw - assert_command_fail dfx deps deploy - assert_contains "Failed to install canister $CANISTER_ID_A" - - ## canister ID not in pulled.json - assert_command_fail dfx deps deploy aaaaa-aa - assert_contains "Could not find aaaaa-aa in pulled.json" - - ## no init.json - rm deps/init.json - assert_command_fail dfx deps deploy - assert_contains "Failed to read init.json. Please run \`dfx deps init\`." - - ## forgot to set init argument for some dependencies - assert_command dfx deps init # b is set here - assert_command_fail dfx deps deploy "$CANISTER_ID_A" - assert_contains "Failed to create and install canister $CANISTER_ID_A" - assert_contains "Failed to find $CANISTER_ID_A entry in init.json. Please run \`dfx deps init $CANISTER_ID_A\`." + # by name in dfx.json + assert_command dfx deps deploy dep_b + assert_contains "Installing canister: $CANISTER_ID_B (dep_b)" # dep_p has been created before, so we only see "Installing ..." here + + # by canister id + assert_command dfx deps deploy $CANISTER_ID_A + assert_contains "Installing canister: $CANISTER_ID_A" + + # deployed pull dependencies can be stopped and deleted + assert_command dfx canister stop dep_b --identity anonymous + assert_command dfx canister delete dep_b --identity anonymous + + assert_command dfx canister stop $CANISTER_ID_A --identity anonymous + assert_command dfx canister delete $CANISTER_ID_A --identity anonymous + + # error cases + ## set wrong init argument + assert_command dfx deps init "$CANISTER_ID_A" --argument "4449444c00017103616263" --argument-type raw + assert_command_fail dfx deps deploy + assert_contains "Failed to install canister $CANISTER_ID_A" + + ## canister ID not in pulled.json + assert_command_fail dfx deps deploy aaaaa-aa + assert_contains "Could not find aaaaa-aa in pulled.json" + + ## no init.json + rm deps/init.json + assert_command_fail dfx deps deploy + assert_contains "Failed to read init.json. Please run \`dfx deps init\`." + + ## forgot to set init argument for some dependencies + assert_command dfx deps init # b is set here + assert_command_fail dfx deps deploy "$CANISTER_ID_A" + assert_contains "Failed to create and install canister $CANISTER_ID_A" + assert_contains "Failed to find $CANISTER_ID_A entry in init.json. Please run \`dfx deps init $CANISTER_ID_A\`." } @test "dfx deps pulled dependencies work with app canister" { - use_test_specific_cache_root # dfx deps pull will download files to cache - - # start a "mainnet" replica which host the onchain canisters - dfx_start - - setup_onchain - - # pull canisters in app project - cd app - assert_command dfx deps pull --network local - - # delete onchain canisters so that the replica has no canisters as a clean local replica - cd ../onchain - dfx canister stop a - dfx canister delete a - dfx canister stop b - dfx canister delete b - dfx canister stop c - dfx canister delete c - - cd ../app - assert_command_fail dfx canister create dep_b - assert_contains "dep_b is a pull dependency. Please deploy it using \`dfx deps deploy dep_b\`" - assert_command dfx canister create app - - assert_command dfx canister create --all - assert_contains "There are pull dependencies defined in dfx.json. Please deploy them using \`dfx deps deploy\`." - - assert_command dfx build app - assert_command dfx canister install app - - # pulled dependency dep_b hasn't been deployed on local replica - assert_command_fail dfx canister call app get_b - assert_contains "Canister $CANISTER_ID_B not found" - - assert_command dfx deps init - assert_command dfx deps init "$CANISTER_ID_A" --argument 11 - assert_command dfx deps init "$CANISTER_ID_C" --argument 33 - assert_command dfx deps deploy - - assert_command dfx canister call app get_b - assert_eq "(2 : nat)" "$output" - assert_command dfx canister call app get_c - assert_eq "(33 : nat)" "$output" # corresponding to "--argument 33" above - assert_command dfx canister call app get_b_times_a - assert_eq "(22 : nat)" "$output" # 2 * 11 - assert_command dfx canister call app get_c_times_a - assert_eq "(363 : nat)" "$output" # 33 * 11 - - # start a clean local replica - dfx canister stop app - dfx canister delete app - assert_command dfx deploy # only deploy app canister + use_test_specific_cache_root # dfx deps pull will download files to cache + + # start a "mainnet" replica which host the onchain canisters + dfx_start + + setup_onchain + + # pull canisters in app project + cd app + assert_command dfx deps pull --network local + + # delete onchain canisters so that the replica has no canisters as a clean local replica + cd ../onchain + dfx canister stop a + dfx canister delete a + dfx canister stop b + dfx canister delete b + dfx canister stop c + dfx canister delete c + + cd ../app + assert_command_fail dfx canister create dep_b + assert_contains "dep_b is a pull dependency. Please deploy it using \`dfx deps deploy dep_b\`" + assert_command dfx canister create app + + assert_command dfx canister create --all + assert_contains "There are pull dependencies defined in dfx.json. Please deploy them using \`dfx deps deploy\`." + + assert_command dfx build app + assert_command dfx canister install app + + # pulled dependency dep_b hasn't been deployed on local replica + assert_command_fail dfx canister call app get_b + assert_contains "Canister $CANISTER_ID_B not found" + + assert_command dfx deps init + assert_command dfx deps init "$CANISTER_ID_A" --argument 11 + assert_command dfx deps init "$CANISTER_ID_C" --argument 33 + assert_command dfx deps deploy + + assert_command dfx canister call app get_b + assert_eq "(2 : nat)" "$output" + assert_command dfx canister call app get_c + assert_eq "(33 : nat)" "$output" # corresponding to "--argument 33" above + assert_command dfx canister call app get_b_times_a + assert_eq "(22 : nat)" "$output" # 2 * 11 + assert_command dfx canister call app get_c_times_a + assert_eq "(363 : nat)" "$output" # 33 * 11 + + # start a clean local replica + dfx canister stop app + dfx canister delete app + assert_command dfx deploy # only deploy app canister } @test "dfx deps does nothing in a project has no pull dependencies" { - dfx_new empty + dfx_new empty - # verify the help message - assert_command dfx deps pull -h - assert_contains "Pull canisters upon which the project depends. This command connects to the \"ic\" mainnet by default. + # verify the help message + assert_command dfx deps pull -h + assert_contains "Pull canisters upon which the project depends. This command connects to the \"ic\" mainnet by default. You can still choose other network by setting \`--network\`" - assert_command dfx deps pull - assert_contains "There are no pull dependencies defined in dfx.json" - assert_command dfx deps init - assert_contains "There are no pull dependencies defined in dfx.json" - assert_command dfx deps deploy - assert_contains "There are no pull dependencies defined in dfx.json" - - assert_directory_not_exists "deps" + assert_command dfx deps pull + assert_contains "There are no pull dependencies defined in dfx.json" + assert_command dfx deps init + assert_contains "There are no pull dependencies defined in dfx.json" + assert_command dfx deps deploy + assert_contains "There are no pull dependencies defined in dfx.json" + + assert_directory_not_exists "deps" } diff --git a/e2e/tests-dfx/describe_local_network.bash b/e2e/tests-dfx/describe_local_network.bash index 587991dc1a..4e54675441 100644 --- a/e2e/tests-dfx/describe_local_network.bash +++ b/e2e/tests-dfx/describe_local_network.bash @@ -3,124 +3,124 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx start with disabled canister http" { - create_networks_json - echo "{}" | jq '.local.canister_http.enabled=false' >"$E2E_NETWORKS_JSON" - assert_command dfx start --host 127.0.0.1:0 --background --verbose + create_networks_json + echo "{}" | jq '.local.canister_http.enabled=false' >"$E2E_NETWORKS_JSON" + assert_command dfx start --host 127.0.0.1:0 --background --verbose - assert_match "canister http: disabled \(default: enabled\)" + assert_match "canister http: disabled \(default: enabled\)" } @test "dfx start with a nonstandard subnet type" { - create_networks_json - echo "{}" | jq '.local.replica.subnet_type="verifiedapplication"' >"$E2E_NETWORKS_JSON" + create_networks_json + echo "{}" | jq '.local.replica.subnet_type="verifiedapplication"' >"$E2E_NETWORKS_JSON" - assert_command dfx start --host 127.0.0.1:0 --background --verbose + assert_command dfx start --host 127.0.0.1:0 --background --verbose - assert_match "subnet type: VerifiedApplication \(default: Application\)" + assert_match "subnet type: VerifiedApplication \(default: Application\)" } @test "dfx start with nonstandard bitcoin node" { - assert_command dfx start --host 127.0.0.1:0 --background --bitcoin-node 192.168.0.1:18000 --verbose + assert_command dfx start --host 127.0.0.1:0 --background --bitcoin-node 192.168.0.1:18000 --verbose - assert_match "bitcoin: enabled \(default: disabled\)" - assert_match "nodes: \[192.168.0.1:18000\] \(default: \[127.0.0.1:18444\]\)" + assert_match "bitcoin: enabled \(default: disabled\)" + assert_match "nodes: \[192.168.0.1:18000\] \(default: \[127.0.0.1:18444\]\)" } @test "dfx start enabling bitcoin" { - assert_command dfx start --host 127.0.0.1:0 --background --enable-bitcoin --verbose + assert_command dfx start --host 127.0.0.1:0 --background --enable-bitcoin --verbose - assert_match "bitcoin: enabled \(default: disabled\)" + assert_match "bitcoin: enabled \(default: disabled\)" } @test "dfx start in a project without a network definition" { - mkdir some-project - cd some-project - echo "{}" >dfx.json - - # we have to pass 0 for port to avoid conflicts - assert_command dfx start --host 127.0.0.1:0 --background --verbose - - assert_match "There is no project-specific network 'local' defined in .*/some-project/dfx.json." - assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not exist." - - assert_match "Local server configuration:" - assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:4943\)" - assert_match "bitcoin: disabled" - assert_match "canister http: enabled" - assert_match "subnet type: Application" - assert_match "scope: shared" + mkdir some-project + cd some-project + echo "{}" >dfx.json + + # we have to pass 0 for port to avoid conflicts + assert_command dfx start --host 127.0.0.1:0 --background --verbose + + assert_match "There is no project-specific network 'local' defined in .*/some-project/dfx.json." + assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not exist." + + assert_match "Local server configuration:" + assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:4943\)" + assert_match "bitcoin: disabled" + assert_match "canister http: enabled" + assert_match "subnet type: Application" + assert_match "scope: shared" } @test "dfx start outside of a project with default configuration" { - assert_command dfx start --host 127.0.0.1:0 --background --verbose + assert_command dfx start --host 127.0.0.1:0 --background --verbose - assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." - assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not exist." + assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." + assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not exist." } @test "dfx start outside of a project with a shared configuration file" { - create_networks_json + create_networks_json - assert_command dfx start --background --verbose + assert_command dfx start --background --verbose - assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." - assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not define it." + assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." + assert_match "Using the default definition for the 'local' shared network because $DFX_CONFIG_ROOT/.config/dfx/networks.json does not define it." } @test "dfx start outside of a project with a shared configuration file that defines the local network" { - create_networks_json - echo "{}" | jq '.local.bind="127.0.0.1:0"' >"$E2E_NETWORKS_JSON" + create_networks_json + echo "{}" | jq '.local.bind="127.0.0.1:0"' >"$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose + assert_command dfx start --background --verbose - assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." - assert_match "Using shared network 'local' defined in $DFX_CONFIG_ROOT/.config/dfx/networks.json" + assert_match "There is no project-specific network 'local' because there is no project \(no dfx.json\)." + assert_match "Using shared network 'local' defined in $DFX_CONFIG_ROOT/.config/dfx/networks.json" } @test "dfx start describes default project-specific network" { - # almost default: use a dynamic port - echo "{}" | jq '.networks.local.bind="127.0.0.1:0"' > dfx.json - - assert_command dfx start --background --verbose - - assert_match "Local server configuration:" - assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:8000\)" - assert_match "bitcoin: disabled" - assert_match "canister http: enabled" - assert_match "subnet type: Application" - assert_match "data directory: .*/working-dir/.dfx/network/local" - assert_match "scope: project" + # almost default: use a dynamic port + echo "{}" | jq '.networks.local.bind="127.0.0.1:0"' > dfx.json + + assert_command dfx start --background --verbose + + assert_match "Local server configuration:" + assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:8000\)" + assert_match "bitcoin: disabled" + assert_match "canister http: enabled" + assert_match "subnet type: Application" + assert_match "data directory: .*/working-dir/.dfx/network/local" + assert_match "scope: project" } @test "dfx start describes default shared network" { - # almost default: use a dynamic port - create_networks_json - echo "{}" | jq '.local.bind="127.0.0.1:0"' >"$E2E_NETWORKS_JSON" + # almost default: use a dynamic port + create_networks_json + echo "{}" | jq '.local.bind="127.0.0.1:0"' >"$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose + assert_command dfx start --background --verbose - assert_match "Local server configuration:" - assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:4943\)" - assert_match "bitcoin: disabled" - assert_match "canister http: enabled" - assert_match "subnet type: Application" + assert_match "Local server configuration:" + assert_match "bind address: 127.0.0.1:0 \(default: 127.0.0.1:4943\)" + assert_match "bitcoin: disabled" + assert_match "canister http: enabled" + assert_match "subnet type: Application" - if [ "$(uname)" == "Darwin" ]; then - assert_match "data directory: .*/home-dir/Library/Application Support/org.dfinity.dfx/network/local" - elif [ "$(uname)" == "Linux" ]; then - assert_match "data directory: .*/home-dir/.local/share/dfx/network/local" - fi + if [ "$(uname)" == "Darwin" ]; then + assert_match "data directory: .*/home-dir/Library/Application Support/org.dfinity.dfx/network/local" + elif [ "$(uname)" == "Linux" ]; then + assert_match "data directory: .*/home-dir/.local/share/dfx/network/local" + fi - assert_match "scope: shared" + assert_match "scope: shared" } \ No newline at end of file diff --git a/e2e/tests-dfx/dfx_install.bash b/e2e/tests-dfx/dfx_install.bash index 8e8a7e068b..60c8f8b1a2 100644 --- a/e2e/tests-dfx/dfx_install.bash +++ b/e2e/tests-dfx/dfx_install.bash @@ -2,81 +2,79 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx cache show does not install the dfx version into the cache" { - use_test_specific_cache_root - test -z "$(ls -A "$DFX_CACHE_ROOT")" + use_test_specific_cache_root + test -z "$(ls -A "$DFX_CACHE_ROOT")" - assert_command dfx cache show + assert_command dfx cache show - # does not populate the cache with this version - test ! -e "$(dfx cache show)" + # does not populate the cache with this version + test ! -e "$(dfx cache show)" - # it does create the empty versions directory though - test -d "$DFX_CACHE_ROOT/.cache/dfinity/versions" - test -z "$(ls -A "$DFX_CACHE_ROOT/.cache/dfinity/versions"``)" + # it does create the empty versions directory though + test -d "$DFX_CACHE_ROOT/.cache/dfinity/versions" + test -z "$(ls -A "$DFX_CACHE_ROOT/.cache/dfinity/versions"``)" } @test "non-forced install populates an empty cache" { - use_test_specific_cache_root - test ! -e "$(dfx cache show)"/dfx + use_test_specific_cache_root + test ! -e "$(dfx cache show)"/dfx - dfx_new + dfx_new - test -f "$(dfx cache show)"/dfx + test -f "$(dfx cache show)"/dfx } @test "forced install populates an empty cache" { - use_test_specific_cache_root + use_test_specific_cache_root - test ! -e "$(dfx cache show)"/dfx + test ! -e "$(dfx cache show)"/dfx - assert_command dfx cache install + assert_command dfx cache install - test -f "$(dfx cache show)"/dfx + test -f "$(dfx cache show)"/dfx } @test "forced install over an install succeeds" { - assert_command dfx cache install - test -f "$(dfx cache show)"/dfx + assert_command dfx cache install + test -f "$(dfx cache show)"/dfx - assert_command dfx cache install + assert_command dfx cache install } @test "Motoko base library files are not executable" { - assert_command dfx cache install - for file in "$(dfx cache show)"/base/*.mo; do - assert_command_fail test -x "$file" - assert_command_fail "$file" - assert_contains "Permission denied" - done + assert_command dfx cache install + for file in "$(dfx cache show)"/base/*.mo; do + assert_command_fail test -x "$file" + assert_command_fail "$file" + assert_contains "Permission denied" + done } @test "forced install overwrites a cached version" { - assert_command dfx cache install - test -f "$(dfx cache show)"/dfx + assert_command dfx cache install + test -f "$(dfx cache show)"/dfx - # add something extra to the cache - echo "garbage" >"$(dfx cache show)"/garbage - test -f "$(dfx cache show)"/garbage + # add something extra to the cache + echo "garbage" >"$(dfx cache show)"/garbage + test -f "$(dfx cache show)"/garbage - assert_command dfx cache install + assert_command dfx cache install - # dfx cache install should have removed it - test ! -e "$(dfx cache show)"/garbage + # dfx cache install should have removed it + test ! -e "$(dfx cache show)"/garbage - # and also installed the cache itself - test -f "$(dfx cache show)"/dfx + # and also installed the cache itself + test -f "$(dfx cache show)"/dfx } diff --git a/e2e/tests-dfx/dotenv.bash b/e2e/tests-dfx/dotenv.bash index 542895bb04..1f6f2ab755 100644 --- a/e2e/tests-dfx/dotenv.bash +++ b/e2e/tests-dfx/dotenv.bash @@ -3,84 +3,84 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop - - standard_teardown + dfx_stop + + standard_teardown } @test "writes environment variables to .env" { - dfx_start - dfx canister create --all - # .env should also include canisters that are not explicit dependencies - jq 'del(.canisters.e2e_project_frontend.dependencies)' dfx.json | sponge dfx.json - backend_canister=$(dfx canister id e2e_project_backend) - frontend_canister=$(dfx canister id e2e_project_frontend) - - assert_command dfx build e2e_project_frontend - - assert_file_exists .env - env=$(< .env) - assert_contains "DFX_NETWORK='local'" "$env" - assert_contains "CANISTER_ID_E2E_PROJECT_BACKEND='$backend_canister'" "$env" - assert_contains "E2E_PROJECT_BACKEND_CANISTER_ID='$backend_canister'" "$env" - assert_contains "CANISTER_ID_E2E_PROJECT_FRONTEND='$frontend_canister'" "$env" - assert_contains "E2E_PROJECT_FRONTEND_CANISTER_ID='$frontend_canister'" "$env" - - setup_actuallylocal_project_network - dfx canister create --all --network actuallylocal - assert_command dfx build --network actuallylocal - assert_contains "DFX_NETWORK='actuallylocal'" "$(< .env)" + dfx_start + dfx canister create --all + # .env should also include canisters that are not explicit dependencies + jq 'del(.canisters.e2e_project_frontend.dependencies)' dfx.json | sponge dfx.json + backend_canister=$(dfx canister id e2e_project_backend) + frontend_canister=$(dfx canister id e2e_project_frontend) + + assert_command dfx build e2e_project_frontend + + assert_file_exists .env + env=$(< .env) + assert_contains "DFX_NETWORK='local'" "$env" + assert_contains "CANISTER_ID_E2E_PROJECT_BACKEND='$backend_canister'" "$env" + assert_contains "E2E_PROJECT_BACKEND_CANISTER_ID='$backend_canister'" "$env" + assert_contains "CANISTER_ID_E2E_PROJECT_FRONTEND='$frontend_canister'" "$env" + assert_contains "E2E_PROJECT_FRONTEND_CANISTER_ID='$frontend_canister'" "$env" + + setup_actuallylocal_project_network + dfx canister create --all --network actuallylocal + assert_command dfx build --network actuallylocal + assert_contains "DFX_NETWORK='actuallylocal'" "$(< .env)" } @test "writes environment variables to selected file" { - dfx_start - dfx canister create --all + dfx_start + dfx canister create --all - assert_command dfx build --output-env-file flag.env - assert_file_exists flag.env - assert_contains "DFX_NETWORK='local'" "$(< flag.env)" + assert_command dfx build --output-env-file flag.env + assert_file_exists flag.env + assert_contains "DFX_NETWORK='local'" "$(< flag.env)" - jq '.output_env_file="json.env"' dfx.json | sponge dfx.json - assert_command dfx build - assert_file_exists json.env - assert_contains "DFX_NETWORK='local'" "$(< json.env)" + jq '.output_env_file="json.env"' dfx.json | sponge dfx.json + assert_command dfx build + assert_file_exists json.env + assert_contains "DFX_NETWORK='local'" "$(< json.env)" - jq 'del(.output_env_file)' dfx.json | sponge dfx.json - assert_command dfx build - assert_file_not_exists .env + jq 'del(.output_env_file)' dfx.json | sponge dfx.json + assert_command dfx build + assert_file_not_exists .env } @test "does not clobber existing .env content" { - dfx_start - dfx canister create --all - echo 'foo=bar' > .env - - assert_command dfx build - assert_file_exists .env - env=$(< .env) - assert_contains "DFX_NETWORK='local'" "$env" - assert_contains "foo=bar" "$env" - - echo 'baz=quux' >> .env - assert_command dfx build - env=$(< .env) - assert_contains "DFX_NETWORK='local'" "$env" - assert_contains "foo=bar" "$env" - assert_contains "baz=quux" "$env" - - # deliberately corrupt the file - head -n 3 .env | sponge .env - echo 'baz=quux' >> .env - assert_command dfx build - env=$(< .env) - assert_contains "# END DFX CANISTER ENVIRONMENT VARIABLES" "$env" - assert_contains "DFX_NETWORK='local'" "$env" - assert_contains "foo=bar" "$env" - assert_contains "baz=quux" "$env" + dfx_start + dfx canister create --all + echo 'foo=bar' > .env + + assert_command dfx build + assert_file_exists .env + env=$(< .env) + assert_contains "DFX_NETWORK='local'" "$env" + assert_contains "foo=bar" "$env" + + echo 'baz=quux' >> .env + assert_command dfx build + env=$(< .env) + assert_contains "DFX_NETWORK='local'" "$env" + assert_contains "foo=bar" "$env" + assert_contains "baz=quux" "$env" + + # deliberately corrupt the file + head -n 3 .env | sponge .env + echo 'baz=quux' >> .env + assert_command dfx build + env=$(< .env) + assert_contains "# END DFX CANISTER ENVIRONMENT VARIABLES" "$env" + assert_contains "DFX_NETWORK='local'" "$env" + assert_contains "foo=bar" "$env" + assert_contains "baz=quux" "$env" } diff --git a/e2e/tests-dfx/ed25519.bash b/e2e/tests-dfx/ed25519.bash index e8de4d8d6c..1e81d06663 100644 --- a/e2e/tests-dfx/ed25519.bash +++ b/e2e/tests-dfx/ed25519.bash @@ -3,27 +3,27 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "can call a canister using an ed25519 identity" { - install_asset ed25519 - assert_command dfx identity import --storage-mode plaintext ed25519 identity.pem - dfx_new # This installs replica and other binaries - dfx identity use ed25519 - install_asset whoami - dfx_start - dfx canister create whoami - dfx build - dfx canister install whoami - assert_command dfx canister call whoami whoami - assert_eq '(principal "2nor3-keehi-duuup-d7jcn-onggn-3atzm-gejtl-5tlzn-k4g6c-nnbf7-7qe")' - assert_command dfx identity get-principal - assert_eq "2nor3-keehi-duuup-d7jcn-onggn-3atzm-gejtl-5tlzn-k4g6c-nnbf7-7qe" + install_asset ed25519 + assert_command dfx identity import --storage-mode plaintext ed25519 identity.pem + dfx_new # This installs replica and other binaries + dfx identity use ed25519 + install_asset whoami + dfx_start + dfx canister create whoami + dfx build + dfx canister install whoami + assert_command dfx canister call whoami whoami + assert_eq '(principal "2nor3-keehi-duuup-d7jcn-onggn-3atzm-gejtl-5tlzn-k4g6c-nnbf7-7qe")' + assert_command dfx identity get-principal + assert_eq "2nor3-keehi-duuup-d7jcn-onggn-3atzm-gejtl-5tlzn-k4g6c-nnbf7-7qe" } diff --git a/e2e/tests-dfx/error_context.bash b/e2e/tests-dfx/error_context.bash index 61ad808525..694e1a8fa4 100644 --- a/e2e/tests-dfx/error_context.bash +++ b/e2e/tests-dfx/error_context.bash @@ -3,181 +3,181 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new - install_asset error_context + dfx_new + install_asset error_context } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "problems reading wallets.json" { - dfx_start + dfx_start - assert_command dfx identity get-wallet + assert_command dfx identity get-wallet - echo "invalid json" >"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + echo "invalid json" >"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - assert_command_fail dfx identity get-wallet - assert_match "Failed to parse contents of .*/network/local/wallets.json as json" - assert_match "expected value at line 1 column 1" + assert_command_fail dfx identity get-wallet + assert_match "Failed to parse contents of .*/network/local/wallets.json as json" + assert_match "expected value at line 1 column 1" - assert_command_fail dfx wallet upgrade - assert_match "Failed to parse contents of .*/network/local/wallets.json as json" - assert_match "expected value at line 1 column 1" + assert_command_fail dfx wallet upgrade + assert_match "Failed to parse contents of .*/network/local/wallets.json as json" + assert_match "expected value at line 1 column 1" - echo '{ "identities": {} }' >"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + echo '{ "identities": {} }' >"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - # maybe you were sudo when you made it - chmod u=w,go= "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - assert_command_fail dfx identity get-wallet - assert_match "Failed to read .*/network/local/wallets.json" - assert_match "Permission denied" + # maybe you were sudo when you made it + chmod u=w,go= "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + assert_command_fail dfx identity get-wallet + assert_match "Failed to read .*/network/local/wallets.json" + assert_match "Permission denied" - assert_command_fail dfx wallet upgrade - assert_match "Failed to read .*/network/local/wallets.json" - assert_match "Permission denied" + assert_command_fail dfx wallet upgrade + assert_match "Failed to read .*/network/local/wallets.json" + assert_match "Permission denied" - # can't write it? - chmod u=r,go= "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - assert_command dfx identity new --storage-mode plaintext alice - assert_command_fail dfx identity get-wallet --identity alice - assert_match "Failed to write to .*/local/wallets.json" - assert_match "Permission denied" + # can't write it? + chmod u=r,go= "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + assert_command dfx identity new --storage-mode plaintext alice + assert_command_fail dfx identity get-wallet --identity alice + assert_match "Failed to write to .*/local/wallets.json" + assert_match "Permission denied" } @test "address already in use" { - dfx_start + dfx_start - port=$(get_webserver_port) - address="127.0.0.1:$port" + port=$(get_webserver_port) + address="127.0.0.1:$port" - # fool dfx start into thinking dfx isn't running - mv "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/pid" "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/hidden_pid" + # fool dfx start into thinking dfx isn't running + mv "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/pid" "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/hidden_pid" - assert_command_fail dfx start --host "$address" + assert_command_fail dfx start --host "$address" - # What was the purpose of the address - assert_match "frontend address" + # What was the purpose of the address + assert_match "frontend address" - # What was the address we were looking for - assert_match "$address" + # What was the address we were looking for + assert_match "$address" - # The underlying cause - assert_match "Address already in use" + # The underlying cause + assert_match "Address already in use" - # Allow dfx stop to stop dfx in teardown. Otherwise, bats will never exit - mv "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/hidden_pid" "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/pid" + # Allow dfx stop to stop dfx in teardown. Otherwise, bats will never exit + mv "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/hidden_pid" "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/pid" } @test "corrupt dfx.json" { - echo "corrupt" >dfx.json - assert_command_fail dfx deploy + echo "corrupt" >dfx.json + assert_command_fail dfx deploy - # The bare minimum is to mention the file - assert_match "dfx.json" + # The bare minimum is to mention the file + assert_match "dfx.json" - # It's nice to mention the full path to the file - assert_match "$(pwd)/dfx.json" + # It's nice to mention the full path to the file + assert_match "$(pwd)/dfx.json" - # The underlying cause - assert_match "expected value at line 1 column 1" + # The underlying cause + assert_match "expected value at line 1 column 1" } @test "packtool missing" { - dfx_start + dfx_start - assert_command dfx canister create packtool_missing + assert_command dfx canister create packtool_missing - jq '.defaults.build.packtool="not-a-valid-packtool and some parameters"' dfx.json | sponge dfx.json + jq '.defaults.build.packtool="not-a-valid-packtool and some parameters"' dfx.json | sponge dfx.json - assert_command_fail dfx build packtool_missing + assert_command_fail dfx build packtool_missing - # expect to see the name of the packtool and the parameters - assert_match '"not-a-valid-packtool" "and" "some" "parameters"' - # expect to see the underlying cause - assert_match "No such file or directory" + # expect to see the name of the packtool and the parameters + assert_match '"not-a-valid-packtool" "and" "some" "parameters"' + # expect to see the underlying cause + assert_match "No such file or directory" } @test "moc missing" { - use_test_specific_cache_root # Because this test modifies a file in the cache + use_test_specific_cache_root # Because this test modifies a file in the cache - dfx_start + dfx_start - assert_command dfx canister create m_o_c_missing + assert_command dfx canister create m_o_c_missing - rm -f "$(dfx cache show)/moc" - assert_command_fail dfx build m_o_c_missing + rm -f "$(dfx cache show)/moc" + assert_command_fail dfx build m_o_c_missing - # expect to see the name of the binary - assert_match "moc" + # expect to see the name of the binary + assert_match "moc" - # expect to see the full path of the binary - assert_contains "$(dfx cache show)/moc" + # expect to see the full path of the binary + assert_contains "$(dfx cache show)/moc" - # expect to see the underlying cause - assert_match "No such file or directory" + # expect to see the underlying cause + assert_match "No such file or directory" } @test "npm is not installed" { - dfx_start + dfx_start - assert_command dfx canister create npm_missing + assert_command dfx canister create npm_missing - # this is how dfx decides to run `npm run build' - touch package.json + # this is how dfx decides to run `npm run build' + touch package.json - dfx_path="$(which dfx)" - # commands needed by assert_command_fail: - helpers_path="$(which mktemp rm echo | xargs -n 1 dirname | sort | uniq | tr '\n' ':')" - PATH="$helpers_path" assert_command_fail "$dfx_path" deploy npm_missing + dfx_path="$(which dfx)" + # commands needed by assert_command_fail: + helpers_path="$(which mktemp rm echo | xargs -n 1 dirname | sort | uniq | tr '\n' ':')" + PATH="$helpers_path" assert_command_fail "$dfx_path" deploy npm_missing - # expect to see the npm command line - assert_contains 'program: "npm"' - assert_match 'args: \[.*"npm".*"run".*"build".*\]' - # expect to see the name of the canister - assert_match "npm_missing" - # expect to see the underlying cause - assert_match "No such file or directory" + # expect to see the npm command line + assert_contains 'program: "npm"' + assert_match 'args: \[.*"npm".*"run".*"build".*\]' + # expect to see the name of the canister + assert_match "npm_missing" + # expect to see the underlying cause + assert_match "No such file or directory" } @test "missing asset source directory" { - dfx_start + dfx_start - assert_command dfx canister create asset_bad_source_path + assert_command dfx canister create asset_bad_source_path - assert_command_fail dfx deploy asset_bad_source_path + assert_command_fail dfx deploy asset_bad_source_path - # expect to see the bad path - assert_match "src/does/not/exist" - # expect to see the name of the canister - assert_match "asset_bad_source_path" - # expect to see the underlying cause - assert_match "No such file or directory" + # expect to see the bad path + assert_match "src/does/not/exist" + # expect to see the name of the canister + assert_match "asset_bad_source_path" + # expect to see the underlying cause + assert_match "No such file or directory" } @test "custom bad build step" { - dfx_start + dfx_start - assert_command dfx canister create custom_bad_build_step + assert_command dfx canister create custom_bad_build_step - assert_command_fail dfx build custom_bad_build_step + assert_command_fail dfx build custom_bad_build_step - # expect to see what it tried to call - assert_match "not-the-name-of-an-executable-that-exists" - # expect to see the name of the canister - assert_match "custom_bad_build_step" - # expect to see the underlying cause - assert_match "Cannot find command or file" + # expect to see what it tried to call + assert_match "not-the-name-of-an-executable-that-exists" + # expect to see the name of the canister + assert_match "custom_bad_build_step" + # expect to see the underlying cause + assert_match "Cannot find command or file" } @test "invalid optimization level" { - jq '.canisters.bad_optimization_level.optimize="bad_level"' dfx.json | sponge dfx.json - assert_command_fail dfx_start - assert_match "expected one of " + jq '.canisters.bad_optimization_level.optimize="bad_level"' dfx.json | sponge dfx.json + assert_command_fail dfx_start + assert_match "expected one of " } \ No newline at end of file diff --git a/e2e/tests-dfx/error_diagnosis.bash b/e2e/tests-dfx/error_diagnosis.bash index fa774ac1cf..5713607c9a 100644 --- a/e2e/tests-dfx/error_diagnosis.bash +++ b/e2e/tests-dfx/error_diagnosis.bash @@ -3,56 +3,56 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "Duplicate assets in dist/ from src/" { - dfx_new_frontend hello - install_asset greet - dfx_start - assert_command dfx deploy + dfx_new_frontend hello + install_asset greet + dfx_start + assert_command dfx deploy - # simulate previous deploy with CopyPlugin step - cp src/hello_frontend/assets/* dist/hello_frontend/ + # simulate previous deploy with CopyPlugin step + cp src/hello_frontend/assets/* dist/hello_frontend/ - assert_command_fail dfx deploy - assert_contains "Remove the CopyPlugin step from webpack.config.js" - assert_contains "Delete all files from the dist/ directory" + assert_command_fail dfx deploy + assert_contains "Remove the CopyPlugin step from webpack.config.js" + assert_contains "Delete all files from the dist/ directory" } @test "HTTP 403 has a full diagnosis" { - dfx_new hello - install_asset greet - dfx_start - assert_command dfx deploy - - # make sure normal status command works - assert_command dfx canister status hello_backend - - # create a non-controller ID - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity use alice - - # calling canister status with different identity provokes HTTP 403 - assert_command_fail dfx canister status hello_backend - assert_match "not part of the controllers" # this is part of the error explanation - assert_match "'dfx canister update-settings --add-controller \(--network ic\)'" # this is part of the solution + dfx_new hello + install_asset greet + dfx_start + assert_command dfx deploy + + # make sure normal status command works + assert_command dfx canister status hello_backend + + # create a non-controller ID + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity use alice + + # calling canister status with different identity provokes HTTP 403 + assert_command_fail dfx canister status hello_backend + assert_match "not part of the controllers" # this is part of the error explanation + assert_match "'dfx canister update-settings --add-controller \(--network ic\)'" # this is part of the solution } @test "Instruct user to set a wallet" { - dfx_new hello - install_asset greet - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity use alice - - # this will fail because no wallet is configured for alice on network ic - assert_command_fail dfx deploy --network ic - assert_match "requires a configured wallet" # this is part of the error explanation - assert_match "'dfx identity set-wallet --network '" # this is part of the solution + dfx_new hello + install_asset greet + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity use alice + + # this will fail because no wallet is configured for alice on network ic + assert_command_fail dfx deploy --network ic + assert_match "requires a configured wallet" # this is part of the error explanation + assert_match "'dfx identity set-wallet --network '" # this is part of the solution } diff --git a/e2e/tests-dfx/extension.bash b/e2e/tests-dfx/extension.bash index 0d81e3e91b..f5688d51ef 100644 --- a/e2e/tests-dfx/extension.bash +++ b/e2e/tests-dfx/extension.bash @@ -3,86 +3,86 @@ load ../utils/_ setup() { - standard_setup - use_test_specific_cache_root + standard_setup + use_test_specific_cache_root } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "install extension from official registry" { - assert_command_fail dfx snsx + assert_command_fail dfx snsx - assert_command dfx extension list - assert_match 'No extensions installed' + assert_command dfx extension list + assert_match 'No extensions installed' - assert_command dfx extension install sns --install-as snsx - # TODO: how to capture spinner message? - # assert_match 'Successfully installed extension' + assert_command dfx extension install sns --install-as snsx --version 0.2.1 + # TODO: how to capture spinner message? + # assert_match 'Successfully installed extension' - assert_command dfx extension list - assert_match 'snsx' + assert_command dfx extension list + assert_match 'snsx' - assert_command dfx --help - assert_match 'snsx.*Toolkit for' + assert_command dfx --help + assert_match 'snsx.*Toolkit for' - assert_command dfx snsx --help + assert_command dfx snsx --help - assert_command dfx extension uninstall snsx - # TODO: how to capture spinner message? - # assert_match 'Successfully uninstalled extension' + assert_command dfx extension uninstall snsx + # TODO: how to capture spinner message? + # assert_match 'Successfully uninstalled extension' - assert_command dfx extension list - assert_match 'No extensions installed' + assert_command dfx extension list + assert_match 'No extensions installed' } @test "manually create extension" { - assert_command dfx extension list - assert_match 'No extensions installed' + assert_command dfx extension list + assert_match 'No extensions installed' - CACHE_DIR=$(dfx cache show) - mkdir -p "$CACHE_DIR"/extensions/test_extension - echo '#!/usr/bin/env bash + CACHE_DIR=$(dfx cache show) + mkdir -p "$CACHE_DIR"/extensions/test_extension + echo '#!/usr/bin/env bash echo testoutput' > "$CACHE_DIR"/extensions/test_extension/test_extension - chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension + chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension - assert_command_fail dfx extension list - assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" + assert_command_fail dfx extension list + assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" - assert_command_fail dfx extension run test_extension - assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" + assert_command_fail dfx extension run test_extension + assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" - assert_command_fail dfx test_extension - assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" + assert_command_fail dfx test_extension + assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" - assert_command_fail dfx --help - assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" + assert_command_fail dfx --help + assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" - assert_command_fail dfx test_extension --help - assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" + assert_command_fail dfx test_extension --help + assert_match "Error.*Cannot load extension manifest.*Failed to read JSON file.*Failed to read .*extensions/test_extension/extension.json.*No such file or directory" - echo "{}" > "$CACHE_DIR"/extensions/test_extension/extension.json + echo "{}" > "$CACHE_DIR"/extensions/test_extension/extension.json - assert_command_fail dfx extension list - assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" + assert_command_fail dfx extension list + assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" - assert_command_fail dfx extension run test_extension - assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" + assert_command_fail dfx extension run test_extension + assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" - assert_command_fail dfx test_extension - assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" + assert_command_fail dfx test_extension + assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" - assert_command_fail dfx --help - assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" + assert_command_fail dfx --help + assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" - assert_command_fail dfx test_extension --help - assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" + assert_command_fail dfx test_extension --help + assert_match "Error.*Cannot load extension manifest.*Failed to parse contents of .*extensions/test_extension/extension.json as json.* missing field .* at line .* column .*" - echo '{ + echo '{ "name": "test_extension", "version": "0.1.0", "homepage": "https://github.com/dfinity/dfx-extensions", @@ -92,45 +92,45 @@ echo testoutput' > "$CACHE_DIR"/extensions/test_extension/test_extension "keywords": [] }' > "$CACHE_DIR"/extensions/test_extension/extension.json - assert_command dfx --help - assert_match "test_extension.*Test extension for e2e purposes." + assert_command dfx --help + assert_match "test_extension.*Test extension for e2e purposes." - assert_command dfx test_extension --help - assert_match "Test extension for e2e purposes..*Usage: dfx test_extension" + assert_command dfx test_extension --help + assert_match "Test extension for e2e purposes..*Usage: dfx test_extension" - assert_command dfx extension list - assert_match "test_extension" + assert_command dfx extension list + assert_match "test_extension" - assert_command dfx extension run test_extension - assert_match "testoutput" + assert_command dfx extension run test_extension + assert_match "testoutput" - assert_command dfx test_extension - assert_match "testoutput" + assert_command dfx test_extension + assert_match "testoutput" - assert_command dfx extension uninstall test_extension - # TODO: how to capture spinner message? - # assert_match 'Successfully uninstalled extension' + assert_command dfx extension uninstall test_extension + # TODO: how to capture spinner message? + # assert_match 'Successfully uninstalled extension' - assert_command dfx extension list - assert_match 'No extensions installed' + assert_command dfx extension list + assert_match 'No extensions installed' } @test "run with hyphened parameters" { - CACHE_DIR=$(dfx cache show) - mkdir -p "$CACHE_DIR"/extensions/test_extension + CACHE_DIR=$(dfx cache show) + mkdir -p "$CACHE_DIR"/extensions/test_extension - cat > "$CACHE_DIR"/extensions/test_extension/test_extension << "EOF" + cat > "$CACHE_DIR"/extensions/test_extension/test_extension << "EOF" #!/usr/bin/env bash if [ "$2" == "--the-param" ]; then - echo "pamparam the param is $3" + echo "pamparam the param is $3" fi EOF - chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension + chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension - cat > "$CACHE_DIR"/extensions/test_extension/extension.json < "$CACHE_DIR"/extensions/test_extension/extension.json < "$CACHE_DIR"/extensions/test_extension/test_extension << "EOF" + cat > "$CACHE_DIR"/extensions/test_extension/test_extension << "EOF" #!/usr/bin/env bash echo $@ EOF - chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension + chmod +x "$CACHE_DIR"/extensions/test_extension/test_extension - cat > "$CACHE_DIR"/extensions/test_extension/extension.json < "$CACHE_DIR"/extensions/test_extension/extension.json <"$E2E_NETWORKS_JSON" - - assert_command dfx deploy - assert_match "e2e_project_backend: http://$CANDID_UI_ID.localhost:$PORT/\?id=$APP_ID" - assert_match "e2e_project_frontend: http://$ASSETS_ID.localhost:$PORT/" + dfx_start + PORT=$(get_webserver_port) + + assert_command dfx deploy + CANDID_UI_ID=$(dfx canister id __Candid_UI) + APP_ID=$(dfx canister id e2e_project_backend) + ASSETS_ID=$(dfx canister id e2e_project_frontend) + assert_match "e2e_project_backend: http://127.0.0.1:$PORT/\?canisterId=$CANDID_UI_ID&id=$APP_ID" + assert_match "e2e_project_frontend: http://127.0.0.1:$PORT/\?canisterId=$ASSETS_ID" + + # the urls are a little nicer if the bind address is localhost:8000 rather than 127.0.0.1:8000 + jq -n '.local.bind="localhost:'"$PORT"'"' >"$E2E_NETWORKS_JSON" + + assert_command dfx deploy + assert_match "e2e_project_backend: http://$CANDID_UI_ID.localhost:$PORT/\?id=$APP_ID" + assert_match "e2e_project_frontend: http://$ASSETS_ID.localhost:$PORT/" } @test "dfx start serves a frontend with static assets" { - skip "Need a build of @dfinity/agent that works with HTTP Query" - dfx_start - dfx canister create --all - dfx build - dfx canister install --all - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - assert_command curl http://localhost:"$PORT"/?canisterId="$ID" - assert_match "logo.png" + skip "Need a build of @dfinity/agent that works with HTTP Query" + dfx_start + dfx canister create --all + dfx build + dfx canister install --all + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + assert_command curl http://localhost:"$PORT"/?canisterId="$ID" + assert_match "logo.png" } @test "dfx start serves a frontend on a port" { - skip "Need a build of @dfinity/agent that works with HTTP Query" + skip "Need a build of @dfinity/agent that works with HTTP Query" - dfx_start --host 127.0.0.1:12345 + dfx_start --host 127.0.0.1:12345 - jq '.networks.local.bind="127.0.0.1:12345"' dfx.json | sponge dfx.json + jq '.networks.local.bind="127.0.0.1:12345"' dfx.json | sponge dfx.json - dfx canister create --all - dfx build - dfx canister install --all + dfx canister create --all + dfx build + dfx canister install --all - ID=$(dfx canister id e2e_project_frontend) - assert_command curl http://localhost:12345/?canisterId="$ID" - assert_match "" + ID=$(dfx canister id e2e_project_frontend) + assert_command curl http://localhost:12345/?canisterId="$ID" + assert_match "" - assert_command_fail curl http://localhost:8000 - assert_match "Connection refused" + assert_command_fail curl http://localhost:8000 + assert_match "Connection refused" } @test "dfx uses .ic-assets.json file provided in src/__project_name__frontend/src" { - echo '[{"match": "*", "headers": {"x-key": "x-value"}}]' > src/e2e_project_frontend/src/.ic-assets.json - - dfx_start - dfx canister create --all - dfx build - dfx canister install --all - - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) - assert_command curl -vv http://localhost:"$PORT"/?canisterId="$ID" - assert_match "< x-key: x-value" - assert_command curl -vv http://localhost:"$PORT"/index.js?canisterId="$ID" - assert_match "< x-key: x-value" + echo '[{"match": "*", "headers": {"x-key": "x-value"}}]' > src/e2e_project_frontend/src/.ic-assets.json + + dfx_start + dfx canister create --all + dfx build + dfx canister install --all + + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) + assert_command curl -vv http://localhost:"$PORT"/?canisterId="$ID" + assert_match "< x-key: x-value" + assert_command curl -vv http://localhost:"$PORT"/index.js?canisterId="$ID" + assert_match "< x-key: x-value" } @test "dfx uses a custom build command if one is provided" { - jq '.canisters.e2e_project_frontend.source = ["dist/e2e_project_frontend/"]' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_frontend.build = ["npm run custom-build"]' dfx.json | sponge dfx.json - jq '.scripts["custom-build"] = "mkdir -p ./dist/e2e_project_frontend/assets/ && cp -r ./src/e2e_project_frontend/assets/* ./dist/e2e_project_frontend"' package.json | sponge package.json + jq '.canisters.e2e_project_frontend.source = ["dist/e2e_project_frontend/"]' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_frontend.build = ["npm run custom-build"]' dfx.json | sponge dfx.json + jq '.scripts["custom-build"] = "mkdir -p ./dist/e2e_project_frontend/assets/ && cp -r ./src/e2e_project_frontend/assets/* ./dist/e2e_project_frontend"' package.json | sponge package.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - ID=$(dfx canister id e2e_project_frontend) - PORT=$(get_webserver_port) + ID=$(dfx canister id e2e_project_frontend) + PORT=$(get_webserver_port) - assert_command curl -vv http://localhost:"$PORT"/sample-asset.txt?canisterId="$ID" - assert_match "This is a sample asset!" + assert_command curl -vv http://localhost:"$PORT"/sample-asset.txt?canisterId="$ID" + assert_match "This is a sample asset!" } diff --git a/e2e/tests-dfx/generate.bash b/e2e/tests-dfx/generate.bash index 604fea0c0b..80e571c20d 100755 --- a/e2e/tests-dfx/generate.bash +++ b/e2e/tests-dfx/generate.bash @@ -3,148 +3,148 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx generate creates files" { - dfx_new hello - dfx_start - dfx canister create --all - dfx build - dfx canister install --all - - dfx generate - - assert_file_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_exists "src/declarations/hello_backend/index.js" - assert_file_exists "src/declarations/hello_backend/index.d.ts" + dfx_new hello + dfx_start + dfx canister create --all + dfx build + dfx canister install --all + + dfx generate + + assert_file_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_exists "src/declarations/hello_backend/index.js" + assert_file_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate creates only JS files" { - dfx_new hello - jq '.canisters.hello_backend.declarations.bindings=["js"]' dfx.json | sponge dfx.json + dfx_new hello + jq '.canisters.hello_backend.declarations.bindings=["js"]' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - dfx generate + dfx generate - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_exists "src/declarations/hello_backend/index.js" - assert_file_not_exists "src/declarations/hello_backend/index.d.ts" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_exists "src/declarations/hello_backend/index.js" + assert_file_not_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate creates only TS files" { - dfx_new hello - jq '.canisters.hello_backend.declarations.bindings=["ts"]' dfx.json | sponge dfx.json + dfx_new hello + jq '.canisters.hello_backend.declarations.bindings=["ts"]' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - dfx generate + dfx generate - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_not_exists "src/declarations/hello_backend/index.js" - assert_file_exists "src/declarations/hello_backend/index.d.ts" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_not_exists "src/declarations/hello_backend/index.js" + assert_file_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate creates only JS & TS files" { - dfx_new hello - jq '.canisters.hello_backend.declarations.bindings=["js", "ts"]' dfx.json | sponge dfx.json + dfx_new hello + jq '.canisters.hello_backend.declarations.bindings=["js", "ts"]' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - dfx generate + dfx generate - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_exists "src/declarations/hello_backend/index.js" - assert_file_exists "src/declarations/hello_backend/index.d.ts" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_exists "src/declarations/hello_backend/index.js" + assert_file_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate creates only DID files" { - dfx_new hello - jq '.canisters.hello_backend.declarations.bindings=["did"]' dfx.json | sponge dfx.json + dfx_new hello + jq '.canisters.hello_backend.declarations.bindings=["did"]' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - dfx generate + dfx generate - assert_file_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_not_exists "src/declarations/hello_backend/index.js" - assert_file_not_exists "src/declarations/hello_backend/index.d.ts" + assert_file_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_not_exists "src/declarations/hello_backend/index.js" + assert_file_not_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate does not create any files" { - dfx_new hello - jq '.canisters.hello_backend.declarations.bindings=[]' dfx.json | sponge dfx.json + dfx_new hello + jq '.canisters.hello_backend.declarations.bindings=[]' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - dfx build - dfx canister install --all + dfx_start + dfx canister create --all + dfx build + dfx canister install --all - dfx generate + dfx generate - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" - assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" - assert_file_not_exists "src/declarations/hello_backend/index.js" - assert_file_not_exists "src/declarations/hello_backend/index.d.ts" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.js" + assert_file_not_exists "src/declarations/hello_backend/hello_backend.did.d.ts" + assert_file_not_exists "src/declarations/hello_backend/index.js" + assert_file_not_exists "src/declarations/hello_backend/index.d.ts" } @test "dfx generate succeeds with an encrypted identity without input" { - dfx_new hello - dfx_start - dfx canister create --all - - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" - assert_command dfx identity use alice - - assert_command timeout 30s dfx generate + dfx_new hello + dfx_start + dfx canister create --all + + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" + assert_command dfx identity use alice + + assert_command timeout 30s dfx generate } @test "dfx generate does not require canister IDs for non-Motoko canisters" { - dfx_new_rust hello - assert_command dfx generate + dfx_new_rust hello + assert_command dfx generate } @test "dfx generate does not require canister IDs for Motoko canisters" { - dfx_new hello - assert_command dfx generate + dfx_new hello + assert_command dfx generate } @test "dfx generate --network is still valid" { - # The option has no effect, but is still accepted to not break existing scripts - dfx_new hello - assert_command dfx generate --network local + # The option has no effect, but is still accepted to not break existing scripts + dfx_new hello + assert_command dfx generate --network local - # Option is not advertised anymore - assert_command dfx generate --help - assert_not_contains "--network" + # Option is not advertised anymore + assert_command dfx generate --help + assert_not_contains "--network" } diff --git a/e2e/tests-dfx/id.bash b/e2e/tests-dfx/id.bash index f019a3996a..9a46066db6 100644 --- a/e2e/tests-dfx/id.bash +++ b/e2e/tests-dfx/id.bash @@ -3,96 +3,96 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "id subcommand prints valid canister identifier" { - install_asset id - dfx_start - dfx canister create --all - dfx build - assert_command dfx canister id e2e_project_backend - assert_match "$(jq -r .e2e_project_backend.local < .dfx/local/canister_ids.json)" + install_asset id + dfx_start + dfx canister create --all + dfx build + assert_command dfx canister id e2e_project_backend + assert_match "$(jq -r .e2e_project_backend.local < .dfx/local/canister_ids.json)" } @test "id subcommand does not display warning about plaintext keys" { - install_asset id - dfx identity get-principal - echo "{}" | jq '.e2e_project_backend.ic = "bd3sg-teaaa-aaaaa-qaaba-cai"' >canister_ids.json - assert_command dfx canister id e2e_project_backend --ic - assert_eq "bd3sg-teaaa-aaaaa-qaaba-cai" + install_asset id + dfx identity get-principal + echo "{}" | jq '.e2e_project_backend.ic = "bd3sg-teaaa-aaaaa-qaaba-cai"' >canister_ids.json + assert_command dfx canister id e2e_project_backend --ic + assert_eq "bd3sg-teaaa-aaaaa-qaaba-cai" } @test "id subcommand works from a subdirectory of the project - ephemeral id" { - install_asset id - dfx_start - dfx canister create --all - ID=$(dfx canister id e2e_project_backend) - echo "canister id is $ID" + install_asset id + dfx_start + dfx canister create --all + ID=$(dfx canister id e2e_project_backend) + echo "canister id is $ID" - ( - cd src - dfx canister id e2e_project_backend - assert_command dfx canister id e2e_project_backend - assert_eq "$ID" - ) + ( + cd src + dfx canister id e2e_project_backend + assert_command dfx canister id e2e_project_backend + assert_eq "$ID" + ) } @test "id subcommand works from a subdirectory of the project - persistent id" { - install_asset id + install_asset id - jq '.networks.local.type="persistent"' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - ID=$(dfx canister id e2e_project_backend) - echo "canister id is $ID" - ( - cd src - dfx canister id e2e_project_backend - assert_command dfx canister id e2e_project_backend - assert_eq "$ID" - ) + jq '.networks.local.type="persistent"' dfx.json | sponge dfx.json + dfx_start + dfx canister create --all + ID=$(dfx canister id e2e_project_backend) + echo "canister id is $ID" + ( + cd src + dfx canister id e2e_project_backend + assert_command dfx canister id e2e_project_backend + assert_eq "$ID" + ) } @test "id subcommand uses default network for remotes only" { - install_asset id - install_shared_asset subnet_type/shared_network_settings/application - # Add a remote canister with a specific ID for one network and a different default for other networks. - jq '.canisters.external_canister = { - "build": "", - "candid": "candid/external_canister.did", - "remote": { - "id": { - "namedremote": "va76m-bqaaa-aaaaa-aaayq-cai", - "__default": "rkp4c-7iaaa-aaaaa-aaaca-cai" - } - }, - "type": "custom", - "wasm": "" - }' dfx.json | sponge dfx.json - # We need to define the networks we are going to use: - jq '.namedremote= {"type": "persistent", "providers": ["http://namedremote"]}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - jq '.somethingelse= {"type": "persistent", "providers": ["http://somethingelse"]}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - cat dfx.json - cat "$E2E_NETWORKS_JSON" - # Ok, start: - dfx_start || true - dfx canister create --all - # The local dfx canister ID should not be affected: - assert_command dfx canister id e2e_project_backend - assert_match "$(jq -r .e2e_project_backend.local < .dfx/local/canister_ids.json)" - # Named remotes should be unaffected: - assert_command dfx canister --network namedremote id external_canister - assert_match "va76m-bqaaa-aaaaa-aaayq-cai" - # Other remotes should use the default entry: - assert_command dfx canister --network somethingelse id external_canister - assert_match "rkp4c-7iaaa-aaaaa-aaaca-cai" + install_asset id + install_shared_asset subnet_type/shared_network_settings/application + # Add a remote canister with a specific ID for one network and a different default for other networks. + jq '.canisters.external_canister = { + "build": "", + "candid": "candid/external_canister.did", + "remote": { + "id": { + "namedremote": "va76m-bqaaa-aaaaa-aaayq-cai", + "__default": "rkp4c-7iaaa-aaaaa-aaaca-cai" + } + }, + "type": "custom", + "wasm": "" + }' dfx.json | sponge dfx.json + # We need to define the networks we are going to use: + jq '.namedremote= {"type": "persistent", "providers": ["http://namedremote"]}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + jq '.somethingelse= {"type": "persistent", "providers": ["http://somethingelse"]}' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + cat dfx.json + cat "$E2E_NETWORKS_JSON" + # Ok, start: + dfx_start || true + dfx canister create --all + # The local dfx canister ID should not be affected: + assert_command dfx canister id e2e_project_backend + assert_match "$(jq -r .e2e_project_backend.local < .dfx/local/canister_ids.json)" + # Named remotes should be unaffected: + assert_command dfx canister --network namedremote id external_canister + assert_match "va76m-bqaaa-aaaaa-aaayq-cai" + # Other remotes should use the default entry: + assert_command dfx canister --network somethingelse id external_canister + assert_match "rkp4c-7iaaa-aaaaa-aaaca-cai" } diff --git a/e2e/tests-dfx/identity.bash b/e2e/tests-dfx/identity.bash index 4946491fa7..a39681152f 100644 --- a/e2e/tests-dfx/identity.bash +++ b/e2e/tests-dfx/identity.bash @@ -3,198 +3,198 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "identity new: name validation" { - assert_command_fail dfx identity new iden%tity --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new iden%tity --storage-mode plaintext + assert_match "Invalid identity name" - assert_command_fail dfx identity new 'iden tity' --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new 'iden tity' --storage-mode plaintext + assert_match "Invalid identity name" - assert_command_fail dfx identity new "iden\$tity" --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new "iden\$tity" --storage-mode plaintext + assert_match "Invalid identity name" - assert_command_fail dfx identity new iden\\tity --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new iden\\tity --storage-mode plaintext + assert_match "Invalid identity name" - assert_command_fail dfx identity new 'iden\ttity' --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new 'iden\ttity' --storage-mode plaintext + assert_match "Invalid identity name" - assert_command_fail dfx identity new iden/tity --storage-mode plaintext - assert_match "Invalid identity name" + assert_command_fail dfx identity new iden/tity --storage-mode plaintext + assert_match "Invalid identity name" - assert_command dfx identity new i_den.ti-ty --storage-mode plaintext + assert_command dfx identity new i_den.ti-ty --storage-mode plaintext - assert_command dfx identity new i_den@ti-ty --storage-mode plaintext + assert_command dfx identity new i_den@ti-ty --storage-mode plaintext } @test "identity get-principal: the get-principal is the same as sender id" { - install_asset identity - dfx_start - assert_command dfx identity new --storage-mode plaintext jose + install_asset identity + dfx_start + assert_command dfx identity new --storage-mode plaintext jose - PRINCIPAL_ID=$(dfx identity get-principal --identity jose) + PRINCIPAL_ID=$(dfx identity get-principal --identity jose) - dfx canister create e2e_project_backend --identity jose - dfx build e2e_project_backend --identity jose - dfx canister install e2e_project_backend --identity jose + dfx canister create e2e_project_backend --identity jose + dfx build e2e_project_backend --identity jose + dfx canister install e2e_project_backend --identity jose - assert_command dfx canister call e2e_project_backend amInitializer --identity jose + assert_command dfx canister call e2e_project_backend amInitializer --identity jose - SENDER_ID=$(dfx canister call e2e_project_backend fromCall --identity jose) + SENDER_ID=$(dfx canister call e2e_project_backend fromCall --identity jose) - if [ "$PRINCIPAL_ID" -ne "$SENDER_ID" ]; then - echo "IDs did not match: Principal '${PRINCIPAL_ID}' != Sender '${SENDER_ID}'..." | fail - fi + if [ "$PRINCIPAL_ID" -ne "$SENDER_ID" ]; then + echo "IDs did not match: Principal '${PRINCIPAL_ID}' != Sender '${SENDER_ID}'..." | fail + fi } @test "identity get-principal (anonymous): the get-principal is the same as sender id" { - install_asset identity - dfx_start - assert_command dfx identity new --storage-mode plaintext jose + install_asset identity + dfx_start + assert_command dfx identity new --storage-mode plaintext jose - ANONYMOUS_PRINCIPAL_ID="2vxsx-fae" + ANONYMOUS_PRINCIPAL_ID="2vxsx-fae" - PRINCIPAL_ID=$(dfx identity get-principal --identity anonymous) + PRINCIPAL_ID=$(dfx identity get-principal --identity anonymous) - if [ "$PRINCIPAL_ID" -ne "$ANONYMOUS_PRINCIPAL_ID" ]; then - echo "IDs did not match: Principal '${ANONYMOUS_PRINCIPAL_ID}' != Sender '${PRINCIPAL_ID}'..." | fail - fi + if [ "$PRINCIPAL_ID" -ne "$ANONYMOUS_PRINCIPAL_ID" ]; then + echo "IDs did not match: Principal '${ANONYMOUS_PRINCIPAL_ID}' != Sender '${PRINCIPAL_ID}'..." | fail + fi - dfx canister create e2e_project_backend --identity jose - dfx build e2e_project_backend --identity jose - dfx canister install e2e_project_backend --identity jose + dfx canister create e2e_project_backend --identity jose + dfx build e2e_project_backend --identity jose + dfx canister install e2e_project_backend --identity jose - SENDER_ID=$(dfx canister call e2e_project_backend fromCall --identity anonymous) + SENDER_ID=$(dfx canister call e2e_project_backend fromCall --identity anonymous) - if [ "$ANONYMOUS_PRINCIPAL_ID" -ne "$SENDER_ID" ]; then - echo "IDs did not match: Principal '${ANONYMOUS_PRINCIPAL_ID}' != Sender '${SENDER_ID}'..." | fail - fi + if [ "$ANONYMOUS_PRINCIPAL_ID" -ne "$SENDER_ID" ]; then + echo "IDs did not match: Principal '${ANONYMOUS_PRINCIPAL_ID}' != Sender '${SENDER_ID}'..." | fail + fi } @test "calls and query receive the same principal from dfx" { - install_asset identity - dfx_start - dfx canister create --all - assert_command dfx build - assert_command dfx canister install --all - - ID_CALL=$(dfx canister call e2e_project_backend fromCall) - ID_QUERY=$(dfx canister call e2e_project_backend fromQuery) - if [ "$ID_CALL" -ne "$ID_QUERY" ]; then - echo "IDs did not match: call '${ID_CALL}' != query '${ID_QUERY}'..." | fail - fi - - ID=$(dfx canister call e2e_project_backend getCanisterId) - assert_command dfx canister call e2e_project_backend isMyself "$ID" - assert_eq '(true)' - assert_command dfx canister call e2e_project_backend isMyself "$ID_CALL" - assert_eq '(false)' + install_asset identity + dfx_start + dfx canister create --all + assert_command dfx build + assert_command dfx canister install --all + + ID_CALL=$(dfx canister call e2e_project_backend fromCall) + ID_QUERY=$(dfx canister call e2e_project_backend fromQuery) + if [ "$ID_CALL" -ne "$ID_QUERY" ]; then + echo "IDs did not match: call '${ID_CALL}' != query '${ID_QUERY}'..." | fail + fi + + ID=$(dfx canister call e2e_project_backend getCanisterId) + assert_command dfx canister call e2e_project_backend isMyself "$ID" + assert_eq '(true)' + assert_command dfx canister call e2e_project_backend isMyself "$ID_CALL" + assert_eq '(false)' } @test "dfx ping does not create a default identity" { - dfx_start + dfx_start - assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity.json" - assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" + assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity.json" + assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" - assert_command dfx ping + assert_command dfx ping - assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity.json" - assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" + assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity.json" + assert_file_not_exists "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" - # shellcheck disable=SC2154 - assert_not_match 'Creating' "$stderr" - # shellcheck disable=SC2154 - assert_not_match '(default.*identity|identity.*default)' "$stderr" - # shellcheck disable=SC2154 - assert_match "ic_api_version" "$stdout" + # shellcheck disable=SC2154 + assert_not_match 'Creating' "$stderr" + # shellcheck disable=SC2154 + assert_not_match '(default.*identity|identity.*default)' "$stderr" + # shellcheck disable=SC2154 + assert_match "ic_api_version" "$stdout" } @test "dfx canister: creates the default identity on first run" { - install_asset identity - dfx_start - assert_command dfx canister create e2e_project_backend - assert_match 'Creating the "default" identity.' "$stderr" + install_asset identity + dfx_start + assert_command dfx canister create e2e_project_backend + assert_match 'Creating the "default" identity.' "$stderr" } @test "after using a specific identity while creating a canister, that user is the initializer" { - install_asset identity - dfx_start - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob - - dfx canister create --all --identity alice - assert_command dfx build --identity alice - assert_command dfx canister install --all --identity alice - - # The user Identity's principal is the initializer - assert_command dfx canister call e2e_project_backend amInitializer --identity alice - assert_eq '(true)' - - assert_command dfx canister call e2e_project_backend amInitializer --identity bob - assert_eq '(false)' - - # these all fail (other identities are not initializer; cannot store assets): - assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity bob - assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity default - assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' - assert_command_fail dfx canister call e2e_project_frontend retrieve '("B")' - - # but alice, the initializer, can store assets: - assert_command dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity alice - assert_eq '()' - assert_command dfx canister call --output idl e2e_project_frontend retrieve '("B")' - assert_eq '(blob "XWV")' + install_asset identity + dfx_start + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + + dfx canister create --all --identity alice + assert_command dfx build --identity alice + assert_command dfx canister install --all --identity alice + + # The user Identity's principal is the initializer + assert_command dfx canister call e2e_project_backend amInitializer --identity alice + assert_eq '(true)' + + assert_command dfx canister call e2e_project_backend amInitializer --identity bob + assert_eq '(false)' + + # these all fail (other identities are not initializer; cannot store assets): + assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity bob + assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity default + assert_command_fail dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' + assert_command_fail dfx canister call e2e_project_frontend retrieve '("B")' + + # but alice, the initializer, can store assets: + assert_command dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=vec { 88; 87; 86; }})' --identity alice + assert_eq '()' + assert_command dfx canister call --output idl e2e_project_frontend retrieve '("B")' + assert_eq '(blob "XWV")' } @test "after renaming an identity, the renamed identity is still initializer" { - install_asset identity - dfx_start - assert_command dfx identity new --storage-mode plaintext alice - - dfx canister create --all --identity alice - assert_command dfx build --identity alice - assert_command dfx canister install --all --identity alice - assert_command dfx canister call e2e_project_backend amInitializer --identity alice - assert_eq '(true)' - assert_command dfx canister call e2e_project_backend amInitializer - assert_eq '(false)' - - assert_command dfx identity rename alice bob - - assert_command dfx identity whoami - assert_eq 'default' - assert_command dfx canister call e2e_project_backend amInitializer --identity bob - assert_eq '(true)' - - assert_command dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=blob "hello"})' --identity bob - assert_eq '()' - assert_command dfx canister call --output idl e2e_project_frontend retrieve '("B")' - assert_eq '(blob "hello")' + install_asset identity + dfx_start + assert_command dfx identity new --storage-mode plaintext alice + + dfx canister create --all --identity alice + assert_command dfx build --identity alice + assert_command dfx canister install --all --identity alice + assert_command dfx canister call e2e_project_backend amInitializer --identity alice + assert_eq '(true)' + assert_command dfx canister call e2e_project_backend amInitializer + assert_eq '(false)' + + assert_command dfx identity rename alice bob + + assert_command dfx identity whoami + assert_eq 'default' + assert_command dfx canister call e2e_project_backend amInitializer --identity bob + assert_eq '(true)' + + assert_command dfx canister call e2e_project_frontend store '(record{key="B"; content_type="application/octet-stream"; content_encoding="identity"; content=blob "hello"})' --identity bob + assert_eq '()' + assert_command dfx canister call --output idl e2e_project_frontend retrieve '("B")' + assert_eq '(blob "hello")' } @test "using an unencrypted identity on mainnet provokes a warning" { - assert_command dfx ledger balance --network ic - assert_match "WARN: The default identity is not stored securely." "$stderr" - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/get_ledger_balance.exp" - dfx identity new bob --storage-mode plaintext - assert_command dfx ledger balance --network ic --identity bob - assert_match "WARN: The bob identity is not stored securely." "$stderr" - - export DFX_WARNING=-mainnet_plaintext_identity - assert_command dfx ledger balance --network ic --identity bob - assert_not_contains "not stored securely" "$stderr" + assert_command dfx ledger balance --network ic + assert_match "WARN: The default identity is not stored securely." "$stderr" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/get_ledger_balance.exp" + dfx identity new bob --storage-mode plaintext + assert_command dfx ledger balance --network ic --identity bob + assert_match "WARN: The bob identity is not stored securely." "$stderr" + + export DFX_WARNING=-mainnet_plaintext_identity + assert_command dfx ledger balance --network ic --identity bob + assert_not_contains "not stored securely" "$stderr" } diff --git a/e2e/tests-dfx/identity_command.bash b/e2e/tests-dfx/identity_command.bash index dd5cea5649..f02119f122 100644 --- a/e2e/tests-dfx/identity_command.bash +++ b/e2e/tests-dfx/identity_command.bash @@ -3,12 +3,12 @@ load ../utils/_ setup() { - standard_setup - export DFX_CI_MOCK_KEYRING_LOCATION="$MOCK_KEYRING_LOCATION" + standard_setup + export DFX_CI_MOCK_KEYRING_LOCATION="$MOCK_KEYRING_LOCATION" } teardown() { - standard_teardown + standard_teardown } ## @@ -16,15 +16,15 @@ teardown() { ## @test "identity get-principal: different identities have different principal ids" { - assert_command dfx identity new jose - assert_command dfx identity new juana + assert_command dfx identity new jose + assert_command dfx identity new juana - PRINCIPAL_ID_JOSE=$(dfx identity get-principal --identity jose) - PRINCIPAL_ID_JUANA=$(dfx identity get-principal --identity juana) + PRINCIPAL_ID_JOSE=$(dfx identity get-principal --identity jose) + PRINCIPAL_ID_JUANA=$(dfx identity get-principal --identity juana) - if [ "$PRINCIPAL_ID_JOSE" -eq "$PRINCIPAL_ID_JUANA" ]; then - echo "IDs should not match: Jose '${PRINCIPAL_ID_JOSE}' == Juana '${PRINCIPAL_ID_JUANA}'..." | fail - fi + if [ "$PRINCIPAL_ID_JOSE" -eq "$PRINCIPAL_ID_JUANA" ]; then + echo "IDs should not match: Jose '${PRINCIPAL_ID_JOSE}' == Juana '${PRINCIPAL_ID_JUANA}'..." | fail + fi } ## @@ -32,21 +32,21 @@ teardown() { ## @test "identity list: shows identities in alpha order" { - assert_command dfx identity new dan - assert_command dfx identity new frank - assert_command dfx identity new alice - assert_command dfx identity new bob - assert_command dfx identity list - assert_match \ + assert_command dfx identity new dan + assert_command dfx identity new frank + assert_command dfx identity new alice + assert_command dfx identity new bob + assert_command dfx identity list + assert_match \ 'alice anonymous bob dan default frank' - assert_command dfx identity new charlie - assert_command dfx identity list - assert_match \ + assert_command dfx identity new charlie + assert_command dfx identity list + assert_match \ 'alice anonymous bob @@ -57,16 +57,16 @@ frank' } @test "identity list: shows the anonymous identity" { - assert_command dfx identity list - # shellcheck disable=SC2154 - assert_match 'anonymous' "$stdout" + assert_command dfx identity list + # shellcheck disable=SC2154 + assert_match 'anonymous' "$stdout" } @test "identity list: shows the default identity" { - assert_command dfx identity list - assert_match 'default' "$stdout" - # shellcheck disable=SC2154 - assert_match 'Creating the "default" identity.' "$stderr" + assert_command dfx identity list + assert_match 'default' "$stdout" + # shellcheck disable=SC2154 + assert_match 'Creating the "default" identity.' "$stderr" } ## @@ -74,76 +74,76 @@ frank' ## @test "identity new: creates a new keyring identity" { - assert_command dfx identity new alice - assert_match 'Created identity: "alice".' "$stderr" - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_match "internet_computer_identity_alice" + assert_command dfx identity new alice + assert_match 'Created identity: "alice".' "$stderr" + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_match "internet_computer_identity_alice" - # does not change the default identity - assert_command dfx identity whoami - assert_eq 'default' + # does not change the default identity + assert_command dfx identity whoami + assert_eq 'default' } @test "identity new --storage-mode plaintext: creates a new identity" { - assert_command dfx identity new alice --storage-mode plaintext - assert_match 'Created identity: "alice".' "$stderr" - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" + assert_command dfx identity new alice --storage-mode plaintext + assert_match 'Created identity: "alice".' "$stderr" + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" - # does not change the default identity - assert_command dfx identity whoami - assert_eq 'default' + # does not change the default identity + assert_command dfx identity whoami + assert_eq 'default' } @test "identity new: cannot create an identity called anonymous" { - assert_command_fail dfx identity new anonymous + assert_command_fail dfx identity new anonymous } @test "identity new: cannot create an identity that already exists" { - assert_command dfx identity new bob - assert_command_fail dfx identity new bob - assert_match "Identity already exists" + assert_command dfx identity new bob + assert_command_fail dfx identity new bob + assert_match "Identity already exists" } @test "identity new: --force re-creates an identity" { - assert_command dfx identity new alice - dfx identity use alice - PRINCIPAL_1="$(dfx identity get-principal)" - assert_command dfx identity new --force alice - PRINCIPAL_2="$(dfx identity get-principal)" - assert_neq "$PRINCIPAL_1" "$PRINCIPAL_2" + assert_command dfx identity new alice + dfx identity use alice + PRINCIPAL_1="$(dfx identity get-principal)" + assert_command dfx identity new --force alice + PRINCIPAL_2="$(dfx identity get-principal)" + assert_neq "$PRINCIPAL_1" "$PRINCIPAL_2" } @test "identity new: --force does not switch to created identity" { - # Was a bug: https://dfinity.atlassian.net/browse/SDK-911 - assert_command dfx identity new --force alice - PRINCIPAL_ORIGINAL="$(dfx identity get-principal)" - assert_command dfx identity use alice - PRINCIPAL_ALICE="$(dfx identity get-principal)" - assert_neq "$PRINCIPAL_ORIGINAL" "$PRINCIPAL_ALICE" + # Was a bug: https://dfinity.atlassian.net/browse/SDK-911 + assert_command dfx identity new --force alice + PRINCIPAL_ORIGINAL="$(dfx identity get-principal)" + assert_command dfx identity use alice + PRINCIPAL_ALICE="$(dfx identity get-principal)" + assert_neq "$PRINCIPAL_ORIGINAL" "$PRINCIPAL_ALICE" } @test "identity new: create an HSM-backed identity" { - assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob - assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" - assert_eq "/something/else/somewhere.so" - assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" - assert_eq "abcd4321" + assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob + assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" + assert_eq "/something/else/somewhere.so" + assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" + assert_eq "abcd4321" } @test "identity new: key_id must be hex digits" { - assert_command_fail dfx identity new --hsm-pkcs11-lib-path xxx --hsm-key-id abcx bob - assert_match "Key id must contain only hex digits" + assert_command_fail dfx identity new --hsm-pkcs11-lib-path xxx --hsm-key-id abcx bob + assert_match "Key id must contain only hex digits" } @test "identity new: key_id must be an even number of digits" { - assert_command_fail dfx identity new --hsm-pkcs11-lib-path xxx --hsm-key-id fed64 bob - assert_match "Key id must consist of an even number of hex digits" + assert_command_fail dfx identity new --hsm-pkcs11-lib-path xxx --hsm-key-id fed64 bob + assert_match "Key id must consist of an even number of hex digits" } @test "identity new: key is compatible with openssl" { - assert_command dfx identity new --storage-mode plaintext bob - assert_command openssl ec -in "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" + assert_command dfx identity new --storage-mode plaintext bob + assert_command openssl ec -in "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" } ## @@ -151,99 +151,99 @@ frank' ## @test "identity remove: can remove an identity that exists" { - assert_command_fail cat "$MOCK_KEYRING_LOCATION" - assert_command dfx identity new alice + assert_command_fail cat "$MOCK_KEYRING_LOCATION" + assert_command dfx identity new alice - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_match "internet_computer_identity_alice" - assert_command dfx identity list - assert_match \ + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_match "internet_computer_identity_alice" + assert_command dfx identity list + assert_match \ 'alice anonymous default' - assert_command dfx identity remove alice - assert_match 'Removed identity "alice".' "$stderr" - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_not_match "internet_computer_identity_alice" + assert_command dfx identity remove alice + assert_match 'Removed identity "alice".' "$stderr" + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_not_match "internet_computer_identity_alice" - assert_command dfx identity list - assert_match 'default' + assert_command dfx identity list + assert_match 'default' } @test "identity remove --storage-mode plaintext: can remove an identity that exists" { - assert_command_fail head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_command dfx identity new alice --storage-mode plaintext + assert_command_fail head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_command dfx identity new alice --storage-mode plaintext - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" - assert_command dfx identity list - assert_match \ + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" + assert_command dfx identity list + assert_match \ 'alice anonymous default' - assert_command dfx identity remove alice - assert_match 'Removed identity "alice".' "$stderr" - assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_command dfx identity remove alice + assert_match 'Removed identity "alice".' "$stderr" + assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_command dfx identity list - assert_match 'default' + assert_command dfx identity list + assert_match 'default' } @test "identity remove: reports an error if no such identity" { - assert_command_fail dfx identity remove charlie + assert_command_fail dfx identity remove charlie } @test "identity remove: only remove identities with configured wallet if --drop-wallets is specified" { - # There's no replica running, and no real wallet. This is just a valid principal. - WALLET="rwlgt-iiaaa-aaaaa-aaaaa-cai" - assert_command dfx identity new alice - assert_command dfx identity use alice - assert_command dfx identity set-wallet --force "$WALLET" --network ic - assert_command dfx identity use default - assert_command_fail dfx identity remove alice - # make sure the configured wallet is displayed - assert_match "identity 'alice' on network 'ic' has wallet $WALLET" - assert_command dfx identity remove alice --drop-wallets - assert_match "identity 'alice' on network 'ic' has wallet $WALLET" + # There's no replica running, and no real wallet. This is just a valid principal. + WALLET="rwlgt-iiaaa-aaaaa-aaaaa-cai" + assert_command dfx identity new alice + assert_command dfx identity use alice + assert_command dfx identity set-wallet --force "$WALLET" --network ic + assert_command dfx identity use default + assert_command_fail dfx identity remove alice + # make sure the configured wallet is displayed + assert_match "identity 'alice' on network 'ic' has wallet $WALLET" + assert_command dfx identity remove alice --drop-wallets + assert_match "identity 'alice' on network 'ic' has wallet $WALLET" } @test "identity remove: cannot remove the non-default active identity" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity use alice - assert_command_fail dfx identity remove alice - - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" - assert_command dfx identity list - assert_match \ + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity use alice + assert_command_fail dfx identity remove alice + + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" + assert_command dfx identity list + assert_match \ 'alice anonymous default' } @test "identity remove: cannot remove the default identity" { - # a new one will just get created again - assert_command_fail dfx identity remove default - assert_match "Cannot delete the default identity" + # a new one will just get created again + assert_command_fail dfx identity remove default + assert_match "Cannot delete the default identity" } @test "identity remove: cannot remove the anonymous identity" { - assert_command_fail dfx identity remove anonymous + assert_command_fail dfx identity remove anonymous } @test "identity remove: can remove an HSM-backed identity" { - assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob - assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" - assert_eq "/something/else/somewhere.so" - assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" - assert_eq "abcd4321" - assert_command ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" + assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob + assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" + assert_eq "/something/else/somewhere.so" + assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.json" + assert_eq "abcd4321" + assert_command ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" - assert_command dfx identity remove bob - assert_command_fail ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" + assert_command dfx identity remove bob + assert_command_fail ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" } ## @@ -251,110 +251,110 @@ default' ## @test "identity rename: can rename an identity" { - assert_command dfx identity new alice - assert_command dfx identity list - assert_match \ + assert_command dfx identity new alice + assert_command dfx identity list + assert_match \ 'alice anonymous default' - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_match "internet_computer_identity_alice" - KEY="$(dfx identity export alice)" + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_match "internet_computer_identity_alice" + KEY="$(dfx identity export alice)" - assert_command dfx identity rename alice bob - assert_match 'Renamed identity "alice" to "bob".' "$stderr" + assert_command dfx identity rename alice bob + assert_match 'Renamed identity "alice" to "bob".' "$stderr" - assert_command dfx identity list - assert_match \ + assert_command dfx identity list + assert_match \ 'anonymous bob default' - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_match "internet_computer_identity_bob" - assert_eq "$KEY" "$(dfx identity export bob)" - assert_command cat "$MOCK_KEYRING_LOCATION" - assert_not_match "internet_computer_identity_alice" + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_match "internet_computer_identity_bob" + assert_eq "$KEY" "$(dfx identity export bob)" + assert_command cat "$MOCK_KEYRING_LOCATION" + assert_not_match "internet_computer_identity_alice" } @test "identity rename --storage-mode plaintext: can rename an identity" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity list - assert_match \ + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity list + assert_match \ 'alice anonymous default' - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" - x=$(cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem") - KEY="$x" + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" + x=$(cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem") + KEY="$x" - assert_command dfx identity rename alice bob - assert_match 'Renamed identity "alice" to "bob".' "$stderr" + assert_command dfx identity rename alice bob + assert_match 'Renamed identity "alice" to "bob".' "$stderr" - assert_command dfx identity list - assert_match \ + assert_command dfx identity list + assert_match \ 'anonymous bob default' - assert_command cat "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" - assert_eq "$KEY" "$(cat "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem")" - assert_match "BEGIN EC PRIVATE KEY" - assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_command cat "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" + assert_eq "$KEY" "$(cat "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem")" + assert_match "BEGIN EC PRIVATE KEY" + assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" } @test "identity rename: can rename the default identity, which also changes the default" { - assert_command dfx identity list - assert_match 'default' - assert_command dfx identity rename default bob - assert_command dfx identity list - assert_match 'bob' - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" + assert_command dfx identity list + assert_match 'default' + assert_command dfx identity rename default bob + assert_command dfx identity list + assert_match 'bob' + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" - assert_command dfx identity whoami - assert_eq 'bob' + assert_command dfx identity whoami + assert_eq 'bob' } @test "identity rename: can rename the selected identity, which also changes the default" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity use alice - assert_command dfx identity list - assert_match \ + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity use alice + assert_command dfx identity list + assert_match \ 'alice anonymous default' - assert_command dfx identity rename alice charlie + assert_command dfx identity rename alice charlie - assert_command dfx identity list - assert_match \ + assert_command dfx identity list + assert_match \ 'anonymous charlie default' - assert_command dfx identity whoami - assert_eq 'charlie' + assert_command dfx identity whoami + assert_eq 'charlie' - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/charlie/identity.pem" - assert_match "BEGIN EC PRIVATE KEY" - assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/charlie/identity.pem" + assert_match "BEGIN EC PRIVATE KEY" + assert_command_fail cat "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" } @test "identity rename: cannot create an anonymous identity via rename" { assert_command dfx identity new alice - assert_command_fail dfx identity rename alice anonymous - assert_match "Cannot create an anonymous identity" + assert_command_fail dfx identity rename alice anonymous + assert_match "Cannot create an anonymous identity" } @test "identity rename: can rename an HSM-backed identity" { - skip "Need to instantiate identity when renaming so skipping until we have an hsm mock" - assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob - assert_command dfx identity rename bob alice - assert_command_fail ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" + skip "Need to instantiate identity when renaming so skipping until we have an hsm mock" + assert_command dfx identity new --hsm-pkcs11-lib-path /something/else/somewhere.so --hsm-key-id abcd4321 bob + assert_command dfx identity rename bob alice + assert_command_fail ls "$DFX_CONFIG_ROOT/.config/dfx/identity/bob" - assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.json" - assert_eq "/something/else/somewhere.so" - assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.json" - assert_eq "abcd4321" + assert_command jq -r .hsm.pkcs11_lib_path "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.json" + assert_eq "/something/else/somewhere.so" + assert_command jq -r .hsm.key_id "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.json" + assert_eq "abcd4321" } ## @@ -362,46 +362,46 @@ default' ## @test "identity use: switches to an existing identity" { - assert_command dfx identity new alice - assert_command dfx identity whoami - assert_eq 'default' - assert_command dfx identity use alice - assert_command dfx identity whoami - assert_eq 'alice' + assert_command dfx identity new alice + assert_command dfx identity whoami + assert_eq 'default' + assert_command dfx identity use alice + assert_command dfx identity whoami + assert_eq 'alice' - ## and back - assert_command dfx identity use default - assert_command dfx identity whoami - assert_eq 'default' + ## and back + assert_command dfx identity use default + assert_command dfx identity whoami + assert_eq 'default' } @test "identity use: cannot use an identity that has not been created yet" { - assert_command_fail dfx identity use alice - assert_command dfx identity whoami - assert_eq 'default' + assert_command_fail dfx identity use alice + assert_command dfx identity whoami + assert_eq 'default' } @test "identity use: can switch to the anonymous identity" { - assert_command dfx identity use anonymous - assert_command dfx identity whoami - assert_eq 'anonymous' - assert_command dfx identity get-principal - assert_eq '2vxsx-fae' + assert_command dfx identity use anonymous + assert_command dfx identity whoami + assert_eq 'anonymous' + assert_command dfx identity get-principal + assert_eq '2vxsx-fae' } @test "identity use: is overridden by env var DFX_IDENTITY" { - assert_command dfx identity new dan - assert_command dfx identity new frank - assert_command dfx identity new alice - assert_command dfx identity use dan - assert_command dfx identity whoami - assert_eq 'dan' - DFX_IDENTITY=frank - export DFX_IDENTITY - assert_command dfx identity whoami - assert_eq 'frank' - assert_command dfx identity whoami --identity alice - assert_eq 'alice' + assert_command dfx identity new dan + assert_command dfx identity new frank + assert_command dfx identity new alice + assert_command dfx identity use dan + assert_command dfx identity whoami + assert_eq 'dan' + DFX_IDENTITY=frank + export DFX_IDENTITY + assert_command dfx identity whoami + assert_eq 'frank' + assert_command dfx identity whoami --identity alice + assert_eq 'alice' } @@ -410,165 +410,165 @@ default' ## @test "identity whoami: creates the default identity on first run" { - # Just an example. All the identity commands do this. - assert_command dfx identity whoami - assert_eq 'default' "$stdout" - assert_match 'Creating the "default" identity.' "$stderr" - assert_match 'Created the "default" identity.' "$stderr" + # Just an example. All the identity commands do this. + assert_command dfx identity whoami + assert_eq 'default' "$stdout" + assert_match 'Creating the "default" identity.' "$stderr" + assert_match 'Created the "default" identity.' "$stderr" } @test "identity whoami: shows the current identity" { - assert_command dfx identity whoami - assert_eq 'default' "$stdout" - assert_command dfx identity new charlie - assert_command dfx identity whoami - assert_eq 'default' - assert_command dfx identity use charlie - assert_command dfx identity whoami - assert_eq 'charlie' + assert_command dfx identity whoami + assert_eq 'default' "$stdout" + assert_command dfx identity new charlie + assert_command dfx identity whoami + assert_eq 'default' + assert_command dfx identity use charlie + assert_command dfx identity whoami + assert_eq 'charlie' } ## dfx (+other commands) --identity @test "dfx identity whoami --identity (name): shows the overriding identity" { - assert_command dfx identity whoami - assert_eq 'default' "$stdout" - assert_command dfx identity new charlie - assert_command dfx identity new alice - assert_command dfx identity whoami --identity charlie - assert_eq 'charlie' - assert_command dfx identity whoami --identity alice - assert_eq 'alice' + assert_command dfx identity whoami + assert_eq 'default' "$stdout" + assert_command dfx identity new charlie + assert_command dfx identity new alice + assert_command dfx identity whoami --identity charlie + assert_eq 'charlie' + assert_command dfx identity whoami --identity alice + assert_eq 'alice' } @test "dfx (command) --identity does not persistently change the selected identity" { - assert_command dfx identity whoami - assert_eq 'default' "$stdout" - assert_command dfx identity new charlie - assert_command dfx identity new alice - assert_command dfx identity use charlie - assert_command dfx identity whoami - assert_eq 'charlie' - assert_command dfx identity whoami --identity alice - assert_eq 'alice' - assert_command dfx identity whoami - assert_eq 'charlie' + assert_command dfx identity whoami + assert_eq 'default' "$stdout" + assert_command dfx identity new charlie + assert_command dfx identity new alice + assert_command dfx identity use charlie + assert_command dfx identity whoami + assert_eq 'charlie' + assert_command dfx identity whoami --identity alice + assert_eq 'alice' + assert_command dfx identity whoami + assert_eq 'charlie' } ## ## Identity key migration ## @test "identity manager copies existing key from $DFX_CONFIG_ROOT/.dfinity/identity/creds.pem" { - assert_command dfx identity whoami - assert_command mkdir -p "$DFX_CONFIG_ROOT/.dfinity/identity" - assert_command mv "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" "$DFX_CONFIG_ROOT/.dfinity/identity/creds.pem" - ORIGINAL_KEY=$(cat "$DFX_CONFIG_ROOT/.dfinity/identity/creds.pem") - assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx/identity/default" - assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx/identity" - assert_command rm "$DFX_CONFIG_ROOT/.config/dfx/identity.json" - assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx" - assert_command rmdir "$DFX_CONFIG_ROOT/.config" + assert_command dfx identity whoami + assert_command mkdir -p "$DFX_CONFIG_ROOT/.dfinity/identity" + assert_command mv "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" "$DFX_CONFIG_ROOT/.dfinity/identity/creds.pem" + ORIGINAL_KEY=$(cat "$DFX_CONFIG_ROOT/.dfinity/identity/creds.pem") + assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx/identity/default" + assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx/identity" + assert_command rm "$DFX_CONFIG_ROOT/.config/dfx/identity.json" + assert_command rmdir "$DFX_CONFIG_ROOT/.config/dfx" + assert_command rmdir "$DFX_CONFIG_ROOT/.config" - assert_command dfx identity whoami + assert_command dfx identity whoami - assert_match "migrating key from" - assert_eq "$(cat "$DFX_CONFIG_ROOT"/.config/dfx/identity/default/identity.pem)" "$ORIGINAL_KEY" + assert_match "migrating key from" + assert_eq "$(cat "$DFX_CONFIG_ROOT"/.config/dfx/identity/default/identity.pem)" "$ORIGINAL_KEY" } @test "identity: import" { - openssl ecparam -name secp256k1 -genkey -out identity.pem - assert_command dfx identity import alice identity.pem - assert_match 'Imported identity: "alice".' "$stderr" - assert_command bash -c "dfx identity export alice > alice.pem" - assert_command diff identity.pem alice.pem - assert_eq "" + openssl ecparam -name secp256k1 -genkey -out identity.pem + assert_command dfx identity import alice identity.pem + assert_match 'Imported identity: "alice".' "$stderr" + assert_command bash -c "dfx identity export alice > alice.pem" + assert_command diff identity.pem alice.pem + assert_eq "" } @test "identity: import can only overwrite identity with --force" { - openssl ecparam -name secp256k1 -genkey -out identity.pem - openssl ecparam -name secp256k1 -genkey -out identity2.pem - assert_command dfx identity import alice identity.pem - assert_match 'Imported identity: "alice".' "$stderr" - dfx identity use alice - PRINCIPAL_1="$(dfx identity get-principal)" + openssl ecparam -name secp256k1 -genkey -out identity.pem + openssl ecparam -name secp256k1 -genkey -out identity2.pem + assert_command dfx identity import alice identity.pem + assert_match 'Imported identity: "alice".' "$stderr" + dfx identity use alice + PRINCIPAL_1="$(dfx identity get-principal)" - assert_command_fail dfx identity import alice identity2.pem - assert_match "Identity already exists." - assert_command dfx identity import --force alice identity2.pem - assert_match 'Imported identity: "alice".' - PRINCIPAL_2="$(dfx identity get-principal)" + assert_command_fail dfx identity import alice identity2.pem + assert_match "Identity already exists." + assert_command dfx identity import --force alice identity2.pem + assert_match 'Imported identity: "alice".' + PRINCIPAL_2="$(dfx identity get-principal)" - assert_neq "$PRINCIPAL_1" "$PRINCIPAL_2" + assert_neq "$PRINCIPAL_1" "$PRINCIPAL_2" } @test "identity: import default" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity import bob "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_match 'Imported identity: "bob".' "$stderr" - assert_command bash -c "dfx identity export bob > bob.pem" - assert_command diff "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" "bob.pem" - assert_eq "" + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity import bob "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_match 'Imported identity: "bob".' "$stderr" + assert_command bash -c "dfx identity export bob > bob.pem" + assert_command diff "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" "bob.pem" + assert_eq "" } @test "identity: import --storage-mode plaintext" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command dfx identity import bob "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" --storage-mode plaintext - assert_match 'Imported identity: "bob".' "$stderr" - assert_command diff "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" - assert_eq "" + assert_command dfx identity new alice --storage-mode plaintext + assert_command dfx identity import bob "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" --storage-mode plaintext + assert_match 'Imported identity: "bob".' "$stderr" + assert_command diff "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" "$DFX_CONFIG_ROOT/.config/dfx/identity/bob/identity.pem" + assert_eq "" } @test "identity: cannot import invalid PEM file" { - assert_command dfx identity new alice --storage-mode plaintext - assert_command cp "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" ./alice.pem - # Following 3 lines manipulate the pem file so that it will be invalid - head -n 1 alice.pem > bob.pem - echo -n 1 >> bob.pem - tail -n 3 alice.pem > bob.pem - assert_command_fail dfx identity import bob bob.pem - assert_match 'Failed to validate PEM content' "$stderr" + assert_command dfx identity new alice --storage-mode plaintext + assert_command cp "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" ./alice.pem + # Following 3 lines manipulate the pem file so that it will be invalid + head -n 1 alice.pem > bob.pem + echo -n 1 >> bob.pem + tail -n 3 alice.pem > bob.pem + assert_command_fail dfx identity import bob bob.pem + assert_match 'Failed to validate PEM content' "$stderr" } @test "identity: can import an EC key without an EC PARAMETERS section (as quill generate makes)" { - cat >private-key-no-ec-parameters.pem <private-key-no-ec-parameters.pem <dfx.json # avoid "dfx.json not found, using default." - assert_command dfx ledger account-id --identity private-key-no-ec-parameters - assert_eq "3c00cf85d77b9dbf74a2acec1d9a9e73a3fc65f5048c64800b15f3b2c4c8eb11" + assert_command dfx identity import private-key-no-ec-parameters private-key-no-ec-parameters.pem + assert_command dfx identity get-principal --identity private-key-no-ec-parameters + assert_eq "j4p4p-o5ogq-4gzev-t3kay-hpm5o-xuwpz-yvrpp-47cc4-qyunt-k76yw-qae" + echo "{}" >dfx.json # avoid "dfx.json not found, using default." + assert_command dfx ledger account-id --identity private-key-no-ec-parameters + assert_eq "3c00cf85d77b9dbf74a2acec1d9a9e73a3fc65f5048c64800b15f3b2c4c8eb11" } @test "identity: can export and re-import an identity" { - assert_command dfx identity new alice - dfx identity export alice > export.pem - assert_file_exists export.pem - assert_command dfx identity import bob export.pem + assert_command dfx identity new alice + dfx identity export alice > export.pem + assert_file_exists export.pem + assert_command dfx identity import bob export.pem } @test "identity: can import a seed phrase" { - reg="seed phrase for identity 'alice': ([a-z ]+)" - assert_command dfx identity new alice - [[ $stderr =~ $reg ]] - echo "${BASH_REMATCH[1]}" >seed.txt - principal=$(dfx identity get-principal --identity alice) - assert_command dfx identity import alice2 --seed-file seed.txt --storage-mode plaintext - assert_command dfx identity get-principal --identity alice2 - assert_eq "$principal" - dfx identity export alice2 > export.pem - assert_command openssl asn1parse -in export.pem - assert_match ':secp256k1' + reg="seed phrase for identity 'alice': ([a-z ]+)" + assert_command dfx identity new alice + [[ $stderr =~ $reg ]] + echo "${BASH_REMATCH[1]}" >seed.txt + principal=$(dfx identity get-principal --identity alice) + assert_command dfx identity import alice2 --seed-file seed.txt --storage-mode plaintext + assert_command dfx identity get-principal --identity alice2 + assert_eq "$principal" + dfx identity export alice2 > export.pem + assert_command openssl asn1parse -in export.pem + assert_match ':secp256k1' } @test "identity: consistently imports a known seed phrase" { - echo "display dawn estate night naive stomach receive lock expose boring square boy deposit mistake volume soldier coil rocket match diamond repair opinion action paddle">seed.txt - assert_command dfx identity import alice --seed-file seed.txt --storage-mode plaintext - assert_command dfx identity get-principal --identity alice - assert_eq "qimd7-lqrvx-kdvsm-7zeqn-bgoix-ukjfi-hgmfg-ur2he-odgb2-joms4-nae" + echo "display dawn estate night naive stomach receive lock expose boring square boy deposit mistake volume soldier coil rocket match diamond repair opinion action paddle">seed.txt + assert_command dfx identity import alice --seed-file seed.txt --storage-mode plaintext + assert_command dfx identity get-principal --identity alice + assert_eq "qimd7-lqrvx-kdvsm-7zeqn-bgoix-ukjfi-hgmfg-ur2he-odgb2-joms4-nae" } diff --git a/e2e/tests-dfx/identity_encryption.bash b/e2e/tests-dfx/identity_encryption.bash index 6f545d659f..b986b9a438 100755 --- a/e2e/tests-dfx/identity_encryption.bash +++ b/e2e/tests-dfx/identity_encryption.bash @@ -3,12 +3,12 @@ load ../utils/_ setup() { - standard_setup - export DFX_CI_USE_PROXY_KEYRING="" + standard_setup + export DFX_CI_USE_PROXY_KEYRING="" } teardown() { - standard_teardown + standard_teardown } # @@ -17,18 +17,18 @@ teardown() { # @test "can create and use identity with password" { - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/create_identity_with_password.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/create_identity_with_password.exp" } @test "wrong password is rejected" { - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/wrong_password_rejected.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/init_alice_with_pw.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/wrong_password_rejected.exp" } @test "import and export identity with a password are inverse operations" { - # key generated using `openssl ecparam -genkey -name secp256k1` - cat >import.pem <import.pem <> postinstall.sh - assert_command_fail dfx canister install postinstall_script --mode upgrade - assert_match 'hello-script' + assert_command dfx canister install postinstall_script + assert_match 'hello-script' + + echo 'return 1' >> postinstall.sh + assert_command_fail dfx canister install postinstall_script --mode upgrade + assert_match 'hello-script' } @test "post-install tasks receive environment variables" { - install_asset post_install - dfx_start - echo "echo hello \$CANISTER_ID" >> postinstall.sh + install_asset post_install + dfx_start + echo "echo hello \$CANISTER_ID" >> postinstall.sh - assert_command dfx canister create --all - assert_command dfx build - id=$(dfx canister id postinstall_script) + assert_command dfx canister create --all + assert_command dfx build + id=$(dfx canister id postinstall_script) - assert_command dfx canister install --all - assert_match "hello $id" - assert_command dfx canister install postinstall_script --mode upgrade - assert_match "hello $id" + assert_command dfx canister install --all + assert_match "hello $id" + assert_command dfx canister install postinstall_script --mode upgrade + assert_match "hello $id" - assert_command dfx deploy - assert_match "hello $id" - assert_command dfx deploy postinstall_script - assert_match "hello $id" + assert_command dfx deploy + assert_match "hello $id" + assert_command dfx deploy postinstall_script + assert_match "hello $id" } @test "post-install tasks discover dependencies" { - install_asset post_install - dfx_start - echo "echo hello \$CANISTER_ID_postinstall" >> postinstall.sh + install_asset post_install + dfx_start + echo "echo hello \$CANISTER_ID_postinstall" >> postinstall.sh + + assert_command dfx canister create --all + assert_command dfx build + id=$(dfx canister id postinstall) - assert_command dfx canister create --all - assert_command dfx build - id=$(dfx canister id postinstall) - - assert_command dfx canister install postinstall_script - assert_match "hello $id" + assert_command dfx canister install postinstall_script + assert_match "hello $id" } @test "can install gzip wasm" { - jq '.canisters.e2e_project_backend.gzip=true' dfx.json | sponge dfx.json - dfx_start - dfx canister create --all - assert_command dfx build - assert_command dfx canister install --all - BUILD_HASH="0x$(sha256sum .dfx/local/canisters/e2e_project_backend/e2e_project_backend.wasm.gz | cut -d " " -f 1)" - ONCHAIN_HASH="$(dfx canister info e2e_project_backend | tail -n 1 | cut -d " " -f 3)" - assert_eq "$BUILD_HASH" "$ONCHAIN_HASH" + jq '.canisters.e2e_project_backend.gzip=true' dfx.json | sponge dfx.json + dfx_start + dfx canister create --all + assert_command dfx build + assert_command dfx canister install --all + BUILD_HASH="0x$(sha256sum .dfx/local/canisters/e2e_project_backend/e2e_project_backend.wasm.gz | cut -d " " -f 1)" + ONCHAIN_HASH="$(dfx canister info e2e_project_backend | tail -n 1 | cut -d " " -f 3)" + assert_eq "$BUILD_HASH" "$ONCHAIN_HASH" } @test "--mode=auto selects install or upgrade automatically" { - dfx_start - assert_command dfx canister create e2e_project_backend - assert_command dfx build e2e_project_backend - assert_command dfx canister install e2e_project_backend --mode auto - assert_command dfx canister call e2e_project_backend greet dfx - assert_command dfx canister install e2e_project_backend --mode auto --upgrade-unchanged - assert_command dfx canister call e2e_project_backend greet dfx + dfx_start + assert_command dfx canister create e2e_project_backend + assert_command dfx build e2e_project_backend + assert_command dfx canister install e2e_project_backend --mode auto + assert_command dfx canister call e2e_project_backend greet dfx + assert_command dfx canister install e2e_project_backend --mode auto --upgrade-unchanged + assert_command dfx canister call e2e_project_backend greet dfx } @test "-y skips compat check" { - dfx_start - assert_command dfx canister create e2e_project_backend - assert_command dfx build e2e_project_backend - assert_command dfx canister install e2e_project_backend - assert_command timeout -s9 20s dfx canister install e2e_project_backend --mode reinstall -y # if -y does not work, hangs without stdin + dfx_start + assert_command dfx canister create e2e_project_backend + assert_command dfx build e2e_project_backend + assert_command dfx canister install e2e_project_backend + assert_command timeout -s9 20s dfx canister install e2e_project_backend --mode reinstall -y # if -y does not work, hangs without stdin } @test "--no-asset-upgrade skips asset upgrade" { - dfx_start - use_asset_wasm 0.12.1 - dfx deploy - assert_command dfx canister info e2e_project_frontend - assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 - use_default_asset_wasm - assert_command dfx canister install e2e_project_frontend --mode upgrade --no-asset-upgrade - assert_command dfx canister info e2e_project_frontend - assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 + dfx_start + use_asset_wasm 0.12.1 + dfx deploy + assert_command dfx canister info e2e_project_frontend + assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 + use_default_asset_wasm + assert_command dfx canister install e2e_project_frontend --mode upgrade --no-asset-upgrade + assert_command dfx canister info e2e_project_frontend + assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0 } @test "installing multiple canisters with arguments fails" { - assert_command_fail dfx canister install --all --argument hello - assert_contains "error: the argument '--all' cannot be used with '--argument '" + assert_command_fail dfx canister install --all --argument hello + assert_contains "error: the argument '--all' cannot be used with '--argument '" } diff --git a/e2e/tests-dfx/inter.bash b/e2e/tests-dfx/inter.bash index 29934134a6..73dfcf335c 100644 --- a/e2e/tests-dfx/inter.bash +++ b/e2e/tests-dfx/inter.bash @@ -3,62 +3,60 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop - # stop_dfx_replica - # stop_dfx_bootstrap - standard_teardown + dfx_stop + standard_teardown } @test "inter-canister calls" { - dfx_new_rust inter - install_asset inter - dfx_start - dfx deploy - - # calling motoko canister from rust canister - assert_command dfx canister call inter_rs read - assert_match '(0 : nat)' - assert_command dfx canister call inter_rs inc - assert_command dfx canister call inter_rs read - assert_match '(1 : nat)' - assert_command dfx canister call inter_rs write '(5)' - assert_command dfx canister call inter_rs read - assert_match '(5 : nat)' - - # calling rust canister from motoko canister - assert_command dfx canister call inter_mo write '(0)' - assert_command dfx canister call inter_mo read - assert_match '(0 : nat)' - assert_command dfx canister call inter_mo inc - assert_command dfx canister call inter_mo read - assert_match '(1 : nat)' - assert_command dfx canister call inter_mo write '(6)' - assert_command dfx canister call inter_mo read - assert_match '(6 : nat)' - - # calling rust canister from rust canister, trough motoko canisters - assert_command dfx canister call inter2_rs write '(0)' - assert_command dfx canister call inter2_rs read - assert_match '(0 : nat)' - assert_command dfx canister call inter2_rs inc - assert_command dfx canister call inter2_rs read - assert_match '(1 : nat)' - assert_command dfx canister call inter2_rs write '(7)' - assert_command dfx canister call inter2_rs read - assert_match '(7 : nat)' - - # calling motoko canister from motoko canister, trough rust canisters - assert_command dfx canister call inter2_mo write '(0)' - assert_command dfx canister call inter2_mo read - assert_match '(0 : nat)' - assert_command dfx canister call inter2_mo inc - assert_command dfx canister call inter2_mo read - assert_match '(1 : nat)' - assert_command dfx canister call inter2_mo write '(8)' - assert_command dfx canister call inter2_mo read - assert_match '(8 : nat)' + dfx_new_rust inter + install_asset inter + dfx_start + dfx deploy + + # calling motoko canister from rust canister + assert_command dfx canister call inter_rs read + assert_match '(0 : nat)' + assert_command dfx canister call inter_rs inc + assert_command dfx canister call inter_rs read + assert_match '(1 : nat)' + assert_command dfx canister call inter_rs write '(5)' + assert_command dfx canister call inter_rs read + assert_match '(5 : nat)' + + # calling rust canister from motoko canister + assert_command dfx canister call inter_mo write '(0)' + assert_command dfx canister call inter_mo read + assert_match '(0 : nat)' + assert_command dfx canister call inter_mo inc + assert_command dfx canister call inter_mo read + assert_match '(1 : nat)' + assert_command dfx canister call inter_mo write '(6)' + assert_command dfx canister call inter_mo read + assert_match '(6 : nat)' + + # calling rust canister from rust canister, trough motoko canisters + assert_command dfx canister call inter2_rs write '(0)' + assert_command dfx canister call inter2_rs read + assert_match '(0 : nat)' + assert_command dfx canister call inter2_rs inc + assert_command dfx canister call inter2_rs read + assert_match '(1 : nat)' + assert_command dfx canister call inter2_rs write '(7)' + assert_command dfx canister call inter2_rs read + assert_match '(7 : nat)' + + # calling motoko canister from motoko canister, trough rust canisters + assert_command dfx canister call inter2_mo write '(0)' + assert_command dfx canister call inter2_mo read + assert_match '(0 : nat)' + assert_command dfx canister call inter2_mo inc + assert_command dfx canister call inter2_mo read + assert_match '(1 : nat)' + assert_command dfx canister call inter2_mo write '(8)' + assert_command dfx canister call inter2_mo read + assert_match '(8 : nat)' } diff --git a/e2e/tests-dfx/leak.bash b/e2e/tests-dfx/leak.bash index b1aa750c23..5639f8a991 100644 --- a/e2e/tests-dfx/leak.bash +++ b/e2e/tests-dfx/leak.bash @@ -2,27 +2,25 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "repeated install wasm" { - install_asset custom_canister - install_asset wasm/identity - dfx_start - dfx deploy - for _ in {1..50} - do - echo yes | dfx canister install --mode=reinstall custom - done + install_asset custom_canister + install_asset wasm/identity + dfx_start + dfx deploy + for _ in {1..50} + do + echo yes | dfx canister install --mode=reinstall custom + done } diff --git a/e2e/tests-dfx/ledger.bash b/e2e/tests-dfx/ledger.bash index 73509680a1..71f7c5c5f6 100644 --- a/e2e/tests-dfx/ledger.bash +++ b/e2e/tests-dfx/ledger.bash @@ -2,31 +2,28 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - - setup() { - standard_setup - install_asset ledger - install_shared_asset subnet_type/shared_network_settings/system + standard_setup + install_asset ledger + install_shared_asset subnet_type/shared_network_settings/system - dfx identity import --storage-mode plaintext alice alice.pem - dfx identity import --storage-mode plaintext bob bob.pem + dfx identity import --storage-mode plaintext alice alice.pem + dfx identity import --storage-mode plaintext bob bob.pem - dfx_start_for_nns_install + dfx_start_for_nns_install - dfx extension install nns || true - dfx nns install --ledger-accounts 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc + dfx extension install nns --version 0.2.1 || true + dfx nns install --ledger-accounts 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } current_time_nanoseconds() { - echo "$(date +%s)"000000000 + echo "$(date +%s)"000000000 } @test "deploy icp retry options" { @@ -60,222 +57,222 @@ current_time_nanoseconds() { } @test "ledger account-id" { - dfx identity use alice - assert_command dfx ledger account-id - assert_match 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + dfx identity use alice + assert_command dfx ledger account-id + assert_match 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - assert_command dfx ledger account-id --of-principal fg7gi-vyaaa-aaaal-qadca-cai - assert_match a014842f64a22e59887162a79c7ca7eb02553250704780ec4d954f12d0ea0b18 + assert_command dfx ledger account-id --of-principal fg7gi-vyaaa-aaaal-qadca-cai + assert_match a014842f64a22e59887162a79c7ca7eb02553250704780ec4d954f12d0ea0b18 - # --of-canister accepts both canister alias and canister principal - assert_command dfx canister create dummy_canister - assert_command dfx ledger account-id --of-canister "$(dfx canister id dummy_canister)" - assert_eq "$(dfx ledger account-id --of-canister dummy_canister)" + # --of-canister accepts both canister alias and canister principal + assert_command dfx canister create dummy_canister + assert_command dfx ledger account-id --of-canister "$(dfx canister id dummy_canister)" + assert_eq "$(dfx ledger account-id --of-canister dummy_canister)" } @test "ledger balance & transfer" { - dfx identity use alice - assert_command dfx ledger account-id - assert_match 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - - assert_command dfx ledger balance - assert_match "1000000000.00000000 ICP" - - assert_command dfx ledger transfer --amount 100 --memo 1 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 # to bob - assert_match "Transfer sent at block height" - - # The sender(alice) paid transaction fee which is 0.0001 ICP - assert_command dfx ledger balance - assert_match "999999899.99990000 ICP" - - dfx identity use bob - assert_command dfx ledger account-id - assert_match 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 - - assert_command dfx ledger balance - assert_match "1000000100.00000000 ICP" - - assert_command dfx ledger transfer --icp 100 --e8s 1 --memo 2 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 # to alice - assert_match "Transfer sent at block height" - - # The sender(bob) paid transaction fee which is 0.0001 ICP - # 10100 - 100 - 0.0001 - 0.00000001 = 9999.99989999 - assert_command dfx ledger balance - assert_match "999999999.99989999 ICP" - - # Transaction Deduplication - t=$(current_time_nanoseconds) - - assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - # shellcheck disable=SC2154 - block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - - assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time $((t+1)) 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Transfer sent at block height $block_height" "$stdout" - - assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - # shellcheck disable=SC2154 - assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - - assert_command dfx ledger transfer --icp 1 --memo 2 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Transfer sent at block height $block_height" "$stdout" + dfx identity use alice + assert_command dfx ledger account-id + assert_match 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + + assert_command dfx ledger balance + assert_match "1000000000.00000000 ICP" + + assert_command dfx ledger transfer --amount 100 --memo 1 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 # to bob + assert_match "Transfer sent at block height" + + # The sender(alice) paid transaction fee which is 0.0001 ICP + assert_command dfx ledger balance + assert_match "999999899.99990000 ICP" + + dfx identity use bob + assert_command dfx ledger account-id + assert_match 22ca7edac648b814e81d7946e8bacea99280e07c5f51a04ba7a38009d8ad8e89 + + assert_command dfx ledger balance + assert_match "1000000100.00000000 ICP" + + assert_command dfx ledger transfer --icp 100 --e8s 1 --memo 2 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 # to alice + assert_match "Transfer sent at block height" + + # The sender(bob) paid transaction fee which is 0.0001 ICP + # 10100 - 100 - 0.0001 - 0.00000001 = 9999.99989999 + assert_command dfx ledger balance + assert_match "999999999.99989999 ICP" + + # Transaction Deduplication + t=$(current_time_nanoseconds) + + assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + # shellcheck disable=SC2154 + block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + + assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time $((t+1)) 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Transfer sent at block height $block_height" "$stdout" + + assert_command dfx ledger transfer --icp 1 --memo 1 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + # shellcheck disable=SC2154 + assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + + assert_command dfx ledger transfer --icp 1 --memo 2 --created-at-time "$t" 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Transfer sent at block height $block_height" "$stdout" } @test "ledger subaccounts" { - subacct=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f - assert_command dfx ledger account-id --identity bob --subaccount "$subacct" - assert_match 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc - - dfx identity use alice - assert_command dfx ledger balance - assert_match "1000000000.00000000 ICP" - assert_command dfx ledger transfer --amount 100 --memo 1 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc # to bob+subacct - assert_match "Transfer sent at block height" - assert_command dfx ledger balance - assert_match "999999899.99990000 ICP" - - dfx identity use bob - assert_command dfx ledger balance - assert_match "1000000000.00000000 ICP" - assert_command dfx ledger balance --subaccount "$subacct" - assert_match "1000000100.00000000 ICP" - - assert_command dfx ledger transfer --amount 100 --memo 2 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 --from-subaccount "$subacct" # to alice - assert_match "Transfer sent at block height" - assert_command dfx ledger balance - assert_match "1000000000.00000000 ICP" - assert_command dfx ledger balance --subaccount "$subacct" - assert_match "999999999.99990000 ICP" - assert_command dfx ledger balance --identity alice - assert_match "999999999.99990000 ICP" + subacct=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f + assert_command dfx ledger account-id --identity bob --subaccount "$subacct" + assert_match 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc + + dfx identity use alice + assert_command dfx ledger balance + assert_match "1000000000.00000000 ICP" + assert_command dfx ledger transfer --amount 100 --memo 1 5a94fe181e9d411c58726cb87cbf2d016241b6c350bc3330e4869ca76e54ecbc # to bob+subacct + assert_match "Transfer sent at block height" + assert_command dfx ledger balance + assert_match "999999899.99990000 ICP" + + dfx identity use bob + assert_command dfx ledger balance + assert_match "1000000000.00000000 ICP" + assert_command dfx ledger balance --subaccount "$subacct" + assert_match "1000000100.00000000 ICP" + + assert_command dfx ledger transfer --amount 100 --memo 2 345f723e9e619934daac6ae0f4be13a7b0ba57d6a608e511a00fd0ded5866752 --from-subaccount "$subacct" # to alice + assert_match "Transfer sent at block height" + assert_command dfx ledger balance + assert_match "1000000000.00000000 ICP" + assert_command dfx ledger balance --subaccount "$subacct" + assert_match "999999999.99990000 ICP" + assert_command dfx ledger balance --identity alice + assert_match "999999999.99990000 ICP" } tc_to_num() { - if [[ $1 =~ T ]]; then - echo "${1%%[^0-9]*}000000000000" - else - echo "${1%%[^0-9]*}" - fi + if [[ $1 =~ T ]]; then + echo "${1%%[^0-9]*}000000000000" + else + echo "${1%%[^0-9]*}" + fi } @test "ledger top-up" { - dfx identity use alice - assert_command dfx ledger balance - assert_match "1000000000.00000000 ICP" - - wallet=$(dfx identity get-wallet) - balance=$(tc_to_num "$(dfx wallet balance)") - - assert_command dfx ledger top-up "$wallet" --icp 5 - assert_match "Canister was topped up with 617283500000000 cycles" - balance_now=$(tc_to_num "$(dfx wallet balance)") - - (( balance_now - balance > 600000000000000 )) - - # Transaction Deduplication - t=$(current_time_nanoseconds) - - assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time "$t" - - # shellcheck disable=SC2154 - block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') - - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister was topped up with" "$stdout" - - assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time $((t+1)) - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister was topped up with" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Using transfer at block height $block_height" "$stdout" - - assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time "$t" - # shellcheck disable=SC2154 - assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister was topped up with" "$stdout" + dfx identity use alice + assert_command dfx ledger balance + assert_match "1000000000.00000000 ICP" + + wallet=$(dfx identity get-wallet) + balance=$(tc_to_num "$(dfx wallet balance)") + + assert_command dfx ledger top-up "$wallet" --icp 5 + assert_match "Canister was topped up with 617283500000000 cycles" + balance_now=$(tc_to_num "$(dfx wallet balance)") + + (( balance_now - balance > 600000000000000 )) + + # Transaction Deduplication + t=$(current_time_nanoseconds) + + assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time "$t" + + # shellcheck disable=SC2154 + block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') + + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister was topped up with" "$stdout" + + assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time $((t+1)) + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister was topped up with" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Using transfer at block height $block_height" "$stdout" + + assert_command dfx ledger top-up "$wallet" --icp 5 --created-at-time "$t" + # shellcheck disable=SC2154 + assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister was topped up with" "$stdout" } @test "ledger create-canister" { - dfx identity use alice - assert_command dfx ledger create-canister --amount=100 --subnet-type "type1" "$(dfx identity get-principal)" - assert_match "Transfer sent at block height" - assert_match "Refunded at block height" - assert_match "with message: Provided subnet type type1 does not exist" - - # Transaction Deduplication - t=$(current_time_nanoseconds) - - assert_command dfx ledger create-canister --amount=100 --created-at-time "$t" "$(dfx identity get-principal)" - # shellcheck disable=SC2154 - block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') - # shellcheck disable=SC2154 - created_canister_id=$(echo "$stdout" | sed '3q;d' | sed 's/Canister created with id: //') - - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister created with id: $created_canister_id" "$stdout" - - assert_command dfx ledger create-canister --amount=100 --created-at-time $((t+1)) "$(dfx identity get-principal)" - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister created with id:" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Using transfer at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_not_match "Canister created with id: $created_canister_id" "$stdout" - - assert_command dfx ledger create-canister --amount=100 --created-at-time "$t" "$(dfx identity get-principal)" - # shellcheck disable=SC2154 - assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Transfer sent at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Using transfer at block height $block_height" "$stdout" - # shellcheck disable=SC2154 - assert_match "Canister created with id: $created_canister_id" "$stdout" - + dfx identity use alice + assert_command dfx ledger create-canister --amount=100 --subnet-type "type1" "$(dfx identity get-principal)" + assert_match "Transfer sent at block height" + assert_match "Refunded at block height" + assert_match "with message: Provided subnet type type1 does not exist" + + # Transaction Deduplication + t=$(current_time_nanoseconds) + + assert_command dfx ledger create-canister --amount=100 --created-at-time "$t" "$(dfx identity get-principal)" + # shellcheck disable=SC2154 + block_height=$(echo "$stdout" | sed '1q' | sed 's/Transfer sent at block height //') + # shellcheck disable=SC2154 + created_canister_id=$(echo "$stdout" | sed '3q;d' | sed 's/Canister created with id: //') + + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister created with id: $created_canister_id" "$stdout" + + assert_command dfx ledger create-canister --amount=100 --created-at-time $((t+1)) "$(dfx identity get-principal)" + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister created with id:" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Using transfer at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_not_match "Canister created with id: $created_canister_id" "$stdout" + + assert_command dfx ledger create-canister --amount=100 --created-at-time "$t" "$(dfx identity get-principal)" + # shellcheck disable=SC2154 + assert_match "transaction is a duplicate of another transaction in block $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Transfer sent at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Using transfer at block height $block_height" "$stdout" + # shellcheck disable=SC2154 + assert_match "Canister created with id: $created_canister_id" "$stdout" + } @test "ledger show-subnet-types" { - install_asset cmc + install_asset cmc - dfx deploy cmc + dfx deploy cmc - CANISTER_ID=$(dfx canister id cmc) + CANISTER_ID=$(dfx canister id cmc) - assert_command dfx ledger show-subnet-types --cycles-minting-canister-id "$CANISTER_ID" - assert_eq '["type1", "type2"]' + assert_command dfx ledger show-subnet-types --cycles-minting-canister-id "$CANISTER_ID" + assert_eq '["type1", "type2"]' } diff --git a/e2e/tests-dfx/metadata.bash b/e2e/tests-dfx/metadata.bash index ddd34841c7..9790de9178 100644 --- a/e2e/tests-dfx/metadata.bash +++ b/e2e/tests-dfx/metadata.bash @@ -3,148 +3,148 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "custom canister metadata rules" { - install_asset metadata/custom - install_asset wasm/identity + install_asset metadata/custom + install_asset wasm/identity - dfx_start - dfx deploy + dfx_start + dfx deploy - echo "leaves existing metadata in a custom canister with no metadata settings" - dfx canister metadata --identity anonymous custom_with_default_metadata candid:service >metadata.txt - diff main.did metadata.txt + echo "leaves existing metadata in a custom canister with no metadata settings" + dfx canister metadata --identity anonymous custom_with_default_metadata candid:service >metadata.txt + diff main.did metadata.txt - echo "adds candid:service public metadata from candid field if a metadata entry exists" - dfx canister metadata --identity anonymous custom_with_standard_candid_service_metadata candid:service >metadata.txt - diff custom_with_standard_candid_service_metadata.did metadata.txt + echo "adds candid:service public metadata from candid field if a metadata entry exists" + dfx canister metadata --identity anonymous custom_with_standard_candid_service_metadata candid:service >metadata.txt + diff custom_with_standard_candid_service_metadata.did metadata.txt - echo "adds candid:service metadata from candid field with private visibility per metadata entry" - assert_command_fail dfx canister metadata --identity anonymous custom_with_private_candid_service_metadata candid:service >metadata.txt - dfx canister metadata custom_with_private_candid_service_metadata candid:service >metadata.txt - diff custom_with_private_candid_service_metadata.did metadata.txt + echo "adds candid:service metadata from candid field with private visibility per metadata entry" + assert_command_fail dfx canister metadata --identity anonymous custom_with_private_candid_service_metadata candid:service >metadata.txt + dfx canister metadata custom_with_private_candid_service_metadata candid:service >metadata.txt + diff custom_with_private_candid_service_metadata.did metadata.txt } @test "rust canister metadata rules" { - rustup default stable - rustup target add wasm32-unknown-unknown + rustup default stable + rustup target add wasm32-unknown-unknown - dfx_new_rust + dfx_new_rust - dfx_start - dfx deploy + dfx_start + dfx deploy - echo "adds public candid:service metadata to a default rust canister" - dfx canister metadata --identity anonymous e2e_project_backend candid:service >metadata.txt - diff src/e2e_project_backend/e2e_project_backend.did metadata.txt + echo "adds public candid:service metadata to a default rust canister" + dfx canister metadata --identity anonymous e2e_project_backend candid:service >metadata.txt + diff src/e2e_project_backend/e2e_project_backend.did metadata.txt - echo "adds private candid:service metadata if so configured" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json - dfx deploy - assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend candid:service - dfx canister metadata e2e_project_backend candid:service >metadata.txt - diff src/e2e_project_backend/e2e_project_backend.did metadata.txt + echo "adds private candid:service metadata if so configured" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json + dfx deploy + assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend candid:service + dfx canister metadata e2e_project_backend candid:service >metadata.txt + diff src/e2e_project_backend/e2e_project_backend.did metadata.txt } @test "motoko canister metadata rules" { - dfx_new - dfx_start - install_asset metadata/motoko - dfx canister create --all - - echo "permits specification of a replacement candid definition, if it is a valid subtype" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - assert_command dfx build - find . -name '*.did' - jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="valid_subtype.did"' dfx.json | sponge dfx.json - dfx build - - echo "reports an error if a specified candid:service metadata is not a valid subtype for the canister" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="not_subtype_rename.did"' dfx.json | sponge dfx.json - assert_command_fail dfx build - assert_match "Method new_method is only in the expected type" - - echo "reports an error if a specified candid:service metadata is not a valid subtype for the canister" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="not_subtype_numbertype.did"' dfx.json | sponge dfx.json - assert_command_fail dfx build - assert_match "int is not a subtype of nat" - - - echo "adds private candid:service metadata if so configured" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json - dfx deploy - assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend candid:service - dfx canister metadata e2e_project_backend candid:service >metadata.txt - diff .dfx/local/canisters/e2e_project_backend/e2e_project_backend.did metadata.txt - - - echo "adds public candid:service metadata to a default motoko canister" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - dfx deploy - dfx canister metadata --identity anonymous e2e_project_backend candid:service >metadata.txt - diff .dfx/local/canisters/e2e_project_backend/e2e_project_backend.did metadata.txt + dfx_new + dfx_start + install_asset metadata/motoko + dfx canister create --all + + echo "permits specification of a replacement candid definition, if it is a valid subtype" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + assert_command dfx build + find . -name '*.did' + jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="valid_subtype.did"' dfx.json | sponge dfx.json + dfx build + + echo "reports an error if a specified candid:service metadata is not a valid subtype for the canister" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="not_subtype_rename.did"' dfx.json | sponge dfx.json + assert_command_fail dfx build + assert_match "Method new_method is only in the expected type" + + echo "reports an error if a specified candid:service metadata is not a valid subtype for the canister" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].path="not_subtype_numbertype.did"' dfx.json | sponge dfx.json + assert_command_fail dfx build + assert_match "int is not a subtype of nat" + + + echo "adds private candid:service metadata if so configured" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="candid:service"|.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json + dfx deploy + assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend candid:service + dfx canister metadata e2e_project_backend candid:service >metadata.txt + diff .dfx/local/canisters/e2e_project_backend/e2e_project_backend.did metadata.txt + + + echo "adds public candid:service metadata to a default motoko canister" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + dfx deploy + dfx canister metadata --identity anonymous e2e_project_backend candid:service >metadata.txt + diff .dfx/local/canisters/e2e_project_backend/e2e_project_backend.did metadata.txt } @test "adds arbitrary metadata to a motoko canister" { - dfx_new - dfx_start - install_asset metadata/motoko - dfx canister create --all - - echo "adds public arbitrary metadata to a default motoko canister" - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="arbitrary"|.canisters.e2e_project_backend.metadata[0].path="arbitrary-metadata.txt"' dfx.json | sponge dfx.json - echo "can be anything" >arbitrary-metadata.txt - dfx deploy - dfx canister metadata --identity anonymous e2e_project_backend arbitrary >from-canister.txt - diff arbitrary-metadata.txt from-canister.txt - - # with private visibility - jq '.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json - dfx deploy - assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend arbitrary - dfx canister metadata e2e_project_backend arbitrary >from-canister.txt - diff arbitrary-metadata.txt from-canister.txt + dfx_new + dfx_start + install_asset metadata/motoko + dfx canister create --all + + echo "adds public arbitrary metadata to a default motoko canister" + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="arbitrary"|.canisters.e2e_project_backend.metadata[0].path="arbitrary-metadata.txt"' dfx.json | sponge dfx.json + echo "can be anything" >arbitrary-metadata.txt + dfx deploy + dfx canister metadata --identity anonymous e2e_project_backend arbitrary >from-canister.txt + diff arbitrary-metadata.txt from-canister.txt + + # with private visibility + jq '.canisters.e2e_project_backend.metadata[0].visibility="private"' dfx.json | sponge dfx.json + dfx deploy + assert_command_fail dfx canister metadata --identity anonymous e2e_project_backend arbitrary + dfx canister metadata e2e_project_backend arbitrary >from-canister.txt + diff arbitrary-metadata.txt from-canister.txt } @test "uses the first metadata definition for name and network" { - dfx_new - dfx_start - install_asset metadata/motoko - dfx canister create --all - - jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[0].name="multiple"|.canisters.e2e_project_backend.metadata[0].path="empty-networks-matches-nothing.txt"|.canisters.e2e_project_backend.metadata[0].networks=[]' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[1].name="multiple"|.canisters.e2e_project_backend.metadata[1].path="different-network-no-match.txt"|.canisters.e2e_project_backend.metadata[1].networks=["ic"]' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[2].name="multiple"|.canisters.e2e_project_backend.metadata[2].path="first-match-chosen.txt"' dfx.json | sponge dfx.json - jq '.canisters.e2e_project_backend.metadata[3].name="multiple"|.canisters.e2e_project_backend.metadata[3].path="earlier-match-ignored.txt"' dfx.json | sponge dfx.json - echo "dfx will install this file" >first-match-chosen.txt - dfx deploy - dfx canister metadata --identity anonymous e2e_project_backend multiple >from-canister.txt - diff first-match-chosen.txt from-canister.txt + dfx_new + dfx_start + install_asset metadata/motoko + dfx canister create --all + + jq 'del(.canisters.e2e_project_backend.metadata)' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[0].name="multiple"|.canisters.e2e_project_backend.metadata[0].path="empty-networks-matches-nothing.txt"|.canisters.e2e_project_backend.metadata[0].networks=[]' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[1].name="multiple"|.canisters.e2e_project_backend.metadata[1].path="different-network-no-match.txt"|.canisters.e2e_project_backend.metadata[1].networks=["ic"]' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[2].name="multiple"|.canisters.e2e_project_backend.metadata[2].path="first-match-chosen.txt"' dfx.json | sponge dfx.json + jq '.canisters.e2e_project_backend.metadata[3].name="multiple"|.canisters.e2e_project_backend.metadata[3].path="earlier-match-ignored.txt"' dfx.json | sponge dfx.json + echo "dfx will install this file" >first-match-chosen.txt + dfx deploy + dfx canister metadata --identity anonymous e2e_project_backend multiple >from-canister.txt + diff first-match-chosen.txt from-canister.txt } @test "can add metadata to a compressed canister" { - dfx_start - install_asset gzip - install_asset wasm/identity - jq '.canisters.gzipped.metadata[0].name="arbitrary"|.canisters.gzipped.metadata[0].content="arbitrary content"' dfx.json | sponge dfx.json - - assert_command dfx deploy - assert_command dfx canister metadata gzipped arbitrary - assert_eq "$output" "arbitrary content" + dfx_start + install_asset gzip + install_asset wasm/identity + jq '.canisters.gzipped.metadata[0].name="arbitrary"|.canisters.gzipped.metadata[0].content="arbitrary content"' dfx.json | sponge dfx.json + + assert_command dfx deploy + assert_command dfx canister metadata gzipped arbitrary + assert_eq "$output" "arbitrary content" } @test "existence of build steps do not control custom canister metadata" { @@ -172,22 +172,22 @@ teardown() { } @test "can read canister metadata from replica" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - assert_command dfx deploy + assert_command dfx deploy - dfx canister metadata hello_backend candid:service >metadata.txt - assert_command diff .dfx/local/canisters/hello_backend/hello_backend.did ./metadata.txt + dfx canister metadata hello_backend candid:service >metadata.txt + assert_command diff .dfx/local/canisters/hello_backend/hello_backend.did ./metadata.txt } @test "asset canister provides candid:service metadata" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - assert_command dfx deploy - REPO_ROOT=${BATS_TEST_DIRNAME}/../../ + assert_command dfx deploy + REPO_ROOT=${BATS_TEST_DIRNAME}/../../ - dfx canister metadata hello_frontend candid:service >candid_service_metadata.txt - assert_command diff "$REPO_ROOT/src/distributed/assetstorage.did" ./candid_service_metadata.txt + dfx canister metadata hello_frontend candid:service >candid_service_metadata.txt + assert_command diff "$REPO_ROOT/src/distributed/assetstorage.did" ./candid_service_metadata.txt } diff --git a/e2e/tests-dfx/migrate.bash b/e2e/tests-dfx/migrate.bash index 9ae48eed8a..b2e330d61a 100644 --- a/e2e/tests-dfx/migrate.bash +++ b/e2e/tests-dfx/migrate.bash @@ -3,36 +3,36 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "detects the wallet being outdated" { - use_wallet_wasm 0.7.2 - dfx_start - WALLET=$(dfx identity get-wallet) - use_wallet_wasm 0.10.0 - assert_command dfx diagnose - assert_match "dfx wallet upgrade" - assert_command_fail dfx canister call "$WALLET" wallet_balance128 - assert_command dfx fix - assert_command dfx canister call "$WALLET" wallet_balance128 + use_wallet_wasm 0.7.2 + dfx_start + WALLET=$(dfx identity get-wallet) + use_wallet_wasm 0.10.0 + assert_command dfx diagnose + assert_match "dfx wallet upgrade" + assert_command_fail dfx canister call "$WALLET" wallet_balance128 + assert_command dfx fix + assert_command dfx canister call "$WALLET" wallet_balance128 } @test "detects the wallet being the sole controller" { - dfx_start - dfx canister create e2e_project_backend --controller "$(dfx identity get-wallet)" --no-wallet - dfx build e2e_project_backend - assert_command dfx diagnose - assert_match "dfx canister update-settings" - assert_command_fail dfx canister install e2e_project_backend - assert_command dfx fix - assert_command dfx canister install e2e_project_backend + dfx_start + dfx canister create e2e_project_backend --controller "$(dfx identity get-wallet)" --no-wallet + dfx build e2e_project_backend + assert_command dfx diagnose + assert_match "dfx canister update-settings" + assert_command_fail dfx canister install e2e_project_backend + assert_command dfx fix + assert_command dfx canister install e2e_project_backend } diff --git a/e2e/tests-dfx/mode_reinstall.bash b/e2e/tests-dfx/mode_reinstall.bash index 78a68720b9..a21e4bdd5d 100644 --- a/e2e/tests-dfx/mode_reinstall.bash +++ b/e2e/tests-dfx/mode_reinstall.bash @@ -3,160 +3,160 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "install --mode=reinstall --all fails" { - dfx_start - assert_command_fail dfx canister install --mode=reinstall --all + dfx_start + assert_command_fail dfx canister install --mode=reinstall --all - assert_match "The --mode=reinstall is only valid when specifying a single canister, because reinstallation destroys all data in the canister." + assert_match "The --mode=reinstall is only valid when specifying a single canister, because reinstallation destroys all data in the canister." } @test "install --mode=reinstall fails if no canister is provided" { - # This fails because clap protects against it. + # This fails because clap protects against it. - dfx_start - assert_command_fail dfx canister install --mode=reinstall - assert_match \ + dfx_start + assert_command_fail dfx canister install --mode=reinstall + assert_match \ "error: the following required arguments were not provided: --all" } @test "reinstall succeeds when a canister name is provided" { - dfx_start - dfx deploy + dfx_start + dfx deploy - # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, - # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 - echo yes | ( - assert_command dfx canister install --mode=reinstall hello_backend + # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, + # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 + echo yes | ( + assert_command dfx canister install --mode=reinstall hello_backend - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) } @test "install --mode=reinstall refused if not approved" { - dfx_start - dfx deploy + dfx_start + dfx deploy - echo no | ( - assert_command_fail dfx canister install --mode=reinstall hello_backend + echo no | ( + assert_command_fail dfx canister install --mode=reinstall hello_backend - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_not_match "Installing code for canister" - assert_match "Refusing to install canister without approval: User declined consent." - ) + assert_not_match "Installing code for canister" + assert_match "Refusing to install canister without approval: User declined consent." + ) } @test "deploy --mode=reinstall fails if no canister name specified" { - dfx_start - assert_command_fail dfx deploy --mode=reinstall + dfx_start + assert_command_fail dfx deploy --mode=reinstall - assert_match "The --mode=reinstall is only valid when deploying a single canister, because reinstallation destroys all data in the canister." + assert_match "The --mode=reinstall is only valid when deploying a single canister, because reinstallation destroys all data in the canister." } @test "deploy --mode=reinstall succeeds when a canister name is provided" { - dfx_start - dfx deploy + dfx_start + dfx deploy - # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, - # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 - echo yes | ( - assert_command dfx deploy --mode=reinstall hello_backend + # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, + # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 + echo yes | ( + assert_command dfx deploy --mode=reinstall hello_backend - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) } @test "deploy --mode=reinstall refused if not approved" { - dfx_start - dfx deploy + dfx_start + dfx deploy - echo no | ( - assert_command_fail dfx deploy --mode=reinstall hello_backend + echo no | ( + assert_command_fail dfx deploy --mode=reinstall hello_backend - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_not_match "Installing code for canister" - assert_match "Refusing to install canister without approval: User declined consent." - ) + assert_not_match "Installing code for canister" + assert_match "Refusing to install canister without approval: User declined consent." + ) } @test "deploy --mode=reinstall does not reinstall dependencies" { - dfx_start - install_asset counter - dfx deploy + dfx_start + install_asset counter + dfx deploy - assert_command dfx canister call hello_backend read - assert_eq "(0 : nat)" + assert_command dfx canister call hello_backend read + assert_eq "(0 : nat)" - assert_command dfx canister call hello_backend inc - assert_eq "()" + assert_command dfx canister call hello_backend inc + assert_eq "()" - assert_command dfx canister call hello_backend read - assert_eq "(1 : nat)" + assert_command dfx canister call hello_backend read + assert_eq "(1 : nat)" - dfx canister call hello_backend inc - assert_command dfx canister call hello_backend read - assert_eq "(2 : nat)" + dfx canister call hello_backend inc + assert_command dfx canister call hello_backend read + assert_eq "(2 : nat)" - # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, - # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 - echo "yes" | ( - assert_command dfx deploy --mode=reinstall hello_frontend + # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, + # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 + echo "yes" | ( + assert_command dfx deploy --mode=reinstall hello_frontend - assert_match "You are about to reinstall the hello_frontend canister." - assert_not_match "You are about to reinstall the hello_backend canister." - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_frontend," - ) + assert_match "You are about to reinstall the hello_frontend canister." + assert_not_match "You are about to reinstall the hello_backend canister." + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_frontend," + ) - # the hello_backend canister should not have been upgraded (which would reset the non-stable var) - assert_command dfx canister call hello_backend read - assert_eq "(2 : nat)" + # the hello_backend canister should not have been upgraded (which would reset the non-stable var) + assert_command dfx canister call hello_backend read + assert_eq "(2 : nat)" } @test "confirmation dialogue accepts multiple forms of 'yes'" { - dfx_start - dfx deploy - - # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, - # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 - echo yes | ( - assert_command dfx deploy --mode=reinstall hello_backend - - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) - echo y | ( - assert_command dfx deploy --mode=reinstall hello_backend - - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) - echo YES | ( - assert_command dfx deploy --mode=reinstall hello_backend - - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) - echo YeS | ( - assert_command dfx deploy --mode=reinstall hello_backend - - assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" - assert_match "Reinstalling code for canister hello_backend" - ) + dfx_start + dfx deploy + + # if the pipe is alone with assert_command, $stdout, $stderr etc will not be available, + # so all the assert_match calls will fail. http://mywiki.wooledge.org/BashFAQ/024 + echo yes | ( + assert_command dfx deploy --mode=reinstall hello_backend + + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) + echo y | ( + assert_command dfx deploy --mode=reinstall hello_backend + + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) + echo YES | ( + assert_command dfx deploy --mode=reinstall hello_backend + + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) + echo YeS | ( + assert_command dfx deploy --mode=reinstall hello_backend + + assert_match "YOU WILL LOSE ALL DATA IN THE CANISTER" + assert_match "Reinstalling code for canister hello_backend" + ) } diff --git a/e2e/tests-dfx/network.bash b/e2e/tests-dfx/network.bash index b2a39982db..dcb12ac8da 100644 --- a/e2e/tests-dfx/network.bash +++ b/e2e/tests-dfx/network.bash @@ -3,111 +3,111 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx identity new --storage-mode plaintext test_id - dfx identity use test_id - dfx_new + dfx identity new --storage-mode plaintext test_id + dfx identity use test_id + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "create with wallet stores canister ids for default-persistent networks in canister_ids.json" { - dfx_start - setup_actuallylocal_shared_network - dfx_set_wallet - dfx_set_wallet + dfx_start + setup_actuallylocal_shared_network + dfx_set_wallet + dfx_set_wallet - dfx canister create --all --network actuallylocal + dfx canister create --all --network actuallylocal - # canister creates writes to a spinner (stderr), not stdout - assert_command dfx canister id e2e_project_backend --network actuallylocal - assert_match "$(jq -r .e2e_project_backend.actuallylocal ` - assert_command_fail dfx canister id hello_backend --network playground - assert_contains "Cannot find canister id" + # if network is unknown dfx fails with `Network not found: ` + assert_command_fail dfx canister id hello_backend --network playground + assert_contains "Cannot find canister id" } @test "equivalent: --network ic and --ic" { - dfx_start - dfx identity get-wallet - - assert_command_fail dfx diagnose --network ic - assert_contains "The test_id identity is not stored securely." - assert_contains "use it in mainnet-facing commands" - assert_contains "No wallet found; nothing to do" - - assert_command_fail dfx diagnose --ic - assert_contains "The test_id identity is not stored securely." - assert_contains "use it in mainnet-facing commands" - assert_contains "No wallet found; nothing to do" - - assert_command dfx diagnose - assert_not_contains "identity is not stored securely" - assert_eq "No problems found" + dfx_start + dfx identity get-wallet + + assert_command_fail dfx diagnose --network ic + assert_contains "The test_id identity is not stored securely." + assert_contains "use it in mainnet-facing commands" + assert_contains "No wallet found; nothing to do" + + assert_command_fail dfx diagnose --ic + assert_contains "The test_id identity is not stored securely." + assert_contains "use it in mainnet-facing commands" + assert_contains "No wallet found; nothing to do" + + assert_command dfx diagnose + assert_not_contains "identity is not stored securely" + assert_eq "No problems found" } diff --git a/e2e/tests-dfx/new.bash b/e2e/tests-dfx/new.bash index cba6a2e066..6559b2d5b6 100644 --- a/e2e/tests-dfx/new.bash +++ b/e2e/tests-dfx/new.bash @@ -2,60 +2,58 @@ load ../utils/_ -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - setup() { - standard_setup + standard_setup } teardown() { - standard_teardown + standard_teardown } @test "dfx new - good names" { - dfx new --no-frontend a_good_name_ - dfx new --no-frontend A - dfx new --no-frontend b - dfx new --no-frontend a_ - dfx new --no-frontend a_1 - dfx new --no-frontend a1 - dfx new --no-frontend a1a + dfx new --no-frontend a_good_name_ + dfx new --no-frontend A + dfx new --no-frontend b + dfx new --no-frontend a_ + dfx new --no-frontend a_1 + dfx new --no-frontend a1 + dfx new --no-frontend a1a } @test "dfx new - bad names" { - assert_command_fail dfx new _a_good_name_ - assert_command_fail dfx new __also_good - assert_command_fail dfx new _1 - assert_command_fail dfx new _a - assert_command_fail dfx new 1 - assert_command_fail dfx new 1_ - assert_command_fail dfx new - - assert_command_fail dfx new _ - assert_command_fail dfx new a-b-c - assert_command_fail dfx new '🕹' - assert_command_fail dfx new '不好' - assert_command_fail dfx new 'a:b' + assert_command_fail dfx new _a_good_name_ + assert_command_fail dfx new __also_good + assert_command_fail dfx new _1 + assert_command_fail dfx new _a + assert_command_fail dfx new 1 + assert_command_fail dfx new 1_ + assert_command_fail dfx new - + assert_command_fail dfx new _ + assert_command_fail dfx new a-b-c + assert_command_fail dfx new '🕹' + assert_command_fail dfx new '不好' + assert_command_fail dfx new 'a:b' } @test "dfx new readmes contain appropriate links" { - assert_command dfx new --type rust e2e_rust --no-frontend - assert_command grep "https://docs.rs/ic-cdk" e2e_rust/README.md - assert_command dfx new --type motoko e2e_motoko --no-frontend - assert_command grep "https://internetcomputer.org/docs/current/motoko/main/language-manual" e2e_motoko/README.md + assert_command dfx new --type rust e2e_rust --no-frontend + assert_command grep "https://docs.rs/ic-cdk" e2e_rust/README.md + assert_command dfx new --type motoko e2e_motoko --no-frontend + assert_command grep "https://internetcomputer.org/docs/current/motoko/main/language-manual" e2e_motoko/README.md } @test "dfx new emits projects of the correct type" { - assert_command dfx new --type rust e2e_rust --no-frontend - assert_command jq -r '.canisters.e2e_rust_backend.type' e2e_rust/dfx.json - assert_eq "rust" - assert_command dfx new --type motoko e2e_motoko --no-frontend - assert_command jq -r '.canisters.e2e_motoko_backend.type' e2e_motoko/dfx.json - assert_eq "motoko" + assert_command dfx new --type rust e2e_rust --no-frontend + assert_command jq -r '.canisters.e2e_rust_backend.type' e2e_rust/dfx.json + assert_eq "rust" + assert_command dfx new --type motoko e2e_motoko --no-frontend + assert_command jq -r '.canisters.e2e_motoko_backend.type' e2e_motoko/dfx.json + assert_eq "motoko" } @test "dfx new always emits sample-asset.txt" { - assert_command dfx new e2e_frontend --frontend - assert_file_exists e2e_frontend/src/e2e_frontend_frontend/assets/sample-asset.txt - assert_command dfx new e2e_no_frontend --no-frontend - assert_file_exists e2e_no_frontend/src/e2e_no_frontend_frontend/assets/sample-asset.txt + assert_command dfx new e2e_frontend --frontend + assert_file_exists e2e_frontend/src/e2e_frontend_frontend/assets/sample-asset.txt + assert_command dfx new e2e_no_frontend --no-frontend + assert_file_exists e2e_no_frontend/src/e2e_no_frontend_frontend/assets/sample-asset.txt } diff --git a/e2e/tests-dfx/packtool.bash b/e2e/tests-dfx/packtool.bash index a3109b50a9..8f28b789f7 100644 --- a/e2e/tests-dfx/packtool.bash +++ b/e2e/tests-dfx/packtool.bash @@ -3,73 +3,73 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "build fails if packtool is not configured" { - install_asset packtool + install_asset packtool - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match 'import error \[M0010\], package "(rate|describe)" not defined' + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match 'import error \[M0010\], package "(rate|describe)" not defined' } @test "build succeeds if packtool is configured" { - install_asset packtool - # shellcheck disable=SC1091 - source configure_packtool.bash + install_asset packtool + # shellcheck disable=SC1091 + source configure_packtool.bash - dfx_start - dfx canister create --all - dfx build + dfx_start + dfx canister create --all + dfx build } @test "project calls dependencies made available by packtool" { - install_asset packtool - # shellcheck disable=SC1091 - source configure_packtool.bash + install_asset packtool + # shellcheck disable=SC1091 + source configure_packtool.bash - dfx_start - dfx canister create --all - dfx build - dfx canister install e2e_project_backend + dfx_start + dfx canister create --all + dfx build + dfx canister install e2e_project_backend - assert_command dfx canister call e2e_project_backend rate '("rust")' - assert_eq '("rust: So hot right now.")' + assert_command dfx canister call e2e_project_backend rate '("rust")' + assert_eq '("rust: So hot right now.")' - assert_command dfx canister call e2e_project_backend rate '("php")' - assert_eq '("php: No comment.")' + assert_command dfx canister call e2e_project_backend rate '("php")' + assert_eq '("php: No comment.")' } @test "failure to invoke the package tool reports the command line and reason" { - install_asset packtool - jq '.defaults.build.packtool="./no-such-command that command cannot be invoked"' dfx.json | sponge dfx.json - - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match 'Failed to invoke the package tool' - assert_match 'no-such-command.*that.*command.*cannot.*be.*invoked' - assert_match 'No such file or directory \(os error 2\)' + install_asset packtool + jq '.defaults.build.packtool="./no-such-command that command cannot be invoked"' dfx.json | sponge dfx.json + + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match 'Failed to invoke the package tool' + assert_match 'no-such-command.*that.*command.*cannot.*be.*invoked' + assert_match 'No such file or directory \(os error 2\)' } @test "failure in execution reports the command line and exit code" { - install_asset packtool - jq '.defaults.build.packtool="sh ./command-that-fails.bash"' dfx.json | sponge dfx.json - - dfx_start - dfx canister create --all - assert_command_fail dfx build - assert_match 'The command.*failed' - assert_match 'sh.*command-that-fails.bash' - assert_match 'exit (code|status): 3' + install_asset packtool + jq '.defaults.build.packtool="sh ./command-that-fails.bash"' dfx.json | sponge dfx.json + + dfx_start + dfx canister create --all + assert_command_fail dfx build + assert_match 'The command.*failed' + assert_match 'sh.*command-that-fails.bash' + assert_match 'exit (code|status): 3' } diff --git a/e2e/tests-dfx/ping.bash b/e2e/tests-dfx/ping.bash index 1e18a36914..8b7ecfb1b6 100644 --- a/e2e/tests-dfx/ping.bash +++ b/e2e/tests-dfx/ping.bash @@ -3,86 +3,86 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx ping fails if replica not running" { - assert_command_fail dfx ping + assert_command_fail dfx ping } @test "dfx ping succeeds if replica is running" { - dfx_start - assert_command dfx ping + dfx_start + assert_command dfx ping - assert_match "\"ic_api_version\"" + assert_match "\"ic_api_version\"" } @test "dfx ping succeeds by specific host:post" { - dfx_start - webserver_port=$(get_webserver_port) - assert_command dfx ping http://127.0.0.1:"$webserver_port" + dfx_start + webserver_port=$(get_webserver_port) + assert_command dfx ping http://127.0.0.1:"$webserver_port" - assert_match "\"ic_api_version\"" + assert_match "\"ic_api_version\"" } @test "dfx ping does not require dfx.json" { - dfx_start - webserver_port=$(get_webserver_port) + dfx_start + webserver_port=$(get_webserver_port) - mkdir "$E2E_TEMP_DIR/not-a-project" - ( - cd "$E2E_TEMP_DIR/not-a-project" + mkdir "$E2E_TEMP_DIR/not-a-project" + ( + cd "$E2E_TEMP_DIR/not-a-project" - assert_command dfx ping http://127.0.0.1:"$webserver_port" - assert_match "\"ic_api_version\"" - ) + assert_command dfx ping http://127.0.0.1:"$webserver_port" + assert_match "\"ic_api_version\"" + ) } @test "dfx ping succeeds by network name" { - dfx_start - assert_command dfx ping local + dfx_start + assert_command dfx ping local - assert_match "\"ic_api_version\"" + assert_match "\"ic_api_version\"" } @test "dfx ping succeeds by network name if network bind address is host:port format" { - dfx_start - webserver_port=$(get_webserver_port) - jq '.networks.nnn.bind="127.0.0.1:'"$webserver_port"'"' dfx.json | sponge dfx.json - assert_command dfx ping nnn + dfx_start + webserver_port=$(get_webserver_port) + jq '.networks.nnn.bind="127.0.0.1:'"$webserver_port"'"' dfx.json | sponge dfx.json + assert_command dfx ping nnn - assert_match "\"ic_api_version\"" + assert_match "\"ic_api_version\"" } @test "dfx ping succeeds by arbitrary network name to a nonstandard port" { - dfx_start --host 127.0.0.1:12345 + dfx_start --host 127.0.0.1:12345 - # Make dfx use the port from configuration: - rm "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port" + # Make dfx use the port from configuration: + rm "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port" - jq '.networks.arbitrary.providers=["http://127.0.0.1:12345"]' dfx.json | sponge dfx.json + jq '.networks.arbitrary.providers=["http://127.0.0.1:12345"]' dfx.json | sponge dfx.json - assert_command dfx ping arbitrary - assert_match "\"ic_api_version\"" + assert_command dfx ping arbitrary + assert_match "\"ic_api_version\"" - assert_command_fail dfx ping - # this port won't match the ephemeral port that the replica picked - jq '.networks.arbitrary.providers=["127.0.0.1:22113"]' dfx.json | sponge dfx.json - assert_command_fail dfx ping arbitrary + assert_command_fail dfx ping + # this port won't match the ephemeral port that the replica picked + jq '.networks.arbitrary.providers=["127.0.0.1:22113"]' dfx.json | sponge dfx.json + assert_command_fail dfx ping arbitrary } @test "dfx ping can have a URL for network to ping" { - dfx_start - webserver_port=$(get_webserver_port) - assert_command dfx ping "http://127.0.0.1:$webserver_port" - assert_match "\"ic_api_version\"" + dfx_start + webserver_port=$(get_webserver_port) + assert_command dfx ping "http://127.0.0.1:$webserver_port" + assert_match "\"ic_api_version\"" } diff --git a/e2e/tests-dfx/playground.bash b/e2e/tests-dfx/playground.bash index a2732a4f0c..c90316adb9 100644 --- a/e2e/tests-dfx/playground.bash +++ b/e2e/tests-dfx/playground.bash @@ -3,14 +3,14 @@ load ../utils/_ setup() { - standard_setup - setup_playground - dfx_new hello + standard_setup + setup_playground + dfx_new hello } teardown() { - dfx_stop - standard_teardown + dfx_stop + standard_teardown } setup_playground() { @@ -53,10 +53,10 @@ setup_playground() { assert_match "The principal you are using to call a management function is not part of the controllers." if [ "$(uname)" == "Darwin" ]; then - sed -i '' 's/Hello/Goodbye/g' src/hello_backend/main.mo - elif [ "$(uname)" == "Linux" ]; then - sed -i 's/Hello/Goodbye/g' src/hello_backend/main.mo - fi + sed -i '' 's/Hello/Goodbye/g' src/hello_backend/main.mo + elif [ "$(uname)" == "Linux" ]; then + sed -i 's/Hello/Goodbye/g' src/hello_backend/main.mo + fi assert_command dfx deploy --playground assert_command dfx canister --playground call hello_backend greet '("player")' diff --git a/e2e/tests-dfx/print.bash b/e2e/tests-dfx/print.bash index cef5232c7c..7a1349921a 100644 --- a/e2e/tests-dfx/print.bash +++ b/e2e/tests-dfx/print.bash @@ -3,25 +3,25 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "print_mo" { - install_asset print - dfx_start 2>stderr.txt - dfx canister create --all - dfx build - dfx canister install e2e_project - dfx canister call e2e_project hello - sleep 2 - run tail -2 stderr.txt - assert_match "Hello, World! from DFINITY" + install_asset print + dfx_start 2>stderr.txt + dfx canister create --all + dfx build + dfx canister install e2e_project + dfx canister call e2e_project hello + sleep 2 + run tail -2 stderr.txt + assert_match "Hello, World! from DFINITY" } diff --git a/e2e/tests-dfx/project_local_network.bash b/e2e/tests-dfx/project_local_network.bash index 0f467f27a6..37238ee48e 100644 --- a/e2e/tests-dfx/project_local_network.bash +++ b/e2e/tests-dfx/project_local_network.bash @@ -3,163 +3,163 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx start starts a local network if dfx.json defines one" { - dfx_new hello - cat dfx.json - define_project_network + dfx_new hello + cat dfx.json + define_project_network - dfx_start - dfx deploy + dfx_start + dfx deploy - assert_directory_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY" - assert_file_exists .dfx/network/local/pid + assert_directory_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY" + assert_file_exists .dfx/network/local/pid } @test "can run more than one project network at the same time" { - mkdir a - cd a - dfx_new hello - install_asset counter - define_project_network - dfx_start - dfx deploy - dfx canister call hello_backend inc - dfx canister call hello_backend inc - - cd ../.. - - mkdir b - cd b - dfx_new hello - install_asset counter - define_project_network - dfx_start - dfx deploy - dfx canister call hello_backend write '(6: nat)' - - cd ../.. - - ( - cd a/hello - assert_command dfx canister call hello_backend read - assert_eq "(2 : nat)" - ) - - ( - cd b/hello - assert_command dfx canister call hello_backend read - assert_eq "(6 : nat)" - ) - - # the above would work even with a shared network. - # So here's the real trick: they will have the same canister ids, because - # each project has its own replica. - HELLO_BACKEND_ID_A="$(cd a/hello ; dfx canister id hello_backend)" - HELLO_BACKEND_ID_B="$(cd b/hello ; dfx canister id hello_backend)" - assert_eq "$HELLO_BACKEND_ID_A" "$HELLO_BACKEND_ID_B" - - (cd a/hello ; dfx stop) - (cd b/hello ; dfx stop) + mkdir a + cd a + dfx_new hello + install_asset counter + define_project_network + dfx_start + dfx deploy + dfx canister call hello_backend inc + dfx canister call hello_backend inc + + cd ../.. + + mkdir b + cd b + dfx_new hello + install_asset counter + define_project_network + dfx_start + dfx deploy + dfx canister call hello_backend write '(6: nat)' + + cd ../.. + + ( + cd a/hello + assert_command dfx canister call hello_backend read + assert_eq "(2 : nat)" + ) + + ( + cd b/hello + assert_command dfx canister call hello_backend read + assert_eq "(6 : nat)" + ) + + # the above would work even with a shared network. + # So here's the real trick: they will have the same canister ids, because + # each project has its own replica. + HELLO_BACKEND_ID_A="$(cd a/hello ; dfx canister id hello_backend)" + HELLO_BACKEND_ID_B="$(cd b/hello ; dfx canister id hello_backend)" + assert_eq "$HELLO_BACKEND_ID_A" "$HELLO_BACKEND_ID_B" + + (cd a/hello ; dfx stop) + (cd b/hello ; dfx stop) } @test "upgrade a wallet in a project-specific network" { - dfx_new hello - define_project_network + dfx_new hello + define_project_network - dfx_start - use_wallet_wasm 0.8.2 + dfx_start + use_wallet_wasm 0.8.2 - dfx identity get-wallet + dfx identity get-wallet - assert_command dfx canister info "$(dfx identity get-wallet)" - assert_match "Module hash: 0x$(wallet_sha 0.8.2)" + assert_command dfx canister info "$(dfx identity get-wallet)" + assert_match "Module hash: 0x$(wallet_sha 0.8.2)" - use_wallet_wasm 0.10.0 + use_wallet_wasm 0.10.0 - dfx wallet upgrade - assert_command dfx canister info "$(dfx identity get-wallet)" - assert_match "Module hash: 0x$(wallet_sha 0.10.0)" + dfx wallet upgrade + assert_command dfx canister info "$(dfx identity get-wallet)" + assert_match "Module hash: 0x$(wallet_sha 0.10.0)" } @test "with a project-specific network, wallet id is stored local to the project" { - dfx_new hello - define_project_network + dfx_new hello + define_project_network - dfx_start + dfx_start - assert_file_not_exists .dfx/local/wallets.json + assert_file_not_exists .dfx/local/wallets.json - WALLET_ID="$(dfx identity get-wallet)" + WALLET_ID="$(dfx identity get-wallet)" - assert_command jq -r .identities.default.local .dfx/local/wallets.json - assert_eq "$WALLET_ID" - assert_file_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + assert_command jq -r .identities.default.local .dfx/local/wallets.json + assert_eq "$WALLET_ID" + assert_file_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" } @test "with a shared network, wallet id is stored in the shared location" { - dfx_start + dfx_start - assert_file_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + assert_file_not_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - WALLET_ID="$(dfx identity get-wallet)" + WALLET_ID="$(dfx identity get-wallet)" - assert_command jq -r .identities.default.local "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" - assert_eq "$WALLET_ID" - assert_file_not_exists .dfx/local/wallets.json + assert_command jq -r .identities.default.local "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" + assert_eq "$WALLET_ID" + assert_file_not_exists .dfx/local/wallets.json } @test "for project-specific network create stores canister ids for default-ephemeral local networks in .dfx/{network}/canister_ids.json" { - dfx_new hello - define_project_network + dfx_new hello + define_project_network - dfx_start + dfx_start - dfx canister create --all --network local + dfx canister create --all --network local - # canister creates writes to a spinner (stderr), not stdout - assert_command dfx canister id hello_backend --network local - assert_match "$(jq -r .hello_backend.local <.dfx/local/canister_ids.json)" + # canister creates writes to a spinner (stderr), not stdout + assert_command dfx canister id hello_backend --network local + assert_match "$(jq -r .hello_backend.local <.dfx/local/canister_ids.json)" } @test "for project-specific network, create with wallet stores canister ids for configured-ephemeral networks in canister_ids.json" { - dfx_new hello - define_project_network - dfx_start + dfx_new hello + define_project_network + dfx_start - setup_actuallylocal_project_network - jq '.networks.actuallylocal.type="ephemeral"' dfx.json | sponge dfx.json - dfx_set_wallet + setup_actuallylocal_project_network + jq '.networks.actuallylocal.type="ephemeral"' dfx.json | sponge dfx.json + dfx_set_wallet - dfx canister create --all --network actuallylocal + dfx canister create --all --network actuallylocal - # canister creates writes to a spinner (stderr), not stdout - assert_command dfx canister id hello_backend --network actuallylocal - assert_match "$(jq -r .hello_backend.actuallylocal <.dfx/actuallylocal/canister_ids.json)" + # canister creates writes to a spinner (stderr), not stdout + assert_command dfx canister id hello_backend --network actuallylocal + assert_match "$(jq -r .hello_backend.actuallylocal <.dfx/actuallylocal/canister_ids.json)" } @test "dfx start and stop take into account dfx 0.11.x pid files" { - dfx_new hello - define_project_network - dfx_start + dfx_new hello + define_project_network + dfx_start - mv .dfx/network/local/pid .dfx/pid + mv .dfx/network/local/pid .dfx/pid - assert_command_fail dfx start - assert_match 'dfx is already running' + assert_command_fail dfx start + assert_match 'dfx is already running' - assert_command dfx stop - assert_file_not_exists .dfx/pid - assert_not_match "Nothing to do" - assert_command dfx stop - assert_match "Nothing to do" + assert_command dfx stop + assert_file_not_exists .dfx/pid + assert_not_match "Nothing to do" + assert_command dfx stop + assert_match "Nothing to do" } diff --git a/e2e/tests-dfx/provider.bash b/e2e/tests-dfx/provider.bash index d7cfbf7129..ddd37ed625 100644 --- a/e2e/tests-dfx/provider.bash +++ b/e2e/tests-dfx/provider.bash @@ -3,16 +3,16 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - [ -f replica.pid ] && kill -9 "$(cat replica.pid)" - dfx_stop + [ -f replica.pid ] && kill -9 "$(cat replica.pid)" + dfx_stop - standard_teardown + standard_teardown } # This test is around 15 seconds to run. I don't think it should be faster without raising the @@ -23,8 +23,8 @@ teardown() { # Start a replica manually on a specific port. "$(dfx cache show)/replica" --config ' - [http_handler] - write_port_to="port" + [http_handler] + write_port_to="port" ' & echo $! > replica.pid # Use a local file for the replica. sleep 5 # Wait for replica to be available. @@ -48,17 +48,17 @@ teardown() { } @test "network as URL creates the expected name" { - dfx_start - webserver_port=$(get_webserver_port) - dfx canister create --all --network "http://127.0.0.1:$webserver_port" - [ -d ".dfx/http___127_0_0_1_$webserver_port" ] + dfx_start + webserver_port=$(get_webserver_port) + dfx canister create --all --network "http://127.0.0.1:$webserver_port" + [ -d ".dfx/http___127_0_0_1_$webserver_port" ] } @test "network as URL does not create unexpected names" { - dfx_start - webserver_port=$(get_webserver_port) - dfx canister create --all --network "http://127.0.0.1:$webserver_port" - dfx build --all --network "http://127.0.0.1:$webserver_port" - dfx canister install --all --network "http://127.0.0.1:$webserver_port" - assert_eq 1 "$(find .dfx/http* -maxdepth 0 | wc -l | tr -d ' ')" + dfx_start + webserver_port=$(get_webserver_port) + dfx canister create --all --network "http://127.0.0.1:$webserver_port" + dfx build --all --network "http://127.0.0.1:$webserver_port" + dfx canister install --all --network "http://127.0.0.1:$webserver_port" + assert_eq 1 "$(find .dfx/http* -maxdepth 0 | wc -l | tr -d ' ')" } diff --git a/e2e/tests-dfx/remote.bash b/e2e/tests-dfx/remote.bash index cdc4b9f22c..5bd9a08d04 100644 --- a/e2e/tests-dfx/remote.bash +++ b/e2e/tests-dfx/remote.bash @@ -3,318 +3,318 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "canister call and sign" { - install_asset remote/call/actual - dfx_start - setup_actuallylocal_shared_network - - dfx identity new --storage-mode plaintext alice - - assert_command dfx deploy --network actuallylocal --identity alice - assert_command dfx canister call remote write '("initial data in the remote canister")' --identity alice --network actuallylocal - assert_command dfx canister call remote read --identity alice --network actuallylocal - assert_eq '("initial data in the remote canister")' - - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json - - install_asset remote/call/mock - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - setup_actuallylocal_shared_network - setup_local_shared_network - - # set up: remote method is update, local is query - # call remote method as update to make a change - assert_command dfx deploy --network actuallylocal - assert_command dfx canister call remote which_am_i --network actuallylocal - assert_eq '("actual")' - - cat dfx.json - cat canister_ids.json - - # - # Here the mock doesn't know about the method at all. - # In order for the candid decoder to know the type, dfx must both: - # - look up the canister by (remote) canister id - # - use the remote candid definition - # - assert_command dfx canister call --query "$REMOTE_CANISTER_ID" make_struct '("A query by principal", "B query by principal")' --network actuallylocal - assert_eq '(record { a = "A query by principal"; b = "B query by principal" })' - assert_command dfx canister call "$REMOTE_CANISTER_ID" make_struct '("A default by principal", "B default by principal")' --network actuallylocal - assert_eq '(record { a = "A default by principal"; b = "B default by principal" })' - assert_command dfx canister call --update "$REMOTE_CANISTER_ID" make_struct '("A update by principal", "B update by principal")' --network actuallylocal - assert_eq '(record { a = "A update by principal"; b = "B update by principal" })' - - assert_command dfx canister call --query remote make_struct '("A query by name", "B query by name")' --network actuallylocal - assert_eq '(record { a = "A query by name"; b = "B query by name" })' - assert_command dfx canister call remote make_struct '("A default by name", "B default by name")' --network actuallylocal - assert_eq '(record { a = "A default by name"; b = "B default by name" })' - assert_command dfx canister call --update remote make_struct '("A update by name", "B update by name")' --network actuallylocal - assert_eq '(record { a = "A update by name"; b = "B update by name" })' - - # This also should work when no canister type can be determined / if no info but the bare minimum of remote id and remote candid is given: - jq 'del(.canisters.remote.main)' dfx.json | sponge dfx.json - assert_command dfx canister call --query remote make_struct '("A query by name", "B query by name")' --network actuallylocal - assert_eq '(record { a = "A query by name"; b = "B query by name" })' - - # We can't check this for sign, because dfx canister send outputs something like this: - # To see the content of response, copy-paste the encoded string into cbor.me. - # Response: d9d9f7a2667374617475736[snip]2696e636970616c - - # - # Here: - # - the actual method is an update - # - the mock method is a query - # - in remote candid definition, the method is an update - # We try to call with --query. - # We expect dfx to notice that the method is in fact an update, which it knows from the remote candid definition. - # - assert_command_fail dfx canister call --query "$REMOTE_CANISTER_ID" actual_update_mock_query_remote_candid_update '("call by principal with --query")' --network actuallylocal - assert_match 'not a query method' - assert_command_fail dfx canister call --query remote actual_update_mock_query_remote_candid_update '("call by name with --query")' --network actuallylocal - assert_match 'not a query method' - - # And the same for dfx canister sign: - assert_command_fail dfx canister sign --query "$REMOTE_CANISTER_ID" actual_update_mock_query_remote_candid_update '("call by principal with --query")' --network actuallylocal - assert_match 'not a query method' - assert_command_fail dfx canister sign --query remote actual_update_mock_query_remote_candid_update '("call by name with --query")' --network actuallylocal - assert_match 'not a query method' + install_asset remote/call/actual + dfx_start + setup_actuallylocal_shared_network + + dfx identity new --storage-mode plaintext alice + + assert_command dfx deploy --network actuallylocal --identity alice + assert_command dfx canister call remote write '("initial data in the remote canister")' --identity alice --network actuallylocal + assert_command dfx canister call remote read --identity alice --network actuallylocal + assert_eq '("initial data in the remote canister")' + + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json + + install_asset remote/call/mock + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + setup_actuallylocal_shared_network + setup_local_shared_network + + # set up: remote method is update, local is query + # call remote method as update to make a change + assert_command dfx deploy --network actuallylocal + assert_command dfx canister call remote which_am_i --network actuallylocal + assert_eq '("actual")' + + cat dfx.json + cat canister_ids.json + + # + # Here the mock doesn't know about the method at all. + # In order for the candid decoder to know the type, dfx must both: + # - look up the canister by (remote) canister id + # - use the remote candid definition + # + assert_command dfx canister call --query "$REMOTE_CANISTER_ID" make_struct '("A query by principal", "B query by principal")' --network actuallylocal + assert_eq '(record { a = "A query by principal"; b = "B query by principal" })' + assert_command dfx canister call "$REMOTE_CANISTER_ID" make_struct '("A default by principal", "B default by principal")' --network actuallylocal + assert_eq '(record { a = "A default by principal"; b = "B default by principal" })' + assert_command dfx canister call --update "$REMOTE_CANISTER_ID" make_struct '("A update by principal", "B update by principal")' --network actuallylocal + assert_eq '(record { a = "A update by principal"; b = "B update by principal" })' + + assert_command dfx canister call --query remote make_struct '("A query by name", "B query by name")' --network actuallylocal + assert_eq '(record { a = "A query by name"; b = "B query by name" })' + assert_command dfx canister call remote make_struct '("A default by name", "B default by name")' --network actuallylocal + assert_eq '(record { a = "A default by name"; b = "B default by name" })' + assert_command dfx canister call --update remote make_struct '("A update by name", "B update by name")' --network actuallylocal + assert_eq '(record { a = "A update by name"; b = "B update by name" })' + + # This also should work when no canister type can be determined / if no info but the bare minimum of remote id and remote candid is given: + jq 'del(.canisters.remote.main)' dfx.json | sponge dfx.json + assert_command dfx canister call --query remote make_struct '("A query by name", "B query by name")' --network actuallylocal + assert_eq '(record { a = "A query by name"; b = "B query by name" })' + + # We can't check this for sign, because dfx canister send outputs something like this: + # To see the content of response, copy-paste the encoded string into cbor.me. + # Response: d9d9f7a2667374617475736[snip]2696e636970616c + + # + # Here: + # - the actual method is an update + # - the mock method is a query + # - in remote candid definition, the method is an update + # We try to call with --query. + # We expect dfx to notice that the method is in fact an update, which it knows from the remote candid definition. + # + assert_command_fail dfx canister call --query "$REMOTE_CANISTER_ID" actual_update_mock_query_remote_candid_update '("call by principal with --query")' --network actuallylocal + assert_match 'not a query method' + assert_command_fail dfx canister call --query remote actual_update_mock_query_remote_candid_update '("call by name with --query")' --network actuallylocal + assert_match 'not a query method' + + # And the same for dfx canister sign: + assert_command_fail dfx canister sign --query "$REMOTE_CANISTER_ID" actual_update_mock_query_remote_candid_update '("call by principal with --query")' --network actuallylocal + assert_match 'not a query method' + assert_command_fail dfx canister sign --query remote actual_update_mock_query_remote_candid_update '("call by name with --query")' --network actuallylocal + assert_match 'not a query method' } @test "canister create fails for a remote canister" { - install_asset remote/actual - dfx_start - setup_actuallylocal_shared_network + install_asset remote/actual + dfx_start + setup_actuallylocal_shared_network - dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext alice - assert_command dfx deploy --network actuallylocal --identity alice + assert_command dfx deploy --network actuallylocal --identity alice - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json - install_asset remote/basic - setup_actuallylocal_shared_network - setup_local_shared_network - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + install_asset remote/basic + setup_actuallylocal_shared_network + setup_local_shared_network + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - assert_command_fail dfx canister create remote --network actuallylocal - assert_match "Canister 'remote' is a remote canister on network 'actuallylocal', and cannot be created from here." + assert_command_fail dfx canister create remote --network actuallylocal + assert_match "Canister 'remote' is a remote canister on network 'actuallylocal', and cannot be created from here." } @test "canister install fails for a remote canister" { - install_asset remote/actual - dfx_start - setup_actuallylocal_shared_network + install_asset remote/actual + dfx_start + setup_actuallylocal_shared_network - dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext alice - assert_command dfx deploy --network actuallylocal --identity alice + assert_command dfx deploy --network actuallylocal --identity alice - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json - install_asset remote/basic - setup_actuallylocal_shared_network - setup_local_shared_network - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + install_asset remote/basic + setup_actuallylocal_shared_network + setup_local_shared_network + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - assert_command_fail dfx canister install remote --network actuallylocal - assert_match "Canister 'remote' is a remote canister on network 'actuallylocal', and cannot be installed from here." + assert_command_fail dfx canister install remote --network actuallylocal + assert_match "Canister 'remote' is a remote canister on network 'actuallylocal', and cannot be installed from here." } @test "canister create --all, canister install --all skip remote canisters" { - install_asset remote/actual - dfx_start - setup_actuallylocal_shared_network + install_asset remote/actual + dfx_start + setup_actuallylocal_shared_network - # - # Set up the "remote" canister, with a different controller in order to - # demonstrate that we don't try to install/upgrade it as a remote canister. - # - dfx identity new --storage-mode plaintext alice + # + # Set up the "remote" canister, with a different controller in order to + # demonstrate that we don't try to install/upgrade it as a remote canister. + # + dfx identity new --storage-mode plaintext alice - assert_command dfx deploy --network actuallylocal --identity alice - assert_command dfx canister call remote write '("this is data in the remote canister")' --identity alice --network actuallylocal + assert_command dfx deploy --network actuallylocal --identity alice + assert_command dfx canister call remote write '("this is data in the remote canister")' --identity alice --network actuallylocal - assert_command dfx canister call remote read --identity alice --network actuallylocal - assert_eq '("this is data in the remote canister")' + assert_command dfx canister call remote read --identity alice --network actuallylocal + assert_eq '("this is data in the remote canister")' - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json - install_asset remote/basic - setup_actuallylocal_shared_network - setup_local_shared_network - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + install_asset remote/basic + setup_actuallylocal_shared_network + setup_local_shared_network + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - # Here we want to make sure that create+build+install works with the common flow - assert_command dfx canister create --all - assert_command dfx build - assert_command dfx canister install --all + # Here we want to make sure that create+build+install works with the common flow + assert_command dfx canister create --all + assert_command dfx build + assert_command dfx canister install --all - assert_command dfx canister call basic read_remote - assert_eq '("")' - assert_command dfx canister call remote which_am_i - assert_eq '("mock")' + assert_command dfx canister call basic read_remote + assert_eq '("")' + assert_command dfx canister call remote which_am_i + assert_eq '("mock")' - assert_command dfx canister create --all --network actuallylocal - assert_command dfx build --network actuallylocal -vv - assert_match "Not building canister 'remote'" - assert_command dfx canister install --all --network actuallylocal + assert_command dfx canister create --all --network actuallylocal + assert_command dfx build --network actuallylocal -vv + assert_match "Not building canister 'remote'" + assert_command dfx canister install --all --network actuallylocal - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("this is data in the remote canister")' + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("this is data in the remote canister")' - # We can change the value by calling the original canister - assert_command dfx canister call "${REMOTE_CANISTER_ID}" write '("altered data (by canister id) in the remote canister")' --network actuallylocal - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("altered data (by canister id) in the remote canister")' + # We can change the value by calling the original canister + assert_command dfx canister call "${REMOTE_CANISTER_ID}" write '("altered data (by canister id) in the remote canister")' --network actuallylocal + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("altered data (by canister id) in the remote canister")' - # We can also call through the canister name - assert_command dfx canister call remote write '("altered data (by canister name) in the remote canister")' --network actuallylocal - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("altered data (by canister name) in the remote canister")' + # We can also call through the canister name + assert_command dfx canister call remote write '("altered data (by canister name) in the remote canister")' --network actuallylocal + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("altered data (by canister name) in the remote canister")' - assert_command dfx canister call remote which_am_i --network actuallylocal - assert_eq '("actual")' + assert_command dfx canister call remote which_am_i --network actuallylocal + assert_eq '("actual")' - assert_command jq .basic.actuallylocal canister_ids.json - assert_eq '"'"$(dfx canister id basic --network actuallylocal)"'"' + assert_command jq .basic.actuallylocal canister_ids.json + assert_eq '"'"$(dfx canister id basic --network actuallylocal)"'"' - assert_command jq .remote canister_ids.json - assert_eq "null" + assert_command jq .remote canister_ids.json + assert_eq "null" - # Assert frontend declarations are actually created - dfx generate - assert_file_exists "src/declarations/remote/remote.did" - assert_file_exists "src/declarations/remote/remote.did.js" - assert_file_exists "src/declarations/remote/remote.did.d.ts" - assert_file_exists "src/declarations/remote/index.js" - assert_file_exists "src/declarations/remote/index.d.ts" + # Assert frontend declarations are actually created + dfx generate + assert_file_exists "src/declarations/remote/remote.did" + assert_file_exists "src/declarations/remote/remote.did.js" + assert_file_exists "src/declarations/remote/remote.did.d.ts" + assert_file_exists "src/declarations/remote/index.js" + assert_file_exists "src/declarations/remote/index.d.ts" } @test "for remote build, checks imports against the candid file rather than the mock" { - # In this test, a canister with a remote ID also has a candid file specified. - # When building for the remote network, we expect to use this candid file, - # and for methods calls that don't match the candid file to fail. - - install_asset remote/actual - dfx_start - setup_actuallylocal_shared_network - - # - # Set up the "remote" canister, with a different controller in order to - # demonstrate that we don't try to install/upgrade it as a remote canister. - # - dfx identity new --storage-mode plaintext alice - - assert_command dfx deploy --network actuallylocal --identity alice - - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json - - install_asset remote/extra - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - setup_actuallylocal_shared_network - setup_local_shared_network - - # We expect the local network deploy to succeed, because it is built using the candid file from - # the local canister. - assert_command dfx deploy - - # And we can call the extra method, - assert_command dfx canister call extra remote_extra - assert_eq '("extra!")' - - # We expect the remote network deploy to fail, because it is built using the candid file - # specified for the remote canister. That candid file doesn't define the extra method - # that is present in the mock. - assert_command_fail dfx deploy --network actuallylocal + # In this test, a canister with a remote ID also has a candid file specified. + # When building for the remote network, we expect to use this candid file, + # and for methods calls that don't match the candid file to fail. + + install_asset remote/actual + dfx_start + setup_actuallylocal_shared_network + + # + # Set up the "remote" canister, with a different controller in order to + # demonstrate that we don't try to install/upgrade it as a remote canister. + # + dfx identity new --storage-mode plaintext alice + + assert_command dfx deploy --network actuallylocal --identity alice + + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json + + install_asset remote/extra + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + setup_actuallylocal_shared_network + setup_local_shared_network + + # We expect the local network deploy to succeed, because it is built using the candid file from + # the local canister. + assert_command dfx deploy + + # And we can call the extra method, + assert_command dfx canister call extra remote_extra + assert_eq '("extra!")' + + # We expect the remote network deploy to fail, because it is built using the candid file + # specified for the remote canister. That candid file doesn't define the extra method + # that is present in the mock. + assert_command_fail dfx deploy --network actuallylocal } @test "build + install + call -- remote" { - install_asset remote/actual - dfx_start - setup_actuallylocal_shared_network + install_asset remote/actual + dfx_start + setup_actuallylocal_shared_network - # - # Set up the "remote" canister, with a different controller in order to - # demonstrate that we don't try to install/upgrade it as a remote canister. - # - dfx identity new --storage-mode plaintext alice + # + # Set up the "remote" canister, with a different controller in order to + # demonstrate that we don't try to install/upgrade it as a remote canister. + # + dfx identity new --storage-mode plaintext alice - assert_command dfx deploy --network actuallylocal --identity alice - assert_command dfx canister call remote write '("this is data in the remote canister")' --network actuallylocal --identity alice + assert_command dfx deploy --network actuallylocal --identity alice + assert_command dfx canister call remote write '("this is data in the remote canister")' --network actuallylocal --identity alice - assert_command dfx canister call remote read --network actuallylocal --identity alice - assert_eq '("this is data in the remote canister")' + assert_command dfx canister call remote read --network actuallylocal --identity alice + assert_eq '("this is data in the remote canister")' - REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) - echo "Remote canister id: $REMOTE_CANISTER_ID" - rm canister_ids.json + REMOTE_CANISTER_ID=$(jq -r .remote.actuallylocal canister_ids.json) + echo "Remote canister id: $REMOTE_CANISTER_ID" + rm canister_ids.json - install_asset remote/basic - setup_actuallylocal_shared_network - setup_local_shared_network - jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json + install_asset remote/basic + setup_actuallylocal_shared_network + setup_local_shared_network + jq ".canisters.remote.remote.id.actuallylocal=\"$REMOTE_CANISTER_ID\"" dfx.json | sponge dfx.json - assert_command dfx deploy - assert_command dfx canister call basic read_remote - assert_eq '("")' - assert_command dfx canister call remote which_am_i - assert_eq '("mock")' + assert_command dfx deploy + assert_command dfx canister call basic read_remote + assert_eq '("")' + assert_command dfx canister call remote which_am_i + assert_eq '("mock")' - assert_command dfx deploy --network actuallylocal -vv - assert_match "Not building canister 'remote'" - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("this is data in the remote canister")' + assert_command dfx deploy --network actuallylocal -vv + assert_match "Not building canister 'remote'" + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("this is data in the remote canister")' - # We can change the value by calling the original canister - assert_command dfx canister call "${REMOTE_CANISTER_ID}" write '("data altered by canister id in the remote canister")' --network actuallylocal - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("data altered by canister id in the remote canister")' + # We can change the value by calling the original canister + assert_command dfx canister call "${REMOTE_CANISTER_ID}" write '("data altered by canister id in the remote canister")' --network actuallylocal + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("data altered by canister id in the remote canister")' - # We can also call through the canister name - assert_command dfx canister call remote write '("data altered by canister name in the remote canister")' --network actuallylocal - assert_command dfx canister call basic read_remote --network actuallylocal - assert_eq '("data altered by canister name in the remote canister")' + # We can also call through the canister name + assert_command dfx canister call remote write '("data altered by canister name in the remote canister")' --network actuallylocal + assert_command dfx canister call basic read_remote --network actuallylocal + assert_eq '("data altered by canister name in the remote canister")' - assert_command dfx canister call remote which_am_i --network actuallylocal - assert_eq '("actual")' + assert_command dfx canister call remote which_am_i --network actuallylocal + assert_eq '("actual")' - assert_command jq .basic.actuallylocal canister_ids.json - assert_eq '"'"$(dfx canister id basic --network actuallylocal)"'"' + assert_command jq .basic.actuallylocal canister_ids.json + assert_eq '"'"$(dfx canister id basic --network actuallylocal)"'"' - assert_command jq .remote canister_ids.json - assert_eq "null" + assert_command jq .remote canister_ids.json + assert_eq "null" } @test "build step sets remote cid environment variable correctly" { - install_asset remote/envvar - install_asset wasm/identity # need to have some .did and .wasm files to point our custom canister to - dfx_start - setup_actuallylocal_shared_network + install_asset remote/envvar + install_asset wasm/identity # need to have some .did and .wasm files to point our custom canister to + dfx_start + setup_actuallylocal_shared_network - assert_command dfx deploy --network actuallylocal -vv - assert_match "CANISTER_ID_remote: qoctq-giaaa-aaaaa-aaaea-cai" + assert_command dfx deploy --network actuallylocal -vv + assert_match "CANISTER_ID_remote: qoctq-giaaa-aaaaa-aaaea-cai" } diff --git a/e2e/tests-dfx/remote_generate_binding.bash b/e2e/tests-dfx/remote_generate_binding.bash index f6dff63a3b..d29c16fe70 100644 --- a/e2e/tests-dfx/remote_generate_binding.bash +++ b/e2e/tests-dfx/remote_generate_binding.bash @@ -3,114 +3,114 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - standard_teardown + standard_teardown } @test "remote generate-binding succeeds for --all" { - install_asset remote_generate_binding/basic + install_asset remote_generate_binding/basic - assert_command dfx remote generate-binding --all + assert_command dfx remote generate-binding --all - assert_file_exists remote.mo - assert_file_exists remote.rs - assert_file_exists remote.ts - assert_file_exists remote.js + assert_file_exists remote.mo + assert_file_exists remote.rs + assert_file_exists remote.ts + assert_file_exists remote.js } @test "remote generate-binding --overwrite succeeds for --all" { - install_asset remote_generate_binding/basic - echo "to_overwrite" > remote.mo - echo "to_overwrite" > remote.rs + install_asset remote_generate_binding/basic + echo "to_overwrite" > remote.mo + echo "to_overwrite" > remote.rs - assert_command dfx remote generate-binding --overwrite --all + assert_command dfx remote generate-binding --overwrite --all - # should generate if not present - assert_file_exists remote.js - assert_file_exists remote.ts + # should generate if not present + assert_file_exists remote.js + assert_file_exists remote.ts - # should overwrite if already present - assert_neq "to_overwrite" "$(cat remote.mo)" - assert_neq "to_overwrite" "$(cat remote.rs)" + # should overwrite if already present + assert_neq "to_overwrite" "$(cat remote.mo)" + assert_neq "to_overwrite" "$(cat remote.rs)" } @test "remote generate-binding does not overwrite if file already present" { - install_asset remote_generate_binding/basic - echo "to_overwrite" > remote.mo - echo "to_overwrite" > remote.rs + install_asset remote_generate_binding/basic + echo "to_overwrite" > remote.mo + echo "to_overwrite" > remote.rs - assert_command dfx remote generate-binding --all + assert_command dfx remote generate-binding --all - # should generate if not present - assert_file_exists remote.ts - assert_file_exists remote.js + # should generate if not present + assert_file_exists remote.ts + assert_file_exists remote.js - # should not overwrite if already present - assert_eq "to_overwrite" "$(cat remote.mo)" - assert_eq "to_overwrite" "$(cat remote.rs)" + # should not overwrite if already present + assert_eq "to_overwrite" "$(cat remote.mo)" + assert_eq "to_overwrite" "$(cat remote.rs)" } @test "remote generate-binding succeeds for specific rust canister" { - install_asset remote_generate_binding/basic + install_asset remote_generate_binding/basic - assert_command dfx remote generate-binding remote-rust + assert_command dfx remote generate-binding remote-rust - assert_file_exists remote.rs - assert_file_not_exists remote.mo - assert_file_not_exists remote.ts - assert_file_not_exists remote.js + assert_file_exists remote.rs + assert_file_not_exists remote.mo + assert_file_not_exists remote.ts + assert_file_not_exists remote.js } @test "remote generate-binding succeeds for specific motoko canister" { - install_asset remote_generate_binding/basic + install_asset remote_generate_binding/basic - assert_command dfx remote generate-binding remote-motoko + assert_command dfx remote generate-binding remote-motoko - assert_file_exists remote.mo - assert_file_not_exists remote.rs - assert_file_not_exists remote.ts - assert_file_not_exists remote.js + assert_file_exists remote.mo + assert_file_not_exists remote.rs + assert_file_not_exists remote.ts + assert_file_not_exists remote.js } @test "remote generate-binding succeeds for specific javascript canister" { - install_asset remote_generate_binding/basic + install_asset remote_generate_binding/basic - assert_command dfx remote generate-binding remote-javascript + assert_command dfx remote generate-binding remote-javascript - assert_file_exists remote.js - assert_file_not_exists remote.mo - assert_file_not_exists remote.rs - assert_file_not_exists remote.ts + assert_file_exists remote.js + assert_file_not_exists remote.mo + assert_file_not_exists remote.rs + assert_file_not_exists remote.ts } @test "remote generate-binding succeeds for specific typescript canister" { - install_asset remote_generate_binding/basic + install_asset remote_generate_binding/basic - assert_command dfx remote generate-binding remote-typescript + assert_command dfx remote generate-binding remote-typescript - assert_file_exists remote.ts - assert_file_not_exists remote.mo - assert_file_not_exists remote.rs - assert_file_not_exists remote.js + assert_file_exists remote.ts + assert_file_not_exists remote.mo + assert_file_not_exists remote.rs + assert_file_not_exists remote.js } @test "remote generate-binding --overwrite succeeds for specific canister" { - install_asset remote_generate_binding/basic - echo "to_overwrite" > remote.mo + install_asset remote_generate_binding/basic + echo "to_overwrite" > remote.mo - # should not overwrite without --overwrite - assert_command dfx remote generate-binding remote-motoko - assert_match 'already exists' - assert_eq "to_overwrite" "$(cat remote.mo)" + # should not overwrite without --overwrite + assert_command dfx remote generate-binding remote-motoko + assert_match 'already exists' + assert_eq "to_overwrite" "$(cat remote.mo)" - # should overwrite with --overwrite - assert_command dfx remote generate-binding --overwrite remote-motoko - assert_neq "to_overwrite" "$(cat remote.mo)" + # should overwrite with --overwrite + assert_command dfx remote generate-binding --overwrite remote-motoko + assert_neq "to_overwrite" "$(cat remote.mo)" } @test "remote generate-binding incomplete command rejected" { - assert_command_fail dfx remote generate-binding + assert_command_fail dfx remote generate-binding } diff --git a/e2e/tests-dfx/request_status.bash b/e2e/tests-dfx/request_status.bash index 21246ef8e5..ef918b2c4c 100644 --- a/e2e/tests-dfx/request_status.bash +++ b/e2e/tests-dfx/request_status.bash @@ -3,29 +3,29 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "request-status output raw" { - install_asset greet - dfx_start - dfx canister create --all - dfx build + install_asset greet + dfx_start + dfx canister create --all + dfx build - dfx canister install hello_backend + dfx canister install hello_backend - assert_command dfx canister call --async hello_backend greet Bob + assert_command dfx canister call --async hello_backend greet Bob - # shellcheck disable=SC2154 - assert_command dfx canister request-status --output raw "$stdout" "$(dfx canister id hello_backend)" - assert_eq '4449444c0001710b48656c6c6f2c20426f6221' + # shellcheck disable=SC2154 + assert_command dfx canister request-status --output raw "$stdout" "$(dfx canister id hello_backend)" + assert_eq '4449444c0001710b48656c6c6f2c20426f6221' } diff --git a/e2e/tests-dfx/rust.bash b/e2e/tests-dfx/rust.bash index eea3705196..be545b941c 100644 --- a/e2e/tests-dfx/rust.bash +++ b/e2e/tests-dfx/rust.bash @@ -4,61 +4,61 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "rust starter project can build and call" { - dfx_new_rust hello + dfx_new_rust hello - dfx_start - dfx canister create --all - assert_command dfx build hello_backend -vvv - assert_match "Shrinking WASM" - assert_command dfx canister install hello_backend - assert_command dfx canister call hello_backend greet dfinity - assert_match '("Hello, dfinity!")' + dfx_start + dfx canister create --all + assert_command dfx build hello_backend -vvv + assert_match "Shrinking WASM" + assert_command dfx canister install hello_backend + assert_command dfx canister call hello_backend greet dfinity + assert_match '("Hello, dfinity!")' - # dfx sets the candid:service metadata - dfx canister metadata hello_backend candid:service >installed.did - assert_command diff src/hello_backend/hello_backend.did installed.did + # dfx sets the candid:service metadata + dfx canister metadata hello_backend candid:service >installed.did + assert_command diff src/hello_backend/hello_backend.did installed.did } @test "rust canister can resolve dependencies" { - dfx_new_rust rust_deps - install_asset rust_deps - dfx_start - assert_command dfx deploy - assert_command dfx canister call multiply_deps read - assert_match '(1 : nat)' - assert_command dfx canister call multiply_deps mul '(3)' - assert_match '(9 : nat)' - assert_command dfx canister call rust_deps read - assert_match '(9 : nat)' + dfx_new_rust rust_deps + install_asset rust_deps + dfx_start + assert_command dfx deploy + assert_command dfx canister call multiply_deps read + assert_match '(1 : nat)' + assert_command dfx canister call multiply_deps mul '(3)' + assert_match '(9 : nat)' + assert_command dfx canister call rust_deps read + assert_match '(9 : nat)' } @test "rust canister can have nonstandard target dir location" { - dfx_new_rust - # We used to set CARGO_TARGET_DIR="$(echo -ne '\x81')" - # But since rust 1.69, `cargo metadata` returns - # error: path contains invalid UTF-8 characters - CARGO_TARGET_DIR="custom-target" - export CARGO_TARGET_DIR - dfx_start - assert_command dfx deploy - assert_command dfx canister call e2e_project_backend greet dfinity + dfx_new_rust + # We used to set CARGO_TARGET_DIR="$(echo -ne '\x81')" + # But since rust 1.69, `cargo metadata` returns + # error: path contains invalid UTF-8 characters + CARGO_TARGET_DIR="custom-target" + export CARGO_TARGET_DIR + dfx_start + assert_command dfx deploy + assert_command dfx canister call e2e_project_backend greet dfinity } @test "rust canister fails to build with missing lockfile" { - dfx_new_rust - rm -f ./Cargo.lock - dfx_start - assert_command_fail dfx deploy - cargo update - assert_command dfx deploy + dfx_new_rust + rm -f ./Cargo.lock + dfx_start + assert_command_fail dfx deploy + cargo update + assert_command dfx deploy } diff --git a/e2e/tests-dfx/schema.bash b/e2e/tests-dfx/schema.bash index b3433050ee..3886840934 100644 --- a/e2e/tests-dfx/schema.bash +++ b/e2e/tests-dfx/schema.bash @@ -3,22 +3,22 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop - standard_teardown + dfx_stop + standard_teardown } @test "dfx schema prints valid json" { - assert_command dfx schema --outfile out.json - # make sure out.json contains exactly one json object - assert_command jq type out.json - assert_eq '"object"' + assert_command dfx schema --outfile out.json + # make sure out.json contains exactly one json object + assert_command jq type out.json + assert_eq '"object"' } @test "dfx schema still works with broken dfx.json" { - jq '.broken_key="blahblahblah"' dfx.json | sponge dfx.json - assert_command dfx schema + jq '.broken_key="blahblahblah"' dfx.json | sponge dfx.json + assert_command dfx schema } diff --git a/e2e/tests-dfx/shared_local_network.bash b/e2e/tests-dfx/shared_local_network.bash index 586b38e1f8..c1a82364a7 100644 --- a/e2e/tests-dfx/shared_local_network.bash +++ b/e2e/tests-dfx/shared_local_network.bash @@ -3,135 +3,135 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx start creates no files in the current directory when run from an empty directory" { - dfx_start - assert_command find . - assert_eq "." + dfx_start + assert_command find . + assert_eq "." } @test "dfx start creates only an empty .dfx directory when run from a project" { - dfx_new hello - START_DIR_CONTENTS="$(find .)" - dfx_start - END_DIR_CONTENTS="$(find . | grep -v '^\./\.dfx$')" - assert_eq "$START_DIR_CONTENTS" "$END_DIR_CONTENTS" + dfx_new hello + START_DIR_CONTENTS="$(find .)" + dfx_start + END_DIR_CONTENTS="$(find . | grep -v '^\./\.dfx$')" + assert_eq "$START_DIR_CONTENTS" "$END_DIR_CONTENTS" } @test "project data is cleared after dfx start --clean from outside the project" { - mkdir somewhere - ( - cd somewhere - dfx_start - ) - - ( - dfx_new hello - dfx deploy - dfx canister id hello_backend - ) - - mkdir somewhere_else - ( - cd somewhere_else - dfx_stop - dfx_start --clean - ) - - ( - cd hello - assert_command_fail dfx canister id hello_backend - ) -} - -@test "multiple projects with the same canister names" { + mkdir somewhere + ( + cd somewhere dfx_start + ) - mkdir a - cd a + ( dfx_new hello - install_asset counter dfx deploy - HELLO_BACKEND_A="$(dfx canister id hello_backend)" - dfx canister call hello_backend inc - dfx canister call hello_backend inc - cd ../.. + dfx canister id hello_backend + ) - mkdir b - cd b - dfx_new hello - install_asset counter - dfx deploy - HELLO_BACKEND_B="$(dfx canister id hello_backend)" - dfx canister call hello_backend write '(6: nat)' - cd ../.. + mkdir somewhere_else + ( + cd somewhere_else + dfx_stop + dfx_start --clean + ) - assert_command dfx canister call "$HELLO_BACKEND_A" read + ( + cd hello + assert_command_fail dfx canister id hello_backend + ) +} + +@test "multiple projects with the same canister names" { + dfx_start + + mkdir a + cd a + dfx_new hello + install_asset counter + dfx deploy + HELLO_BACKEND_A="$(dfx canister id hello_backend)" + dfx canister call hello_backend inc + dfx canister call hello_backend inc + cd ../.. + + mkdir b + cd b + dfx_new hello + install_asset counter + dfx deploy + HELLO_BACKEND_B="$(dfx canister id hello_backend)" + dfx canister call hello_backend write '(6: nat)' + cd ../.. + + assert_command dfx canister call "$HELLO_BACKEND_A" read + assert_eq "(2 : nat)" + ( + cd a/hello + assert_command dfx canister call hello_backend read assert_eq "(2 : nat)" - ( - cd a/hello - assert_command dfx canister call hello_backend read - assert_eq "(2 : nat)" - ) + ) - assert_command dfx canister call "$HELLO_BACKEND_B" read + assert_command dfx canister call "$HELLO_BACKEND_B" read + assert_eq "(6 : nat)" + ( + cd b/hello + assert_command dfx canister call hello_backend read assert_eq "(6 : nat)" - ( - cd b/hello - assert_command dfx canister call hello_backend read - assert_eq "(6 : nat)" - ) + ) } @test "wallet config file is reset after start --clean" { - dfx_start - - ( - dfx_new hello - dfx wallet balance - dfx identity get-wallet - assert_command dfx diagnose - assert_eq "No problems found" - ) + dfx_start - dfx_stop - dfx_start --clean - - ( - cd hello - assert_command_fail dfx diagnose - assert_match "No wallet found; nothing to do" - ) + ( + dfx_new hello + dfx wallet balance + dfx identity get-wallet + assert_command dfx diagnose + assert_eq "No problems found" + ) + + dfx_stop + dfx_start --clean + + ( + cd hello + assert_command_fail dfx diagnose + assert_match "No wallet found; nothing to do" + ) } @test "separate projects use the same wallet id for a given identity" { - dfx_start + dfx_start - ( dfx_new a ) - ( dfx_new b ) - WALLET_ID_A="$(cd a ; dfx identity get-wallet)" - WALLET_ID_B="$(cd b ; dfx identity get-wallet)" + ( dfx_new a ) + ( dfx_new b ) + WALLET_ID_A="$(cd a ; dfx identity get-wallet)" + WALLET_ID_B="$(cd b ; dfx identity get-wallet)" - assert_eq "$WALLET_ID_A" "$WALLET_ID_B" + assert_eq "$WALLET_ID_A" "$WALLET_ID_B" } @test "dfx identity rename renames wallet for shared local network" { - dfx_start + dfx_start - dfx identity new alice --storage-mode plaintext - ALICE_WALLET="$(dfx identity get-wallet --identity alice)" + dfx identity new alice --storage-mode plaintext + ALICE_WALLET="$(dfx identity get-wallet --identity alice)" - dfx identity rename alice bob - BOB_WALLET="$(dfx identity get-wallet --identity bob)" + dfx identity rename alice bob + BOB_WALLET="$(dfx identity get-wallet --identity bob)" - assert_eq "$ALICE_WALLET" "$BOB_WALLET" + assert_eq "$ALICE_WALLET" "$BOB_WALLET" } diff --git a/e2e/tests-dfx/sign_send.bash b/e2e/tests-dfx/sign_send.bash index 61b879ed89..a0b848c6fd 100644 --- a/e2e/tests-dfx/sign_send.bash +++ b/e2e/tests-dfx/sign_send.bash @@ -3,81 +3,81 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "sign + send" { - install_asset counter - dfx_start - dfx deploy + install_asset counter + dfx_start + dfx deploy - assert_command dfx canister sign --query hello_backend read - assert_eq "Query message generated at [message.json]" + assert_command dfx canister sign --query hello_backend read + assert_eq "Query message generated at [message.json]" - sleep 10 - echo y | assert_command dfx canister send message.json + sleep 10 + echo y | assert_command dfx canister send message.json - assert_command_fail dfx canister send message.json --status - assert_eq "Error: Can only check request_status on update calls." + assert_command_fail dfx canister send message.json --status + assert_eq "Error: Can only check request_status on update calls." - assert_command_fail dfx canister sign --query hello_backend read - assert_eq "Error: [message.json] already exists, please specify a different output file name." + assert_command_fail dfx canister sign --query hello_backend read + assert_eq "Error: [message.json] already exists, please specify a different output file name." - assert_command dfx canister sign --update hello_backend inc --file message-inc.json - assert_eq "Update message generated at [message-inc.json] + assert_command dfx canister sign --update hello_backend inc --file message-inc.json + assert_eq "Update message generated at [message-inc.json] Signed request_status append to update message in [message-inc.json]" - sleep 10 - echo y | assert_command dfx canister send message-inc.json - assert_command dfx canister send message-inc.json --status - assert_match "To see the content of response, copy-paste the encoded string into cbor.me." + sleep 10 + echo y | assert_command dfx canister send message-inc.json + assert_command dfx canister send message-inc.json --status + assert_match "To see the content of response, copy-paste the encoded string into cbor.me." } @test "sign outside of a dfx project" { - cd "$E2E_TEMP_DIR" - mkdir not-a-project-dir - cd not-a-project-dir + cd "$E2E_TEMP_DIR" + mkdir not-a-project-dir + cd not-a-project-dir - assert_command dfx canister sign --query rwlgt-iiaaa-aaaaa-aaaaa-cai read --network ic - assert_match "Query message generated at \[message.json\]" + assert_command dfx canister sign --query rwlgt-iiaaa-aaaaa-aaaaa-cai read --network ic + assert_match "Query message generated at \[message.json\]" } @test "sign subcommand accepts argument from a file" { - install_asset greet - dfx_start - dfx deploy - TMP_NAME_FILE="$(mktemp)" - printf '("Names can be very long")' > "$TMP_NAME_FILE" + install_asset greet + dfx_start + dfx deploy + TMP_NAME_FILE="$(mktemp)" + printf '("Names can be very long")' > "$TMP_NAME_FILE" - assert_command dfx canister sign --argument-file "$TMP_NAME_FILE" --query hello_backend greet - assert_eq "Query message generated at [message.json]" + assert_command dfx canister sign --argument-file "$TMP_NAME_FILE" --query hello_backend greet + assert_eq "Query message generated at [message.json]" - assert_command jq -rc .arg message.json - assert_match "[68,73,68,76,0,1,113,21,78,97,109,101,115,32,99,97,110,32,98,101,32,118,114,121,32,108,111,110,103]" + assert_command jq -rc .arg message.json + assert_match "[68,73,68,76,0,1,113,21,78,97,109,101,115,32,99,97,110,32,98,101,32,118,114,121,32,108,111,110,103]" - rm "$TMP_NAME_FILE" + rm "$TMP_NAME_FILE" } @test "sign subcommand accepts argument from stdin" { - install_asset greet - dfx_start - dfx deploy - TMP_NAME_FILE="$(mktemp)" - printf '("stdin")' > "$TMP_NAME_FILE" + install_asset greet + dfx_start + dfx deploy + TMP_NAME_FILE="$(mktemp)" + printf '("stdin")' > "$TMP_NAME_FILE" - assert_command dfx canister sign --argument-file - --query hello_backend greet < "$TMP_NAME_FILE" - assert_eq "Query message generated at [message.json]" + assert_command dfx canister sign --argument-file - --query hello_backend greet < "$TMP_NAME_FILE" + assert_eq "Query message generated at [message.json]" - assert_command jq -rc .arg message.json - assert_match "[68,73,68,76,0,1,113,5,115,116,100,105,110]" + assert_command jq -rc .arg message.json + assert_match "[68,73,68,76,0,1,113,5,115,116,100,105,110]" - rm "$TMP_NAME_FILE" + rm "$TMP_NAME_FILE" } diff --git a/e2e/tests-dfx/signals.bash b/e2e/tests-dfx/signals.bash deleted file mode 100644 index 14514c1f64..0000000000 --- a/e2e/tests-dfx/signals.bash +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bats - -load ../utils/_ - -# All tests in this file are skipped for ic-ref. See scripts/workflows/e2e-matrix.py - - -setup() { - standard_setup - - dfx_new hello -} - -teardown() { - dfx_stop - - standard_teardown -} - -@test "dfx replica kills the replica upon SIGINT" { - dfx_replica_kills_replica SIGINT -} - -@test "dfx replica kills the replica upon SIGTERM" { - dfx_replica_kills_replica SIGTERM -} - -dfx_replica_kills_replica() { - skip "dfx replica and bootstrap are deprecated" - signal=$1 - - dfx_patchelf - dfx replica --port 0 & - DFX_PID=$! - - # wait for replica to start - assert_file_eventually_exists "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port" 15s - - kill -"$signal" "$DFX_PID" - - assert_process_exits $DFX_PID 15s - assert_no_dfx_start_or_replica_processes -} diff --git a/e2e/tests-dfx/start.bash b/e2e/tests-dfx/start.bash index 08c9915797..2ba61ab7b7 100644 --- a/e2e/tests-dfx/start.bash +++ b/e2e/tests-dfx/start.bash @@ -3,372 +3,372 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx restarts the replica" { - dfx_new hello - dfx_start - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' - - REPLICA_PID=$(get_replica_pid) - - echo "replica pid is $REPLICA_PID" - - kill -KILL "$REPLICA_PID" - assert_process_exits "$REPLICA_PID" 15s - - timeout 15s sh -c \ - 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ - || (echo "replica did not restart" && ps aux && exit 1) - wait_until_replica_healthy - - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - # even after the above, still sometimes fails with - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' + dfx_new hello + dfx_start + + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' + + REPLICA_PID=$(get_replica_pid) + + echo "replica pid is $REPLICA_PID" + + kill -KILL "$REPLICA_PID" + assert_process_exits "$REPLICA_PID" 15s + + timeout 15s sh -c \ + 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ + || (echo "replica did not restart" && ps aux && exit 1) + wait_until_replica_healthy + + # Sometimes initially get an error like: + # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module + # but the condition clears. + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting + # even after the above, still sometimes fails with + # IC0515: Certified state is not available yet. Please try again... + sleep 10 + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting + + assert_command dfx canister call hello_backend greet '("Omega")' + assert_eq '("Hello, Omega!")' } @test "dfx restarts icx-proxy" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' - ICX_PROXY_PID=$(get_icx_proxy_pid) + ICX_PROXY_PID=$(get_icx_proxy_pid) - echo "icx-proxy pid is $ICX_PROXY_PID" + echo "icx-proxy pid is $ICX_PROXY_PID" - kill -KILL "$ICX_PROXY_PID" - assert_process_exits "$ICX_PROXY_PID" 15s + kill -KILL "$ICX_PROXY_PID" + assert_process_exits "$ICX_PROXY_PID" 15s - ID=$(dfx canister id hello_frontend) + ID=$(dfx canister id hello_frontend) - timeout 15s sh -c \ - "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY\"/webserver-port)/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ - || (echo "icx-proxy did not restart" && ps aux && exit 1) + timeout 15s sh -c \ + "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY\"/webserver-port)/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ + || (echo "icx-proxy did not restart" && ps aux && exit 1) - assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" + assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" } @test "dfx restarts icx-proxy when the replica restarts" { - dfx_new hello - dfx_start - - install_asset greet - assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Alpha")' - assert_eq '("Hello, Alpha!")' - - REPLICA_PID=$(get_replica_pid) - ICX_PROXY_PID=$(get_icx_proxy_pid) - - echo "replica pid is $REPLICA_PID" - echo "icx-proxy pid is $ICX_PROXY_PID" - - kill -KILL "$REPLICA_PID" - assert_process_exits "$REPLICA_PID" 15s - assert_process_exits "$ICX_PROXY_PID" 15s - - timeout 15s sh -c \ - 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ - || (echo "replica did not restart" && ps aux && exit 1) - wait_until_replica_healthy - - # Sometimes initially get an error like: - # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module - # but the condition clears. - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - # even after the above, still sometimes fails with - # IC0515: Certified state is not available yet. Please try again... - sleep 10 - timeout 30s sh -c \ - "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ - || (echo "canister call did not succeed") # but continue, for better error reporting - - assert_command dfx canister call hello_backend greet '("Omega")' - assert_eq '("Hello, Omega!")' - - ID=$(dfx canister id hello_frontend) - - timeout 15s sh -c \ - "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ - || (echo "icx-proxy did not restart" && ps aux && exit 1) - - assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" + dfx_new hello + dfx_start + + install_asset greet + assert_command dfx deploy + assert_command dfx canister call hello_backend greet '("Alpha")' + assert_eq '("Hello, Alpha!")' + + REPLICA_PID=$(get_replica_pid) + ICX_PROXY_PID=$(get_icx_proxy_pid) + + echo "replica pid is $REPLICA_PID" + echo "icx-proxy pid is $ICX_PROXY_PID" + + kill -KILL "$REPLICA_PID" + assert_process_exits "$REPLICA_PID" 15s + assert_process_exits "$ICX_PROXY_PID" 15s + + timeout 15s sh -c \ + 'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \ + || (echo "replica did not restart" && ps aux && exit 1) + wait_until_replica_healthy + + # Sometimes initially get an error like: + # IC0304: Attempt to execute a message on canister <>> which contains no Wasm module + # but the condition clears. + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting + # even after the above, still sometimes fails with + # IC0515: Certified state is not available yet. Please try again... + sleep 10 + timeout 30s sh -c \ + "until dfx canister call hello_backend greet '(\"wait\")'; do echo waiting for any canister call to succeed; sleep 1; done" \ + || (echo "canister call did not succeed") # but continue, for better error reporting + + assert_command dfx canister call hello_backend greet '("Omega")' + assert_eq '("Hello, Omega!")' + + ID=$(dfx canister id hello_frontend) + + timeout 15s sh -c \ + "until curl --fail http://localhost:\$(cat \"$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/webserver-port\")/sample-asset.txt?canisterId=$ID; do echo waiting for icx-proxy to restart; sleep 1; done" \ + || (echo "icx-proxy did not restart" && ps aux && exit 1) + + assert_command curl --fail http://localhost:"$(get_webserver_port)"/sample-asset.txt?canisterId="$ID" } @test "dfx start honors replica port configuration" { - create_networks_json - replica_port=$(get_ephemeral_port) - jq ".local.replica.port=$replica_port" "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + create_networks_json + replica_port=$(get_ephemeral_port) + jq ".local.replica.port=$replica_port" "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - dfx_start + dfx_start - assert_command dfx info replica-port - assert_eq "$replica_port" + assert_command dfx info replica-port + assert_eq "$replica_port" } @test "dfx starts replica with subnet_type application - project defaults" { - install_asset subnet_type/project_defaults/application - define_project_network - jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_defaults/application + define_project_network + jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: Application" + assert_command dfx start --background + assert_match "subnet_type: Application" } @test "dfx starts replica with subnet_type verifiedapplication - project defaults" { - install_asset subnet_type/project_defaults/verified_application - define_project_network - jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_defaults/verified_application + define_project_network + jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: VerifiedApplication" + assert_command dfx start --background + assert_match "subnet_type: VerifiedApplication" } @test "dfx starts replica with subnet_type system - project defaults" { - install_asset subnet_type/project_defaults/system - define_project_network - jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_defaults/system + define_project_network + jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: System" + assert_command dfx start --background + assert_match "subnet_type: System" } @test "dfx starts replica with subnet_type application - local network" { - install_asset subnet_type/project_network_settings/application - define_project_network - jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_network_settings/application + define_project_network + jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: Application" + assert_command dfx start --background + assert_match "subnet_type: Application" } @test "dfx starts replica with subnet_type verifiedapplication - local network" { - install_asset subnet_type/project_network_settings/verified_application - define_project_network - jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_network_settings/verified_application + define_project_network + jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: VerifiedApplication" + assert_command dfx start --background + assert_match "subnet_type: VerifiedApplication" } @test "dfx starts replica with subnet_type system - local network" { - install_asset subnet_type/project_network_settings/system - define_project_network - jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json + install_asset subnet_type/project_network_settings/system + define_project_network + jq '.networks.local.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_match "subnet_type: System" + assert_command dfx start --background + assert_match "subnet_type: System" } @test "dfx starts replica with subnet_type application - shared network" { - install_shared_asset subnet_type/shared_network_settings/application - jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + install_shared_asset subnet_type/shared_network_settings/application + jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background - assert_match "subnet_type: Application" + assert_command dfx start --background + assert_match "subnet_type: Application" } @test "dfx starts replica with subnet_type verifiedapplication - shared network" { - install_shared_asset subnet_type/shared_network_settings/verified_application - jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + install_shared_asset subnet_type/shared_network_settings/verified_application + jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background - assert_match "subnet_type: VerifiedApplication" + assert_command dfx start --background + assert_match "subnet_type: VerifiedApplication" } @test "dfx starts replica with subnet_type system - shared network" { - install_shared_asset subnet_type/shared_network_settings/system - jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + install_shared_asset subnet_type/shared_network_settings/system + jq '.local.replica.log_level="info"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background - assert_match "subnet_type: System" + assert_command dfx start --background + assert_match "subnet_type: System" } @test "dfx start detects if dfx is already running - shared network" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - assert_command_fail dfx start - assert_match "dfx is already running" + assert_command_fail dfx start + assert_match "dfx is already running" } @test "dfx start for shared network warns about default settings specified in dfx.json that do not apply" { - dfx_new hello - - IGNORED_MESSAGE="Ignoring the 'defaults' field in dfx.json because project settings never apply to the shared network." - APPLY_SETTINGS_MESSAGE="To apply these settings to the shared network, define them in .*/config-root/.config/dfx/networks.json like so" - - jq 'del(.defaults)' dfx.json | sponge dfx.json - jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_contains "$IGNORED_MESSAGE" - assert_match "$APPLY_SETTINGS_MESSAGE" - assert_contains '"bitcoin": {' - assert_not_contains '"replica"' - assert_not_contains '"canister_http"' - assert_command dfx stop - - jq 'del(.defaults)' dfx.json | sponge dfx.json - jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_contains "$IGNORED_MESSAGE" - assert_match "$APPLY_SETTINGS_MESSAGE" - assert_not_contains '"bitcoin"' - assert_contains '"replica": {' - assert_not_contains '"canister_http"' - assert_command dfx stop - - jq 'del(.defaults)' dfx.json | sponge dfx.json - jq '.defaults.canister_http.enabled=false' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_contains "$IGNORED_MESSAGE" - assert_match "$APPLY_SETTINGS_MESSAGE" - assert_not_contains '"bitcoin"' - assert_not_contains '"replica"' - assert_contains '"canister_http": {' - assert_command dfx stop - - jq 'del(.defaults)' dfx.json | sponge dfx.json - jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json - jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json - jq '.defaults.canister_http.enabled=false' dfx.json | sponge dfx.json - assert_command dfx start --background - assert_contains "$IGNORED_MESSAGE" - assert_match "$APPLY_SETTINGS_MESSAGE" - assert_contains '"bitcoin": {' - assert_contains '"replica": {' - assert_contains '"canister_http": {' - assert_command dfx stop + dfx_new hello + + IGNORED_MESSAGE="Ignoring the 'defaults' field in dfx.json because project settings never apply to the shared network." + APPLY_SETTINGS_MESSAGE="To apply these settings to the shared network, define them in .*/config-root/.config/dfx/networks.json like so" + + jq 'del(.defaults)' dfx.json | sponge dfx.json + jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json + assert_command dfx start --background + assert_contains "$IGNORED_MESSAGE" + assert_match "$APPLY_SETTINGS_MESSAGE" + assert_contains '"bitcoin": {' + assert_not_contains '"replica"' + assert_not_contains '"canister_http"' + assert_command dfx stop + + jq 'del(.defaults)' dfx.json | sponge dfx.json + jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json + assert_command dfx start --background + assert_contains "$IGNORED_MESSAGE" + assert_match "$APPLY_SETTINGS_MESSAGE" + assert_not_contains '"bitcoin"' + assert_contains '"replica": {' + assert_not_contains '"canister_http"' + assert_command dfx stop + + jq 'del(.defaults)' dfx.json | sponge dfx.json + jq '.defaults.canister_http.enabled=false' dfx.json | sponge dfx.json + assert_command dfx start --background + assert_contains "$IGNORED_MESSAGE" + assert_match "$APPLY_SETTINGS_MESSAGE" + assert_not_contains '"bitcoin"' + assert_not_contains '"replica"' + assert_contains '"canister_http": {' + assert_command dfx stop + + jq 'del(.defaults)' dfx.json | sponge dfx.json + jq '.defaults.bitcoin.enabled=true' dfx.json | sponge dfx.json + jq '.defaults.replica.log_level="info"' dfx.json | sponge dfx.json + jq '.defaults.canister_http.enabled=false' dfx.json | sponge dfx.json + assert_command dfx start --background + assert_contains "$IGNORED_MESSAGE" + assert_match "$APPLY_SETTINGS_MESSAGE" + assert_contains '"bitcoin": {' + assert_contains '"replica": {' + assert_contains '"canister_http": {' + assert_command dfx stop } @test "dfx starts replica with correct log level - project defaults" { - dfx_new - jq '.defaults.replica.log_level="warning"' dfx.json | sponge dfx.json - define_project_network + dfx_new + jq '.defaults.replica.log_level="warning"' dfx.json | sponge dfx.json + define_project_network - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.defaults.replica.log_level="critical"' dfx.json | sponge dfx.json - assert_command dfx start --background --verbose --clean - assert_match "log level: Critical" + jq '.defaults.replica.log_level="critical"' dfx.json | sponge dfx.json + assert_command dfx start --background --verbose --clean + assert_match "log level: Critical" } @test "dfx starts replica with correct log level - local network" { - dfx_new - jq '.networks.local.replica.log_level="warning"' dfx.json | sponge dfx.json - define_project_network + dfx_new + jq '.networks.local.replica.log_level="warning"' dfx.json | sponge dfx.json + define_project_network - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.networks.local.replica.log_level="critical"' dfx.json | sponge dfx.json - assert_command dfx start --background --verbose --clean - assert_match "log level: Critical" + jq '.networks.local.replica.log_level="critical"' dfx.json | sponge dfx.json + assert_command dfx start --background --verbose --clean + assert_match "log level: Critical" } @test "dfx starts replica with correct log level - shared network" { - dfx_new - create_networks_json - jq '.local.replica.log_level="warning"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + dfx_new + create_networks_json + jq '.local.replica.log_level="warning"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose - assert_match "log level: Warning" - assert_command dfx stop + assert_command dfx start --background --verbose + assert_match "log level: Warning" + assert_command dfx stop - jq '.local.replica.log_level="critical"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" - assert_command dfx start --background --verbose --clean - assert_match "log level: Critical" + jq '.local.replica.log_level="critical"' "$E2E_NETWORKS_JSON" | sponge "$E2E_NETWORKS_JSON" + assert_command dfx start --background --verbose --clean + assert_match "log level: Critical" } @test "debug print statements work with default log level" { - dfx_new - install_asset print - dfx_start 2>stderr.txt - assert_command dfx deploy - assert_command dfx canister call e2e_project hello - sleep 2 - run tail -2 stderr.txt - assert_match "Hello, World! from DFINITY" + dfx_new + install_asset print + dfx_start 2>stderr.txt + assert_command dfx deploy + assert_command dfx canister call e2e_project hello + sleep 2 + run tail -2 stderr.txt + assert_match "Hello, World! from DFINITY" } @test "modifying networks.json requires --clean on restart" { - dfx_start - dfx stop - assert_command dfx_start - dfx stop - jq -n '.local.replica.log_level="warning"' > "$E2E_NETWORKS_JSON" - assert_command_fail dfx_start - assert_contains "The network configuration was changed. Rerun with \`--clean\`." - assert_command dfx_start --force - dfx stop - assert_command dfx_start --clean + dfx_start + dfx stop + assert_command dfx_start + dfx stop + jq -n '.local.replica.log_level="warning"' > "$E2E_NETWORKS_JSON" + assert_command_fail dfx_start + assert_contains "The network configuration was changed. Rerun with \`--clean\`." + assert_command dfx_start --force + dfx stop + assert_command dfx_start --clean } @test "project-local networks require --clean if dfx.json was updated" { - dfx_new - define_project_network - dfx_start - dfx stop - assert_command dfx_start - dfx stop - jq -n '.local.replica.log_level="warning"' > "$E2E_NETWORKS_JSON" - assert_command dfx_start - dfx stop - jq '.networks.local.replica.log_level="warning"' dfx.json | sponge dfx.json - assert_command_fail dfx_start - assert_contains "The network configuration was changed. Rerun with \`--clean\`." - assert_command dfx_start --force - dfx stop - assert_command dfx_start --clean + dfx_new + define_project_network + dfx_start + dfx stop + assert_command dfx_start + dfx stop + jq -n '.local.replica.log_level="warning"' > "$E2E_NETWORKS_JSON" + assert_command dfx_start + dfx stop + jq '.networks.local.replica.log_level="warning"' dfx.json | sponge dfx.json + assert_command_fail dfx_start + assert_contains "The network configuration was changed. Rerun with \`--clean\`." + assert_command dfx_start --force + dfx stop + assert_command dfx_start --clean } @test "flags count as configuration modification and require --clean" { - dfx_start - dfx stop - assert_command_fail dfx_start --enable-bitcoin - assert_contains "The network configuration was changed. Rerun with \`--clean\`." - assert_command dfx_start --enable-bitcoin --clean - dfx stop - assert_command dfx_start --enable-bitcoin - dfx stop - assert_command_fail dfx_start - assert_contains "The network configuration was changed. Rerun with \`--clean\`." - assert_command dfx_start --force + dfx_start + dfx stop + assert_command_fail dfx_start --enable-bitcoin + assert_contains "The network configuration was changed. Rerun with \`--clean\`." + assert_command dfx_start --enable-bitcoin --clean + dfx stop + assert_command dfx_start --enable-bitcoin + dfx stop + assert_command_fail dfx_start + assert_contains "The network configuration was changed. Rerun with \`--clean\`." + assert_command dfx_start --force } @test "dfx start then ctrl-c won't hang and panic but stop actors quickly" { - assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/ctrl_c_right_after_dfx_start.exp" + assert_command "${BATS_TEST_DIRNAME}/../assets/expect_scripts/ctrl_c_right_after_dfx_start.exp" } diff --git a/e2e/tests-dfx/subcommands.bash b/e2e/tests-dfx/subcommands.bash index 92e6f4c65a..9591922c1d 100644 --- a/e2e/tests-dfx/subcommands.bash +++ b/e2e/tests-dfx/subcommands.bash @@ -3,23 +3,23 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new + dfx_new } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "--identity and --network are stil accepted as prefix" { - install_asset whoami - dfx_start - dfx deploy - dfx identity new alice --storage-mode plaintext - assert_command dfx --identity alice canister --network local call whoami whoami - assert_match "$(dfx --identity alice identity get-principal)" - assert_match "$(dfx identity get-principal --identity alice)" + install_asset whoami + dfx_start + dfx deploy + dfx identity new alice --storage-mode plaintext + assert_command dfx --identity alice canister --network local call whoami whoami + assert_match "$(dfx --identity alice identity get-principal)" + assert_match "$(dfx identity get-principal --identity alice)" } diff --git a/e2e/tests-dfx/update_settings.bash b/e2e/tests-dfx/update_settings.bash index ff34082756..54f529948d 100644 --- a/e2e/tests-dfx/update_settings.bash +++ b/e2e/tests-dfx/update_settings.bash @@ -3,491 +3,502 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } -@test "set freezing threshold" { +@test "set reserved cycles limit" { dfx_start assert_command dfx deploy hello_backend + assert_command dfx canister status hello_backend + assert_contains "Reserved Cycles Limit: 5_000_000_000_000 Cycles" + + assert_command dfx canister update-settings hello_backend --reserved-cycles-limit 650000 + assert_command dfx canister status hello_backend + assert_contains "Reserved Cycles Limit: 650_000 Cycles" +} - # trying to set threshold to 1T seconds, which should not work because it's likely a mistake - assert_command_fail dfx canister update-settings hello_backend --freezing-threshold 100000000000 - assert_match "SECONDS" # error message pointing to the error +@test "set freezing threshold" { + dfx_start + assert_command dfx deploy hello_backend - # with manual override it's ok - assert_command dfx canister update-settings hello_backend --freezing-threshold 100000000000 --confirm-very-long-freezing-threshold + # trying to set threshold to 1T seconds, which should not work because it's likely a mistake + assert_command_fail dfx canister update-settings hello_backend --freezing-threshold 100000000000 + assert_match "SECONDS" # error message pointing to the error - # to check if threshold is set correctly we have to un-freeze the canister by adding cycles. Fabricating 100T cycles onto it - assert_command dfx ledger fabricate-cycles --canister hello_backend --t 100 - assert_command dfx canister status hello_backend - assert_match "Freezing threshold: 100_000_000_000" + # with manual override it's ok + assert_command dfx canister update-settings hello_backend --freezing-threshold 100000000000 --confirm-very-long-freezing-threshold + + # to check if threshold is set correctly we have to un-freeze the canister by adding cycles. Fabricating 100T cycles onto it + assert_command dfx ledger fabricate-cycles --canister hello_backend --t 100 + assert_command dfx canister status hello_backend + assert_match "Freezing threshold: 100_000_000_000" } @test "set controller" { - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity use alice - - dfx_start - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + assert_command dfx identity use alice + + dfx_start + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - dfx canister create hello_backend - dfx build hello_backend - dfx canister install hello_backend - ID=$(dfx canister id hello_backend) + dfx canister create hello_backend + dfx build hello_backend + dfx canister install hello_backend + ID=$(dfx canister id hello_backend) - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller bob --yes - assert_match "Set controller of \"hello_backend\" to: bob" + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller bob --yes + assert_match "Set controller of \"hello_backend\" to: bob" - # Bob is controller, Alice cannot reinstall - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall + # Bob is controller, Alice cannot reinstall + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall - # Bob can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob + # Bob can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob - assert_command dfx identity use bob - # Set controller using canister id and principal - assert_command dfx canister update-settings "$ID" --set-controller "${ALICE_PRINCIPAL}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_PRINCIPAL}" - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall + assert_command dfx identity use bob + # Set controller using canister id and principal + assert_command dfx canister update-settings "$ID" --set-controller "${ALICE_PRINCIPAL}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_PRINCIPAL}" + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall - # Set controller using combination of name/id and identity/principal - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_PRINCIPAL}" --identity alice --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_PRINCIPAL}" + # Set controller using combination of name/id and identity/principal + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_PRINCIPAL}" --identity alice --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_PRINCIPAL}" - assert_command dfx canister update-settings "${ID}" --set-controller alice --identity bob --yes - assert_match "Set controller of \"${ID}\" to: alice" + assert_command dfx canister update-settings "${ID}" --set-controller alice --identity bob --yes + assert_match "Set controller of \"${ID}\" to: alice" - # Set controller using invalid principal/identity fails - assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes - assert_match "Identity charlie does not exist" + # Set controller using invalid principal/identity fails + assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes + assert_match "Identity charlie does not exist" - # Set controller using invalid canister name/id fails - assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes - assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." + # Set controller using invalid canister name/id fails + assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes + assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." - # Fails if no consent is given - echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice - # But works with typing "yes" - echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice + # Fails if no consent is given + echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice + # But works with typing "yes" + echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice } @test "set controller with wallet" { - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity use alice + assert_command dfx identity use alice - dfx_start - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB_WALLET=$(dfx identity get-wallet --identity bob) + dfx_start + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB_WALLET=$(dfx identity get-wallet --identity bob) - dfx canister create hello_backend --wallet "${ALICE_WALLET}" - dfx build hello_backend - dfx canister install hello_backend --wallet "${ALICE_WALLET}" - ID=$(dfx canister id hello_backend) + dfx canister create hello_backend --wallet "${ALICE_WALLET}" + dfx build hello_backend + dfx canister install hello_backend --wallet "${ALICE_WALLET}" + ID=$(dfx canister id hello_backend) - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" - # Bob is controller, Alice cannot reinstall - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${ALICE_WALLET}" + # Bob is controller, Alice cannot reinstall + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${ALICE_WALLET}" - # Bob can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" + # Bob can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" - assert_command dfx identity use bob - # Set controller using canister id and principal - assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --wallet "${BOB_WALLET}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${BOB_WALLET}" + assert_command dfx identity use bob + # Set controller using canister id and principal + assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --wallet "${BOB_WALLET}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${BOB_WALLET}" - # Set controller using combination of name/id and identity/principal - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" + # Set controller using combination of name/id and identity/principal + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" - assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --identity bob --wallet "${BOB_WALLET}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" + assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --identity bob --wallet "${BOB_WALLET}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" - # Set controller using invalid principal/identity fails - assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --wallet "${ALICE_WALLET}" --yes - assert_match "Identity charlie does not exist" + # Set controller using invalid principal/identity fails + assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --wallet "${ALICE_WALLET}" --yes + assert_match "Identity charlie does not exist" - # Set controller using invalid canister name/id fails - assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --wallet "${ALICE_WALLET}" --yes - assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." + # Set controller using invalid canister name/id fails + assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --wallet "${ALICE_WALLET}" --yes + assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." - # Fails if no consent is given - echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" - # But works with typing "yes" - echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" + # Fails if no consent is given + echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" + # But works with typing "yes" + echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" } @test "set controller with wallet 0.7.2" { - use_wallet_wasm 0.7.2 + use_wallet_wasm 0.7.2 - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity use alice + assert_command dfx identity use alice - dfx_start - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB_WALLET=$(dfx identity get-wallet --identity bob) + dfx_start + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB_WALLET=$(dfx identity get-wallet --identity bob) - dfx canister create hello_backend --wallet "${ALICE_WALLET}" - dfx build hello_backend - dfx canister install hello_backend --wallet "${ALICE_WALLET}" - ID=$(dfx canister id hello_backend) + dfx canister create hello_backend --wallet "${ALICE_WALLET}" + dfx build hello_backend + dfx canister install hello_backend --wallet "${ALICE_WALLET}" + ID=$(dfx canister id hello_backend) - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" - # Bob is controller, Alice cannot reinstall - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${ALICE_WALLET}" + # Bob is controller, Alice cannot reinstall + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${ALICE_WALLET}" - # Bob can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" + # Bob can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" - assert_command dfx identity use bob - # Set controller using canister id and principal - assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --wallet "${BOB_WALLET}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${BOB_WALLET}" + assert_command dfx identity use bob + # Set controller using canister id and principal + assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --wallet "${BOB_WALLET}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall --wallet "${BOB_WALLET}" - # Set controller using combination of name/id and identity/principal - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" + # Set controller using combination of name/id and identity/principal + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_WALLET}" - assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --identity bob --wallet "${BOB_WALLET}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" + assert_command dfx canister update-settings "${ID}" --set-controller "${ALICE_WALLET}" --identity bob --wallet "${BOB_WALLET}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_WALLET}" - # Set controller using invalid principal/identity fails - assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes - assert_match "Identity charlie does not exist" + # Set controller using invalid principal/identity fails + assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes + assert_match "Identity charlie does not exist" - # Set controller using invalid canister name/id fails - assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes - assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." + # Set controller using invalid canister name/id fails + assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes + assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." - # Fails if no consent is given - echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" - # But works with typing "yes" - echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" + # Fails if no consent is given + echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" + # But works with typing "yes" + echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_WALLET}" --identity alice --wallet "${ALICE_WALLET}" } @test "set controller without wallet but using wallet 0.7.2" { - use_wallet_wasm 0.7.2 - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob - - assert_command dfx identity use alice - - dfx_start - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - - dfx canister create hello_backend - dfx canister update-settings hello_backend --add-controller "$ALICE_PRINCIPAL" --wallet "$(dfx identity get-wallet)" - dfx build hello_backend - dfx canister install hello_backend - ID=$(dfx canister id hello_backend) - - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller bob --yes - assert_match "Set controller of \"hello_backend\" to: bob" - - # Bob is controller, Alice cannot reinstall - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall - - # Bob can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob - - assert_command dfx identity use bob - # Set controller using canister id and principal - assert_command dfx canister update-settings "$ID" --set-controller "${ALICE_PRINCIPAL}" --yes - assert_match "Set controller of \"${ID}\" to: ${ALICE_PRINCIPAL}" - echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall - - # Set controller using combination of name/id and identity/principal - assert_command dfx canister update-settings hello_backend --set-controller "${BOB_PRINCIPAL}" --identity alice --yes - assert_match "Set controller of \"hello_backend\" to: ${BOB_PRINCIPAL}" - - assert_command dfx canister update-settings "${ID}" --set-controller alice --identity bob --yes - assert_match "Set controller of \"${ID}\" to: alice" - - # Set controller using invalid principal/identity fails - assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes - assert_match "Identity charlie does not exist" - - # Set controller using invalid canister name/id fails - assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes - assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." - - # Fails if no consent is given - echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice - # But works with typing "yes" - echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice + use_wallet_wasm 0.7.2 + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + + assert_command dfx identity use alice + + dfx_start + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + + dfx canister create hello_backend + dfx canister update-settings hello_backend --add-controller "$ALICE_PRINCIPAL" --wallet "$(dfx identity get-wallet)" + dfx build hello_backend + dfx canister install hello_backend + ID=$(dfx canister id hello_backend) + + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller bob --yes + assert_match "Set controller of \"hello_backend\" to: bob" + + # Bob is controller, Alice cannot reinstall + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall + + # Bob can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob + + assert_command dfx identity use bob + # Set controller using canister id and principal + assert_command dfx canister update-settings "$ID" --set-controller "${ALICE_PRINCIPAL}" --yes + assert_match "Set controller of \"${ID}\" to: ${ALICE_PRINCIPAL}" + echo "yes" | assert_command_fail dfx canister install hello_backend -m reinstall + + # Set controller using combination of name/id and identity/principal + assert_command dfx canister update-settings hello_backend --set-controller "${BOB_PRINCIPAL}" --identity alice --yes + assert_match "Set controller of \"hello_backend\" to: ${BOB_PRINCIPAL}" + + assert_command dfx canister update-settings "${ID}" --set-controller alice --identity bob --yes + assert_match "Set controller of \"${ID}\" to: alice" + + # Set controller using invalid principal/identity fails + assert_command_fail dfx canister update-settings hello_backend --set-controller charlie --identity alice --yes + assert_match "Identity charlie does not exist" + + # Set controller using invalid canister name/id fails + assert_command_fail dfx canister update-settings hello_assets --set-controller bob --identity alice --yes + assert_match "Cannot find canister id. Please issue 'dfx canister create hello_assets'." + + # Fails if no consent is given + echo "no" | assert_command_fail dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice + # But works with typing "yes" + echo "yes" | assert_command dfx canister update-settings "${ID}" --set-controller "${BOB_PRINCIPAL}" --identity alice } @test "set multiple controllers" { - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity use alice + assert_command dfx identity use alice - dfx_start - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - # awk step is to avoid trailing space - PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) - - dfx canister create hello_backend - dfx build hello_backend - dfx canister install hello_backend - ID=$(dfx canister id hello_backend) - - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_PRINCIPAL}" --set-controller "${BOB_PRINCIPAL}" - assert_match "Set controllers of \"hello_backend\" to: $PRINCIPALS_SORTED" - - # Both can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob - - assert_command dfx canister info hello_backend - assert_match "Controllers: ${PRINCIPALS_SORTED}" -} + dfx_start + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + # awk step is to avoid trailing space + PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) -@test "set multiple controllers with wallet" { - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + dfx canister create hello_backend + dfx build hello_backend + dfx canister install hello_backend + ID=$(dfx canister id hello_backend) - assert_command dfx identity use alice + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_PRINCIPAL}" --set-controller "${BOB_PRINCIPAL}" + assert_match "Set controllers of \"hello_backend\" to: $PRINCIPALS_SORTED" - dfx_start - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB_WALLET=$(dfx identity get-wallet --identity bob) - # awk step is to avoid trailing space - WALLETS_SORTED=$(echo "${ALICE_WALLET}" "${BOB_WALLET}" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) - - dfx canister create hello_backend - dfx build hello_backend - dfx canister install hello_backend - ID=$(dfx canister id hello_backend) - - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_WALLET}" --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" - assert_match "Set controllers of \"hello_backend\" to: ${WALLETS_SORTED}" - - # Both can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice --wallet "${ALICE_WALLET}" - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" - - assert_command dfx canister info hello_backend - assert_match "Controllers: ${WALLETS_SORTED}" + # Both can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob + + assert_command dfx canister info hello_backend + assert_match "Controllers: ${PRINCIPALS_SORTED}" } -@test "set multiple controllers even with wallet 0.7.2" { - use_wallet_wasm 0.7.2 - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob +@test "set multiple controllers with wallet" { + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity use alice + assert_command dfx identity use alice - dfx_start - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB_WALLET=$(dfx identity get-wallet --identity bob) - # awk step is to avoid trailing space - WALLETS_SORTED=$(echo "${ALICE_WALLET}" "${BOB_WALLET}" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) - - dfx canister create hello_backend - dfx build hello_backend - dfx canister install hello_backend --wallet "${ALICE_WALLET}" - ID=$(dfx canister id hello_backend) - - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_WALLET}" --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" - assert_match "Set controllers of \"hello_backend\" to: $WALLETS_SORTED" - - # Both can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice --wallet "${ALICE_WALLET}" - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" - - assert_command dfx canister info hello_backend - assert_match "Controllers: ${WALLETS_SORTED}" -} + dfx_start + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB_WALLET=$(dfx identity get-wallet --identity bob) + # awk step is to avoid trailing space + WALLETS_SORTED=$(echo "${ALICE_WALLET}" "${BOB_WALLET}" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) -@test "set multiple controllers without wallet but using wallet 0.7.2" { - use_wallet_wasm 0.7.2 - # Create two identities - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob + dfx canister create hello_backend + dfx build hello_backend + dfx canister install hello_backend + ID=$(dfx canister id hello_backend) - assert_command dfx identity use alice + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_WALLET}" --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" + assert_match "Set controllers of \"hello_backend\" to: ${WALLETS_SORTED}" - dfx_start - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - # awk step is to avoid trailing space - PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) - - dfx canister create hello_backend - dfx canister update-settings hello_backend --add-controller "$ALICE_PRINCIPAL" --wallet "$(dfx identity get-wallet)" - dfx build hello_backend - dfx canister install hello_backend - ID=$(dfx canister id hello_backend) - - # Set controller using canister name and identity name - assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_PRINCIPAL}" --set-controller "${BOB_PRINCIPAL}" - assert_match "Set controllers of \"hello_backend\" to: $PRINCIPALS_SORTED" - - # Both can reinstall - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice - echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob - - assert_command dfx canister info hello_backend - assert_match "Controllers: ${PRINCIPALS_SORTED}" + # Both can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice --wallet "${ALICE_WALLET}" + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" + + assert_command dfx canister info hello_backend + assert_match "Controllers: ${WALLETS_SORTED}" } -@test "add controller to existing canister" { - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity new --storage-mode plaintext charlie +@test "set multiple controllers even with wallet 0.7.2" { + use_wallet_wasm 0.7.2 + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + + assert_command dfx identity use alice + + dfx_start + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB_WALLET=$(dfx identity get-wallet --identity bob) + # awk step is to avoid trailing space + WALLETS_SORTED=$(echo "${ALICE_WALLET}" "${BOB_WALLET}" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) + + dfx canister create hello_backend + dfx build hello_backend + dfx canister install hello_backend --wallet "${ALICE_WALLET}" + ID=$(dfx canister id hello_backend) + + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_WALLET}" --set-controller "${BOB_WALLET}" --wallet "${ALICE_WALLET}" + assert_match "Set controllers of \"hello_backend\" to: $WALLETS_SORTED" + + # Both can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice --wallet "${ALICE_WALLET}" + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob --wallet "${BOB_WALLET}" + + assert_command dfx canister info hello_backend + assert_match "Controllers: ${WALLETS_SORTED}" +} - dfx identity use alice - dfx_start +@test "set multiple controllers without wallet but using wallet 0.7.2" { + use_wallet_wasm 0.7.2 + # Create two identities + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + + assert_command dfx identity use alice + + dfx_start + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + # awk step is to avoid trailing space + PRINCIPALS_SORTED=$(echo "$ALICE_PRINCIPAL" "$BOB_PRINCIPAL" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s",$1,$2}' ) + + dfx canister create hello_backend + dfx canister update-settings hello_backend --add-controller "$ALICE_PRINCIPAL" --wallet "$(dfx identity get-wallet)" + dfx build hello_backend + dfx canister install hello_backend + ID=$(dfx canister id hello_backend) + + # Set controller using canister name and identity name + assert_command dfx canister update-settings hello_backend --set-controller "${ALICE_PRINCIPAL}" --set-controller "${BOB_PRINCIPAL}" + assert_match "Set controllers of \"hello_backend\" to: $PRINCIPALS_SORTED" + + # Both can reinstall + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity alice + echo "yes" | assert_command dfx canister install hello_backend -m reinstall --identity bob + + assert_command dfx canister info hello_backend + assert_match "Controllers: ${PRINCIPALS_SORTED}" +} - ALICE=$(dfx identity get-principal --identity alice) - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB=$(dfx identity get-principal --identity bob) - CHARLIE=$(dfx identity get-principal --identity charlie) - SORTED=$(echo "$ALICE" "$ALICE_WALLET" "$BOB" "$CHARLIE" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s %s %s",$1,$2,$3,$4}' ) - - dfx canister create hello_backend - dfx build hello_backend - dfx canister install hello_backend - - # make bob a controller - assert_command dfx canister update-settings hello_backend --add-controller bob - # check that bob has the authority to make someone else a controller - assert_command dfx canister update-settings hello_backend --add-controller charlie --identity bob - assert_command dfx canister info hello_backend - assert_match "Controllers: $SORTED" +@test "add controller to existing canister" { + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + assert_command dfx identity new --storage-mode plaintext charlie + + dfx identity use alice + dfx_start + + ALICE=$(dfx identity get-principal --identity alice) + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB=$(dfx identity get-principal --identity bob) + CHARLIE=$(dfx identity get-principal --identity charlie) + SORTED=$(echo "$ALICE" "$ALICE_WALLET" "$BOB" "$CHARLIE" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s %s %s",$1,$2,$3,$4}' ) + + dfx canister create hello_backend + dfx build hello_backend + dfx canister install hello_backend + + # make bob a controller + assert_command dfx canister update-settings hello_backend --add-controller bob + # check that bob has the authority to make someone else a controller + assert_command dfx canister update-settings hello_backend --add-controller charlie --identity bob + assert_command dfx canister info hello_backend + assert_match "Controllers: $SORTED" } @test "add controller to all canisters" { - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob - assert_command dfx identity new --storage-mode plaintext charlie - - dfx identity use alice - dfx_start - - ALICE=$(dfx identity get-principal --identity alice) - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB=$(dfx identity get-principal --identity bob) - CHARLIE=$(dfx identity get-principal --identity charlie) - SORTED=$(echo "$ALICE" "$ALICE_WALLET" "$BOB" "$CHARLIE" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s %s %s",$1,$2,$3,$4}' ) - - dfx canister create --all - dfx build --all - dfx canister install --all - - # make bob a controller - assert_command dfx canister update-settings --all --add-controller bob - # check that bob has the authority to make someone else a controller - assert_command dfx canister update-settings --all --add-controller charlie --identity bob - assert_command dfx canister info hello_backend - assert_match "Controllers: $SORTED" + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + assert_command dfx identity new --storage-mode plaintext charlie + + dfx identity use alice + dfx_start + + ALICE=$(dfx identity get-principal --identity alice) + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB=$(dfx identity get-principal --identity bob) + CHARLIE=$(dfx identity get-principal --identity charlie) + SORTED=$(echo "$ALICE" "$ALICE_WALLET" "$BOB" "$CHARLIE" | tr " " "\n" | sort | tr "\n" " " | awk '{printf "%s %s %s %s",$1,$2,$3,$4}' ) + + dfx canister create --all + dfx build --all + dfx canister install --all + + # make bob a controller + assert_command dfx canister update-settings --all --add-controller bob + # check that bob has the authority to make someone else a controller + assert_command dfx canister update-settings --all --add-controller charlie --identity bob + assert_command dfx canister info hello_backend + assert_match "Controllers: $SORTED" } @test "update settings by canister id, when canister id is not known to the project" { - dfx_start - dfx deploy + dfx_start + dfx deploy - CANISTER_ID=$(dfx canister id hello_backend) + CANISTER_ID=$(dfx canister id hello_backend) - rm .dfx/local/canister_ids.json - jq '.canisters={}' dfx.json | sponge dfx.json + rm .dfx/local/canister_ids.json + jq '.canisters={}' dfx.json | sponge dfx.json - assert_command dfx canister status "$CANISTER_ID" - assert_match 'Memory allocation: 0' - assert_match 'Compute allocation: 0' - assert_match 'Freezing threshold: 2_592_000' + assert_command dfx canister status "$CANISTER_ID" + assert_match 'Memory allocation: 0' + assert_match 'Compute allocation: 0' + assert_match 'Freezing threshold: 2_592_000' - dfx canister update-settings --memory-allocation 2GB "$CANISTER_ID" - assert_command dfx canister status "$CANISTER_ID" - assert_match 'Memory allocation: 2_000_000_000' - assert_match 'Compute allocation: 0' - assert_match 'Freezing threshold: 2_592_000' + dfx canister update-settings --memory-allocation 2GB "$CANISTER_ID" + assert_command dfx canister status "$CANISTER_ID" + assert_match 'Memory allocation: 2_000_000_000' + assert_match 'Compute allocation: 0' + assert_match 'Freezing threshold: 2_592_000' - # This is just checking that update-settings leaves the previous value - # (of memory allocation) alone when setting something else + # This is just checking that update-settings leaves the previous value + # (of memory allocation) alone when setting something else - # Compute allocations are temporarily disabled. - # See https://dfinity.atlassian.net/browse/RUN-314 - # dfx canister update-settings --compute-allocation 1 "$CANISTER_ID" + # Compute allocations are temporarily disabled. + # See https://dfinity.atlassian.net/browse/RUN-314 + # dfx canister update-settings --compute-allocation 1 "$CANISTER_ID" - dfx canister update-settings --freezing-threshold 172 "$CANISTER_ID" - assert_command dfx canister status "$CANISTER_ID" - assert_match 'Memory allocation: 2_000_000_000' - # assert_match 'Compute allocation: 4' - assert_match 'Freezing threshold: 172' + dfx canister update-settings --freezing-threshold 172 "$CANISTER_ID" + assert_command dfx canister status "$CANISTER_ID" + assert_match 'Memory allocation: 2_000_000_000' + # assert_match 'Compute allocation: 4' + assert_match 'Freezing threshold: 172' } @test "remove controller" { - assert_command dfx identity new --storage-mode plaintext alice - assert_command dfx identity new --storage-mode plaintext bob - ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) - BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) - dfx identity use alice - - dfx_start - dfx deploy - WALLET_PRINCIPAL=$(dfx identity get-wallet) - - assert_command dfx canister update-settings hello_backend --add-controller "${BOB_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_contains "${BOB_PRINCIPAL}" - assert_command dfx canister update-settings hello_backend --remove-controller "${BOB_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_not_contains "${BOB_PRINCIPAL}" - - # Cannot remove own controller without extra consent - echo "no" | assert_command_fail dfx canister update-settings hello_backend --remove-controller "${ALICE_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_contains "${ALICE_PRINCIPAL}" - echo "yes" | assert_command dfx canister update-settings hello_backend --remove-controller "${ALICE_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_not_contains "${ALICE_PRINCIPAL}" - - # Cannot remove wallet controller without extra consent - echo "no" | assert_command_fail dfx canister update-settings hello_backend --remove-controller "${WALLET_PRINCIPAL}" --wallet "${WALLET_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_contains "${WALLET_PRINCIPAL}" - echo "yes" | assert_command dfx canister update-settings hello_backend --remove-controller "${WALLET_PRINCIPAL}" --wallet "${WALLET_PRINCIPAL}" - assert_command dfx canister info hello_backend - assert_not_contains "${WALLET_PRINCIPAL}" + assert_command dfx identity new --storage-mode plaintext alice + assert_command dfx identity new --storage-mode plaintext bob + ALICE_PRINCIPAL=$(dfx identity get-principal --identity alice) + BOB_PRINCIPAL=$(dfx identity get-principal --identity bob) + dfx identity use alice + + dfx_start + dfx deploy + WALLET_PRINCIPAL=$(dfx identity get-wallet) + + assert_command dfx canister update-settings hello_backend --add-controller "${BOB_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_contains "${BOB_PRINCIPAL}" + assert_command dfx canister update-settings hello_backend --remove-controller "${BOB_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_not_contains "${BOB_PRINCIPAL}" + + # Cannot remove own controller without extra consent + echo "no" | assert_command_fail dfx canister update-settings hello_backend --remove-controller "${ALICE_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_contains "${ALICE_PRINCIPAL}" + echo "yes" | assert_command dfx canister update-settings hello_backend --remove-controller "${ALICE_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_not_contains "${ALICE_PRINCIPAL}" + + # Cannot remove wallet controller without extra consent + echo "no" | assert_command_fail dfx canister update-settings hello_backend --remove-controller "${WALLET_PRINCIPAL}" --wallet "${WALLET_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_contains "${WALLET_PRINCIPAL}" + echo "yes" | assert_command dfx canister update-settings hello_backend --remove-controller "${WALLET_PRINCIPAL}" --wallet "${WALLET_PRINCIPAL}" + assert_command dfx canister info hello_backend + assert_not_contains "${WALLET_PRINCIPAL}" } diff --git a/e2e/tests-dfx/upgrade.bash b/e2e/tests-dfx/upgrade.bash index 4bb884bfa1..02bf5a6d5d 100644 --- a/e2e/tests-dfx/upgrade.bash +++ b/e2e/tests-dfx/upgrade.bash @@ -3,47 +3,47 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - stop_webserver - standard_teardown + stop_webserver + standard_teardown } @test "upgrade succeeds" { - latest_version="0.4.7" - latest_version_dir="downloads/dfx/$latest_version/x86_64-$(uname -s | tr '[:upper:]' '[:lower:]')/" - dfx_archive_file_name="dfx-$latest_version.tar.gz" - mkdir -p "$latest_version_dir" - cp "$(which dfx)" . - version=$(./dfx --version) - tar -czf "$latest_version_dir/$dfx_archive_file_name" dfx - echo '{ - "tags": { - "latest": "0.4.7" - }, - "versions": [ - "0.4.3", - "0.4.4", - "0.4.7" - ] - }' > manifest.json - - start_webserver - - # Override current version to force upgrade - assert_command ./dfx upgrade \ - --current-version 0.4.6 \ - --release-root "http://localhost:$E2E_WEB_SERVER_PORT" - assert_match "Current version: .*" - assert_match "Fetching manifest .*" - assert_match "New version available: .*" - - assert_command ./dfx upgrade \ - --release-root "http://localhost:$E2E_WEB_SERVER_PORT" - assert_match "Already up to date" - - assert_command ./dfx --version - assert_contains "$version" + latest_version="0.4.7" + latest_version_dir="downloads/dfx/$latest_version/x86_64-$(uname -s | tr '[:upper:]' '[:lower:]')/" + dfx_archive_file_name="dfx-$latest_version.tar.gz" + mkdir -p "$latest_version_dir" + cp "$(which dfx)" . + version=$(./dfx --version) + tar -czf "$latest_version_dir/$dfx_archive_file_name" dfx + echo '{ + "tags": { + "latest": "0.4.7" + }, + "versions": [ + "0.4.3", + "0.4.4", + "0.4.7" + ] + }' > manifest.json + + start_webserver + + # Override current version to force upgrade + assert_command ./dfx upgrade \ + --current-version 0.4.6 \ + --release-root "http://localhost:$E2E_WEB_SERVER_PORT" + assert_match "Current version: .*" + assert_match "Fetching manifest .*" + assert_match "New version available: .*" + + assert_command ./dfx upgrade \ + --release-root "http://localhost:$E2E_WEB_SERVER_PORT" + assert_match "Already up to date" + + assert_command ./dfx --version + assert_contains "$version" } diff --git a/e2e/tests-dfx/upgrade_check.bash b/e2e/tests-dfx/upgrade_check.bash index 52641c7f0f..f4d93fc025 100644 --- a/e2e/tests-dfx/upgrade_check.bash +++ b/e2e/tests-dfx/upgrade_check.bash @@ -3,15 +3,15 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "safe upgrade by adding a new stable variable" { @@ -26,41 +26,41 @@ teardown() { } @test "changing stable variable from Int to Nat is not allowed" { - install_asset upgrade - dfx_start - dfx deploy - dfx canister call hello_backend inc '()' - jq '.canisters.hello_backend.main="v2_bad.mo"' dfx.json | sponge dfx.json - echo yes | ( - assert_command dfx deploy - assert_match "Stable interface compatibility check failed" - ) - assert_command dfx canister call hello_backend read '()' - assert_match "(0 : nat)" + install_asset upgrade + dfx_start + dfx deploy + dfx canister call hello_backend inc '()' + jq '.canisters.hello_backend.main="v2_bad.mo"' dfx.json | sponge dfx.json + echo yes | ( + assert_command dfx deploy + assert_match "Stable interface compatibility check failed" + ) + assert_command dfx canister call hello_backend read '()' + assert_match "(0 : nat)" } @test "changing stable variable from Int to Nat with reinstall is allowed" { - install_asset upgrade - dfx_start - dfx deploy - dfx canister call hello_backend inc '()' - jq '.canisters.hello_backend.main="v2_bad.mo"' dfx.json | sponge dfx.json - dfx build - echo yes | dfx canister install hello_backend --mode=reinstall - assert_command dfx canister call hello_backend read '()' - assert_match "(0 : nat)" + install_asset upgrade + dfx_start + dfx deploy + dfx canister call hello_backend inc '()' + jq '.canisters.hello_backend.main="v2_bad.mo"' dfx.json | sponge dfx.json + dfx build + echo yes | dfx canister install hello_backend --mode=reinstall + assert_command dfx canister call hello_backend read '()' + assert_match "(0 : nat)" } @test "warning for changing method name" { - install_asset upgrade - dfx_start - dfx deploy - dfx canister call hello_backend inc '()' - jq '.canisters.hello_backend.main="v3_bad.mo"' dfx.json | sponge dfx.json - echo yes | ( - assert_command dfx deploy - assert_match "Candid interface compatibility check failed" - ) - assert_command dfx canister call hello_backend read2 '()' - assert_match "(1 : int)" + install_asset upgrade + dfx_start + dfx deploy + dfx canister call hello_backend inc '()' + jq '.canisters.hello_backend.main="v3_bad.mo"' dfx.json | sponge dfx.json + echo yes | ( + assert_command dfx deploy + assert_match "Candid interface compatibility check failed" + ) + assert_command dfx canister call hello_backend read2 '()' + assert_match "(1 : int)" } diff --git a/e2e/tests-dfx/usage.bash b/e2e/tests-dfx/usage.bash index bf3246face..96b9f5aada 100644 --- a/e2e/tests-dfx/usage.bash +++ b/e2e/tests-dfx/usage.bash @@ -1,13 +1,13 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "dfx help succeeds" { @@ -19,21 +19,21 @@ teardown() { } @test "using an invalid command fails" { - run dfx blurp - if [[ $status -eq 0 ]]; then - echo "$@" >&2 - exit 1 - fi + run dfx blurp + if [[ $status -eq 0 ]]; then + echo "$@" >&2 + exit 1 + fi } @test "returns the right error if not in a project" { - assert_command_fail dfx build - assert_match "Cannot find dfx configuration file in the current working directory. Did you forget to create one?" + assert_command_fail dfx build + assert_match "Cannot find dfx configuration file in the current working directory. Did you forget to create one?" - dfx new t --no-frontend - cd t - dfx_start - dfx canister create --all - assert_command dfx build + dfx new t --no-frontend + cd t + dfx_start + dfx canister create --all + assert_command dfx build } diff --git a/e2e/tests-dfx/usage_env.bash b/e2e/tests-dfx/usage_env.bash index cd000baf37..7cd19f6af5 100644 --- a/e2e/tests-dfx/usage_env.bash +++ b/e2e/tests-dfx/usage_env.bash @@ -3,47 +3,47 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - standard_teardown + standard_teardown } @test "dfx config root env var stores identity & cache" { - use_test_specific_cache_root # Because this test depends on a clean cache state - - #identity - dfx identity new --storage-mode plaintext alice - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" - assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" - - assert_command_fail head "$HOME/.config/dfx/identity/alice/identity.pem" - assert_command_fail head "$HOME/.config/dfx/identity/default/identity.pem" + use_test_specific_cache_root # Because this test depends on a clean cache state + + #identity + dfx identity new --storage-mode plaintext alice + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/alice/identity.pem" + assert_command head "$DFX_CONFIG_ROOT/.config/dfx/identity/default/identity.pem" + + assert_command_fail head "$HOME/.config/dfx/identity/alice/identity.pem" + assert_command_fail head "$HOME/.config/dfx/identity/default/identity.pem" + + #cache + # create a new project to install dfx cache + assert_command_fail ls "$DFX_CACHE_ROOT/.cache/dfinity/versions" + dfx new hello + assert_command ls "$DFX_CACHE_ROOT/.cache/dfinity/versions" + assert_command_fail ls "$HOME/.cache/dfinity/versions" + rm -rf hello + + ( + # use subshell to retain $DFX_CONFIG_ROOT for teardown + # remove configured variable, should use $HOME now + unset DFX_CACHE_ROOT + unset DFX_CONFIG_ROOT + + dfx identity new --storage-mode plaintext bob + assert_command head "$HOME/.config/dfx/identity/bob/identity.pem" + assert_command head "$HOME/.config/dfx/identity/default/identity.pem" #cache # create a new project to install dfx cache - assert_command_fail ls "$DFX_CACHE_ROOT/.cache/dfinity/versions" - dfx new hello - assert_command ls "$DFX_CACHE_ROOT/.cache/dfinity/versions" assert_command_fail ls "$HOME/.cache/dfinity/versions" + dfx new hello + assert_command ls "$HOME/.cache/dfinity/versions" rm -rf hello - - ( - # use subshell to retain $DFX_CONFIG_ROOT for teardown - # remove configured variable, should use $HOME now - unset DFX_CACHE_ROOT - unset DFX_CONFIG_ROOT - - dfx identity new --storage-mode plaintext bob - assert_command head "$HOME/.config/dfx/identity/bob/identity.pem" - assert_command head "$HOME/.config/dfx/identity/default/identity.pem" - - #cache - # create a new project to install dfx cache - assert_command_fail ls "$HOME/.cache/dfinity/versions" - dfx new hello - assert_command ls "$HOME/.cache/dfinity/versions" - rm -rf hello - ) + ) } diff --git a/e2e/tests-dfx/wallet.bash b/e2e/tests-dfx/wallet.bash index e8d71486db..56f9677204 100644 --- a/e2e/tests-dfx/wallet.bash +++ b/e2e/tests-dfx/wallet.bash @@ -3,224 +3,224 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "DFX_WALLET_WASM environment variable overrides wallet module wasm at installation" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - dfx identity new --storage-mode plaintext alice - dfx identity new --storage-mode plaintext bob + dfx identity new --storage-mode plaintext alice + dfx identity new --storage-mode plaintext bob - use_wallet_wasm 0.7.0 - assert_command dfx identity get-wallet --identity alice - assert_match "Using wasm at path: .*/wallet/0.7.0/wallet.wasm" + use_wallet_wasm 0.7.0 + assert_command dfx identity get-wallet --identity alice + assert_match "Using wasm at path: .*/wallet/0.7.0/wallet.wasm" - use_wallet_wasm 0.7.2 - assert_command dfx identity get-wallet --identity bob - assert_match "Using wasm at path: .*/wallet/0.7.2/wallet.wasm" + use_wallet_wasm 0.7.2 + assert_command dfx identity get-wallet --identity bob + assert_match "Using wasm at path: .*/wallet/0.7.2/wallet.wasm" - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - BOB_WALLET=$(dfx identity get-wallet --identity bob) + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + BOB_WALLET=$(dfx identity get-wallet --identity bob) - assert_command dfx canister info "$ALICE_WALLET" --identity alice - assert_match "Module hash: 0xa609400f2576d1d6df72ce868b359fd08e1d68e58454ef17db2361d2f1c242a1" + assert_command dfx canister info "$ALICE_WALLET" --identity alice + assert_match "Module hash: 0xa609400f2576d1d6df72ce868b359fd08e1d68e58454ef17db2361d2f1c242a1" - assert_command dfx canister info "$BOB_WALLET" --identity bob - assert_match "Module hash: 0x1404b28b1c66491689b59e184a9de3c2be0dbdd75d952f29113b516742b7f898" + assert_command dfx canister info "$BOB_WALLET" --identity bob + assert_match "Module hash: 0x1404b28b1c66491689b59e184a9de3c2be0dbdd75d952f29113b516742b7f898" } @test "DFX_WALLET_WASM environment variable overrides wallet module wasm for upgrade" { - dfx_new hello - dfx_start + dfx_new hello + dfx_start - use_wallet_wasm 0.7.0-beta.5 + use_wallet_wasm 0.7.0-beta.5 - assert_command dfx identity get-wallet - WALLET_ID=$(dfx identity get-wallet) + assert_command dfx identity get-wallet + WALLET_ID=$(dfx identity get-wallet) - assert_command dfx canister info "$WALLET_ID" - assert_match "Module hash: 0x3d5b221387875574a9fd75b3165403cf1b301650a602310e9e4229d2f6766dcc" + assert_command dfx canister info "$WALLET_ID" + assert_match "Module hash: 0x3d5b221387875574a9fd75b3165403cf1b301650a602310e9e4229d2f6766dcc" - use_wallet_wasm 0.7.0 - assert_command dfx wallet upgrade + use_wallet_wasm 0.7.0 + assert_command dfx wallet upgrade - assert_command dfx canister info "$WALLET_ID" - assert_match "Module hash: 0xa609400f2576d1d6df72ce868b359fd08e1d68e58454ef17db2361d2f1c242a1" + assert_command dfx canister info "$WALLET_ID" + assert_match "Module hash: 0xa609400f2576d1d6df72ce868b359fd08e1d68e58454ef17db2361d2f1c242a1" } @test "'dfx identity set-wallet --force' bypasses wallet canister verification" { - dfx_new hello - dfx_start - setup_actuallylocal_shared_network - - # get Canister IDs to install the wasm onto - dfx canister create hello_backend --network actuallylocal - ID=$(dfx canister id hello_backend --network actuallylocal) - dfx canister create hello_frontend --network actuallylocal - ID_TWO=$(dfx canister id hello_frontend --network actuallylocal) - - # set controller to user - dfx canister update-settings hello_backend --set-controller "$(dfx identity get-principal)" --network actuallylocal - dfx canister update-settings hello_frontend --set-controller "$(dfx identity get-principal)" --network actuallylocal - - assert_command_fail dfx identity set-wallet "${ID}" --network actuallylocal - assert_not_match "Setting wallet for identity" - assert_command dfx identity set-wallet --force "${ID}" --network actuallylocal - assert_match "Setting wallet for identity 'default' on network 'actuallylocal' to id '$ID'" - assert_command jq -r .identities.default.actuallylocal <"$DFX_CONFIG_ROOT"/.config/dfx/identity/default/wallets.json - assert_eq "$ID" + dfx_new hello + dfx_start + setup_actuallylocal_shared_network + + # get Canister IDs to install the wasm onto + dfx canister create hello_backend --network actuallylocal + ID=$(dfx canister id hello_backend --network actuallylocal) + dfx canister create hello_frontend --network actuallylocal + ID_TWO=$(dfx canister id hello_frontend --network actuallylocal) + + # set controller to user + dfx canister update-settings hello_backend --set-controller "$(dfx identity get-principal)" --network actuallylocal + dfx canister update-settings hello_frontend --set-controller "$(dfx identity get-principal)" --network actuallylocal + + assert_command_fail dfx identity set-wallet "${ID}" --network actuallylocal + assert_not_match "Setting wallet for identity" + assert_command dfx identity set-wallet --force "${ID}" --network actuallylocal + assert_match "Setting wallet for identity 'default' on network 'actuallylocal' to id '$ID'" + assert_command jq -r .identities.default.actuallylocal <"$DFX_CONFIG_ROOT"/.config/dfx/identity/default/wallets.json + assert_eq "$ID" } @test "deploy wallet" { - dfx_new hello - dfx_start - setup_actuallylocal_shared_network - - # get Canister IDs to install the wasm onto - dfx canister create hello_frontend --network actuallylocal - ID=$(dfx canister id hello_frontend --network actuallylocal) - dfx deploy hello_backend --network actuallylocal - ID_TWO=$(dfx canister id hello_backend --network actuallylocal) - - # set controller to user - dfx canister update-settings hello_backend --set-controller "$(dfx identity get-principal)" --network actuallylocal - dfx canister update-settings hello_frontend --set-controller "$(dfx identity get-principal)" --network actuallylocal - - # We're testing on a local network so the create command actually creates a wallet - # Delete this file to force associate wallet created by deploy-wallet to identity - rm "$DFX_CONFIG_ROOT"/.config/dfx/identity/default/wallets.json - - assert_command dfx identity deploy-wallet "${ID}" --network actuallylocal - GET_WALLET_RES=$(dfx identity get-wallet --network actuallylocal) - assert_eq "$ID" "$GET_WALLET_RES" - - # Command should fail on an already-deployed canister - assert_command_fail dfx identity deploy-wallet "${ID_TWO}" --network actuallylocal - assert_match "The wallet canister \"${ID_TWO}\"\ already exists for user \"default\" on \"actuallylocal\" network." + dfx_new hello + dfx_start + setup_actuallylocal_shared_network + + # get Canister IDs to install the wasm onto + dfx canister create hello_frontend --network actuallylocal + ID=$(dfx canister id hello_frontend --network actuallylocal) + dfx deploy hello_backend --network actuallylocal + ID_TWO=$(dfx canister id hello_backend --network actuallylocal) + + # set controller to user + dfx canister update-settings hello_backend --set-controller "$(dfx identity get-principal)" --network actuallylocal + dfx canister update-settings hello_frontend --set-controller "$(dfx identity get-principal)" --network actuallylocal + + # We're testing on a local network so the create command actually creates a wallet + # Delete this file to force associate wallet created by deploy-wallet to identity + rm "$DFX_CONFIG_ROOT"/.config/dfx/identity/default/wallets.json + + assert_command dfx identity deploy-wallet "${ID}" --network actuallylocal + GET_WALLET_RES=$(dfx identity get-wallet --network actuallylocal) + assert_eq "$ID" "$GET_WALLET_RES" + + # Command should fail on an already-deployed canister + assert_command_fail dfx identity deploy-wallet "${ID_TWO}" --network actuallylocal + assert_match "The wallet canister \"${ID_TWO}\"\ already exists for user \"default\" on \"actuallylocal\" network." } @test "wallet create wallet" { - dfx_new - dfx_start - WALLET_ID=$(dfx identity get-wallet) - CREATE_RES=$(dfx canister call "${WALLET_ID}" wallet_create_wallet "(record { cycles = (2000000000000:nat64); settings = record {controller = opt principal \"$(dfx identity get-principal)\";};})") - CHILD_ID=$(echo "${CREATE_RES}" | tr '\n' ' ' | cut -d'"' -f 2) - assert_command dfx canister call "${CHILD_ID}" wallet_balance '()' + dfx_new + dfx_start + WALLET_ID=$(dfx identity get-wallet) + CREATE_RES=$(dfx canister call "${WALLET_ID}" wallet_create_wallet "(record { cycles = (2000000000000:nat64); settings = record {controller = opt principal \"$(dfx identity get-principal)\";};})") + CHILD_ID=$(echo "${CREATE_RES}" | tr '\n' ' ' | cut -d'"' -f 2) + assert_command dfx canister call "${CHILD_ID}" wallet_balance '()' } @test "forward user call through wallet" { - dfx_new - install_asset identity - dfx_start - WALLET=$(dfx identity get-wallet) - assert_command dfx canister create --all --wallet "$WALLET" - assert_command dfx build - assert_command dfx canister install --all --wallet "$WALLET" - - CALL_RES=$(dfx canister call e2e_project_backend fromCall --wallet "$WALLET") - CALLER=$(echo "${CALL_RES}" | cut -d'"' -f 2) - assert_eq "$CALLER" "$WALLET" - - assert_command dfx canister call "$WALLET" wallet_call \ - "(record { canister = principal \"$(dfx canister id e2e_project_backend)\"; method_name = \"amInitializer\"; args = blob \"DIDL\00\00\"; cycles = (0:nat64)})" - assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01~\01" } })' # True in DIDL. + dfx_new + install_asset identity + dfx_start + WALLET=$(dfx identity get-wallet) + assert_command dfx canister create --all --wallet "$WALLET" + assert_command dfx build + assert_command dfx canister install --all --wallet "$WALLET" + + CALL_RES=$(dfx canister call e2e_project_backend fromCall --wallet "$WALLET") + CALLER=$(echo "${CALL_RES}" | cut -d'"' -f 2) + assert_eq "$CALLER" "$WALLET" + + assert_command dfx canister call "$WALLET" wallet_call \ + "(record { canister = principal \"$(dfx canister id e2e_project_backend)\"; method_name = \"amInitializer\"; args = blob \"DIDL\00\00\"; cycles = (0:nat64)})" + assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01~\01" } })' # True in DIDL. } @test "forward user call through wallet: deploy" { - dfx_new - install_asset identity - dfx_start - WALLET=$(dfx identity get-wallet) - assert_command dfx deploy --wallet "$WALLET" - CALL_RES=$(dfx canister call e2e_project_backend fromCall --wallet "$WALLET") - CALLER=$(echo "${CALL_RES}" | cut -d'"' -f 2) - assert_eq "$CALLER" "$WALLET" - - assert_command dfx canister call e2e_project_backend amInitializer - assert_command dfx canister call "$WALLET" wallet_call \ - "(record { canister = principal \"$(dfx canister id e2e_project_backend)\"; method_name = \"amInitializer\"; args = blob \"DIDL\00\00\"; cycles = (0:nat64)})" - assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01~\01" } })' # True in DIDL. + dfx_new + install_asset identity + dfx_start + WALLET=$(dfx identity get-wallet) + assert_command dfx deploy --wallet "$WALLET" + CALL_RES=$(dfx canister call e2e_project_backend fromCall --wallet "$WALLET") + CALLER=$(echo "${CALL_RES}" | cut -d'"' -f 2) + assert_eq "$CALLER" "$WALLET" + + assert_command dfx canister call e2e_project_backend amInitializer + assert_command dfx canister call "$WALLET" wallet_call \ + "(record { canister = principal \"$(dfx canister id e2e_project_backend)\"; method_name = \"amInitializer\"; args = blob \"DIDL\00\00\"; cycles = (0:nat64)})" + assert_eq '(variant { 17_724 = record { 153_986_224 = blob "DIDL\00\01~\01" } })' # True in DIDL. } @test "a 64-bit wallet can still be called in the 128-bit context" { - use_wallet_wasm 0.8.2 - dfx_new hello - dfx_start - WALLET=$(dfx identity get-wallet) - assert_command dfx wallet balance - assert_command dfx deploy --wallet "$WALLET" - assert_command dfx canister call hello_backend greet '("")' --with-cycles 1 --wallet "$WALLET" - dfx identity new alice --storage-mode plaintext - ALICE_WALLET=$(dfx identity get-wallet --identity alice) - dfx wallet send "$ALICE_WALLET" 1 + use_wallet_wasm 0.8.2 + dfx_new hello + dfx_start + WALLET=$(dfx identity get-wallet) + assert_command dfx wallet balance + assert_command dfx deploy --wallet "$WALLET" + assert_command dfx canister call hello_backend greet '("")' --with-cycles 1 --wallet "$WALLET" + dfx identity new alice --storage-mode plaintext + ALICE_WALLET=$(dfx identity get-wallet --identity alice) + dfx wallet send "$ALICE_WALLET" 1 } @test "dfx canister deposit-cycles succeeds on a canister the caller does not own" { - dfx_new hello - dfx_start - dfx identity new alice --storage-mode plaintext - dfx deploy --no-wallet hello_backend --identity alice - assert_command dfx canister deposit-cycles 1 hello_backend --wallet "$(dfx identity get-wallet)" + dfx_new hello + dfx_start + dfx identity new alice --storage-mode plaintext + dfx deploy --no-wallet hello_backend --identity alice + assert_command dfx canister deposit-cycles 1 hello_backend --wallet "$(dfx identity get-wallet)" } @test "dfx canister deposit-cycles uses default wallet if no wallet is specified" { - dfx_new hello - dfx_start - dfx deploy - assert_command dfx canister deposit-cycles 1 hello_backend + dfx_new hello + dfx_start + dfx deploy + assert_command dfx canister deposit-cycles 1 hello_backend } @test "detects if there is no wallet to upgrade" { - dfx_new hello - assert_command_fail dfx wallet upgrade - assert_match "There is no wallet defined for identity 'default' on network 'local'. Nothing to do." + dfx_new hello + assert_command_fail dfx wallet upgrade + assert_match "There is no wallet defined for identity 'default' on network 'local'. Nothing to do." } @test "redeem-faucet-coupon can set a new wallet and top up an existing one" { - dfx_new hello - dfx_start - install_asset faucet - dfx deploy - dfx ledger fabricate-cycles --canister faucet --t 1000 - - dfx identity new --storage-mode plaintext faucet_testing - dfx identity use faucet_testing - - # prepare wallet to hand out - dfx wallet balance # this creates a new wallet with user faucet_testing as controller - dfx canister call faucet set_wallet_to_hand_out "(principal \"$(dfx identity get-wallet)\")" # register the wallet as the wallet that the faucet will return - rm "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" # forget about the currently configured wallet - - # assert: no wallet configured - export DFX_DISABLE_AUTO_WALLET=1 - assert_command_fail dfx wallet balance - assert_match "command requires a configured wallet" - - assert_command dfx wallet redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'valid-coupon' - assert_match "Redeemed coupon valid-coupon for a new wallet" - assert_match "New wallet set." - - # only succeeds if wallet is correctly set - assert_command dfx wallet balance - # checking only balance before the dot, rest may fluctuate - # balance may be 99.??? TC if cycles accounting is done, or 100.000 TC if not - assert_match "99\.|100\." - - unset DFX_DISABLE_AUTO_WALLET - - assert_command dfx wallet redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'another-valid-coupon' - assert_match "Redeemed coupon code another-valid-coupon for 10.000 TC" - - assert_command dfx wallet balance - # checking only balance before the dot, rest may fluctuate - # balance may be 109.??? TC if cycles accounting is done, or 110.000 TC if not - assert_match "109\.|110\." + dfx_new hello + dfx_start + install_asset faucet + dfx deploy + dfx ledger fabricate-cycles --canister faucet --t 1000 + + dfx identity new --storage-mode plaintext faucet_testing + dfx identity use faucet_testing + + # prepare wallet to hand out + dfx wallet balance # this creates a new wallet with user faucet_testing as controller + dfx canister call faucet set_wallet_to_hand_out "(principal \"$(dfx identity get-wallet)\")" # register the wallet as the wallet that the faucet will return + rm "$E2E_SHARED_LOCAL_NETWORK_DATA_DIRECTORY/wallets.json" # forget about the currently configured wallet + + # assert: no wallet configured + export DFX_DISABLE_AUTO_WALLET=1 + assert_command_fail dfx wallet balance + assert_match "command requires a configured wallet" + + assert_command dfx wallet redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'valid-coupon' + assert_match "Redeemed coupon valid-coupon for a new wallet" + assert_match "New wallet set." + + # only succeeds if wallet is correctly set + assert_command dfx wallet balance + # checking only balance before the dot, rest may fluctuate + # balance may be 99.??? TC if cycles accounting is done, or 100.000 TC if not + assert_match "99\.|100\." + + unset DFX_DISABLE_AUTO_WALLET + + assert_command dfx wallet redeem-faucet-coupon --faucet "$(dfx canister id faucet)" 'another-valid-coupon' + assert_match "Redeemed coupon code another-valid-coupon for 10.000 TC" + + assert_command dfx wallet balance + # checking only balance before the dot, rest may fluctuate + # balance may be 109.??? TC if cycles accounting is done, or 110.000 TC if not + assert_match "109\.|110\." } diff --git a/e2e/tests-icx-asset/icx-asset.bash b/e2e/tests-icx-asset/icx-asset.bash index 8c292050bf..786887aea2 100644 --- a/e2e/tests-icx-asset/icx-asset.bash +++ b/e2e/tests-icx-asset/icx-asset.bash @@ -3,24 +3,24 @@ load ../utils/_ setup() { - # when running e2e tests not in GitHub CI (so e.g. locally), build icx-proxy and set environment variable - if [ -z "$ICX_ASSET" ]; then - cargo build -p icx-asset - ICX_ASSET="$(pwd)/target/debug/icx-asset" - fi + # when running e2e tests not in GitHub CI (so e.g. locally), build icx-proxy and set environment variable + if [ -z "$ICX_ASSET" ]; then + cargo build -p icx-asset + ICX_ASSET="$(pwd)/target/debug/icx-asset" + fi - standard_setup + standard_setup - dfx_new_frontend - dfx_start + dfx_new_frontend + dfx_start - assert_command dfx deploy + assert_command dfx deploy } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } icx_asset_sync() { @@ -28,10 +28,10 @@ icx_asset_sync() { REPLICA_ADDRESS="http://localhost:$(get_webserver_port)" CANISTER_ID=$(dfx canister id e2e_project_frontend) if [ -z "$1" ]; then - assert_command "$ICX_ASSET" --pem "$IDENTITY" --replica "$REPLICA_ADDRESS" sync "$CANISTER_ID" src/e2e_project_frontend/assets + assert_command "$ICX_ASSET" --pem "$IDENTITY" --replica "$REPLICA_ADDRESS" sync "$CANISTER_ID" src/e2e_project_frontend/assets else - # shellcheck disable=SC2086 - assert_command "$ICX_ASSET" --pem "$IDENTITY" --replica "$REPLICA_ADDRESS" sync "$CANISTER_ID" $1 $2 + # shellcheck disable=SC2086 + assert_command "$ICX_ASSET" --pem "$IDENTITY" --replica "$REPLICA_ADDRESS" sync "$CANISTER_ID" $1 $2 fi } @@ -51,16 +51,16 @@ icx_asset_upload() { } @test "lists assets" { - for i in $(seq 1 400); do - echo "some easily duplicate text $i" >>src/e2e_project_frontend/assets/notreally.js - done - icx_asset_sync + for i in $(seq 1 400); do + echo "some easily duplicate text $i" >>src/e2e_project_frontend/assets/notreally.js + done + icx_asset_sync - icx_asset_list + icx_asset_list - assert_match "sample-asset.txt.*text/plain.*identity" - assert_match "notreally.js.*application/javascript.*gzip" - assert_match "notreally.js.*application/javascript.*identity" + assert_match "sample-asset.txt.*text/plain.*identity" + assert_match "notreally.js.*application/javascript.*gzip" + assert_match "notreally.js.*application/javascript.*identity" } @test "creates new files" { @@ -72,224 +72,224 @@ icx_asset_upload() { } @test "updates existing files" { - echo -n "an asset that will change" >src/e2e_project_frontend/assets/asset-to-change.txt - assert_command dfx deploy + echo -n "an asset that will change" >src/e2e_project_frontend/assets/asset-to-change.txt + assert_command dfx deploy - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/asset-to-change.txt";accept_encodings=vec{"identity"}})' - # shellcheck disable=SC2154 - assert_match '"an asset that will change"' "$stdout" + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/asset-to-change.txt";accept_encodings=vec{"identity"}})' + # shellcheck disable=SC2154 + assert_match '"an asset that will change"' "$stdout" - echo -n "an asset that has been changed" >src/e2e_project_frontend/assets/asset-to-change.txt + echo -n "an asset that has been changed" >src/e2e_project_frontend/assets/asset-to-change.txt - icx_asset_sync + icx_asset_sync - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/asset-to-change.txt";accept_encodings=vec{"identity"}})' - # shellcheck disable=SC2154 - assert_match '"an asset that has been changed"' "$stdout" + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/asset-to-change.txt";accept_encodings=vec{"identity"}})' + # shellcheck disable=SC2154 + assert_match '"an asset that has been changed"' "$stdout" echo pass } @test "deletes removed files" { - touch src/e2e_project_frontend/assets/will-delete-this.txt - dfx deploy + touch src/e2e_project_frontend/assets/will-delete-this.txt + dfx deploy - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_match '"/will-delete-this.txt"' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_match '"/will-delete-this.txt"' - rm src/e2e_project_frontend/assets/will-delete-this.txt + rm src/e2e_project_frontend/assets/will-delete-this.txt - icx_asset_sync + icx_asset_sync - assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_not_match '"/will-delete-this.txt"' + assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/will-delete-this.txt";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_not_match '"/will-delete-this.txt"' } @test "unsets asset encodings that are removed from project" { - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="text/plain"; content_encoding="arbitrary"; content=blob "content encoded in another way!"})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="text/plain"; content_encoding="arbitrary"; content=blob "content encoded in another way!"})' - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' - icx_asset_sync + icx_asset_sync - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' - # shellcheck disable=SC2086 - assert_command_fail dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"identity"}})' + # shellcheck disable=SC2086 + assert_command_fail dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/sample-asset.txt";accept_encodings=vec{"arbitrary"}})' } @test "synchronizes multiple directories" { - mkdir -p multiple/a - mkdir -p multiple/b - echo "x_contents" >multiple/a/x - echo "y_contents" >multiple/b/y + mkdir -p multiple/a + mkdir -p multiple/b + echo "x_contents" >multiple/a/x + echo "y_contents" >multiple/b/y - icx_asset_sync multiple/a multiple/b - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/x";accept_encodings=vec{"identity"}})' - assert_match "x_contents" - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/y";accept_encodings=vec{"identity"}})' - assert_match "y_contents" + icx_asset_sync multiple/a multiple/b + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/x";accept_encodings=vec{"identity"}})' + assert_match "x_contents" + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --query e2e_project_frontend get '(record{key="/y";accept_encodings=vec{"identity"}})' + assert_match "y_contents" } @test "reports errors about assets with the same key from multiple sources" { - mkdir -p multiple/a - mkdir -p multiple/b - echo "a_duplicate_contents" >multiple/a/duplicate - echo "b_duplicate_contents" >multiple/b/duplicate + mkdir -p multiple/a + mkdir -p multiple/b + echo "a_duplicate_contents" >multiple/a/duplicate + echo "b_duplicate_contents" >multiple/b/duplicate - assert_command_fail icx_asset_sync multiple/a multiple/b - assert_match "Asset with key '/duplicate' defined at .*/e2e_project/multiple/b/duplicate and .*/e2e_project/multiple/a/duplicate" + assert_command_fail icx_asset_sync multiple/a multiple/b + assert_match "Asset with key '/duplicate' defined at .*/e2e_project/multiple/b/duplicate and .*/e2e_project/multiple/a/duplicate" } @test "ignores filenames and directories starting with a dot" { - touch src/e2e_project_frontend/assets/.not-seen - touch src/e2e_project_frontend/assets/is-seen + touch src/e2e_project_frontend/assets/.not-seen + touch src/e2e_project_frontend/assets/is-seen - mkdir -p src/e2e_project_frontend/assets/.dir-skipped - touch src/e2e_project_frontend/assets/.dir-skipped/also-ignored + mkdir -p src/e2e_project_frontend/assets/.dir-skipped + touch src/e2e_project_frontend/assets/.dir-skipped/also-ignored - mkdir -p src/e2e_project_frontend/assets/dir-not-skipped - touch src/e2e_project_frontend/assets/dir-not-skipped/not-ignored + mkdir -p src/e2e_project_frontend/assets/dir-not-skipped + touch src/e2e_project_frontend/assets/dir-not-skipped/not-ignored - icx_asset_sync + icx_asset_sync - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/is-seen";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend get '(record{key="/dir-not-skipped/not-ignored";accept_encodings=vec{"identity"}})' - assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/.not-seen";accept_encodings=vec{"identity"}})' - assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/.dir-skipped/also-ignored";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/is-seen";accept_encodings=vec{"identity"}})' + assert_command dfx canister call --query e2e_project_frontend get '(record{key="/dir-not-skipped/not-ignored";accept_encodings=vec{"identity"}})' + assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/.not-seen";accept_encodings=vec{"identity"}})' + assert_command_fail dfx canister call --query e2e_project_frontend get '(record{key="/.dir-skipped/also-ignored";accept_encodings=vec{"identity"}})' - assert_command dfx canister call --query e2e_project_frontend list '(record{})' + assert_command dfx canister call --query e2e_project_frontend list '(record{})' - assert_match 'is-seen' - assert_match 'not-ignored' + assert_match 'is-seen' + assert_match 'not-ignored' - assert_not_match 'not-seen' - assert_not_match 'also-ignored' + assert_not_match 'not-seen' + assert_not_match 'also-ignored' } @test "does not delete files that are not being uploaded" { - mkdir some_dir - echo "some stuff" >some_dir/a.txt - echo "more things" >some_dir/b.txt + mkdir some_dir + echo "some stuff" >some_dir/a.txt + echo "more things" >some_dir/b.txt - icx_asset_upload /=some_dir + icx_asset_upload /=some_dir - icx_asset_list + icx_asset_list - assert_match " /a.txt.*text/plain.*identity" - assert_match " /b.txt.*text/plain.*identity" + assert_match " /a.txt.*text/plain.*identity" + assert_match " /b.txt.*text/plain.*identity" - echo "ccc" >c.txt - icx_asset_upload c.txt + echo "ccc" >c.txt + icx_asset_upload c.txt - icx_asset_list + icx_asset_list - assert_match " /a.txt.*text/plain.*identity" - assert_match " /b.txt.*text/plain.*identity" - assert_match " /c.txt.*text/plain.*identity" + assert_match " /a.txt.*text/plain.*identity" + assert_match " /b.txt.*text/plain.*identity" + assert_match " /c.txt.*text/plain.*identity" } @test "deletes asset if necessary in order to change content type" { - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="application/pdf"; content_encoding="identity"; content=blob "whatever contents!"})' - # shellcheck disable=SC2086 - assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="application/pdf"; content_encoding="arbitrary"; content=blob "other contents"})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="application/pdf"; content_encoding="identity"; content=blob "whatever contents!"})' + # shellcheck disable=SC2086 + assert_command dfx canister ${DFX_NO_WALLET:-} call --update e2e_project_frontend store '(record{key="/sample-asset.txt"; content_type="application/pdf"; content_encoding="arbitrary"; content=blob "other contents"})' - icx_asset_list + icx_asset_list - assert_match " /sample-asset.txt.*application/pdf.*identity" - assert_match " /sample-asset.txt.*application/pdf.*arbitrary" + assert_match " /sample-asset.txt.*application/pdf.*identity" + assert_match " /sample-asset.txt.*application/pdf.*arbitrary" - echo "just some text" >sample-asset.txt + echo "just some text" >sample-asset.txt - # icx-asset upload should delete the asset (and upload its replacement) since the content type is different. - icx_asset_upload sample-asset.txt + # icx-asset upload should delete the asset (and upload its replacement) since the content type is different. + icx_asset_upload sample-asset.txt - icx_asset_list + icx_asset_list - assert_match " /sample-asset.txt.*text/plain.*identity" - assert_not_match " /sample-asset.txt.*application/pdf.*arbitrary" + assert_match " /sample-asset.txt.*text/plain.*identity" + assert_not_match " /sample-asset.txt.*application/pdf.*arbitrary" } @test "uploads multiple files" { - echo "this is the file content" >uploaded.txt - echo "this is the file content ttt" >xyz.txt - mkdir some_dir - echo "some stuff" >some_dir/a.txt - echo "more things" >some_dir/b.txt + echo "this is the file content" >uploaded.txt + echo "this is the file content ttt" >xyz.txt + mkdir some_dir + echo "some stuff" >some_dir/a.txt + echo "more things" >some_dir/b.txt - icx_asset_upload some_dir/*.txt - icx_asset_list + icx_asset_upload some_dir/*.txt + icx_asset_list - assert_match " /a.txt.*text/plain.*identity" - assert_match " /b.txt.*text/plain.*identity" + assert_match " /a.txt.*text/plain.*identity" + assert_match " /b.txt.*text/plain.*identity" } @test "uploads multiple files from absolute path" { - mkdir some_dir - echo "some stuff" >some_dir/a.txt - echo "more things" >some_dir/b.txt + mkdir some_dir + echo "some stuff" >some_dir/a.txt + echo "more things" >some_dir/b.txt - icx_asset_upload "$(realpath some_dir/a.txt)" "$(realpath some_dir/b.txt)" - icx_asset_list + icx_asset_upload "$(realpath some_dir/a.txt)" "$(realpath some_dir/b.txt)" + icx_asset_list - assert_match " /a.txt.*text/plain.*identity" - assert_match " /b.txt.*text/plain.*identity" + assert_match " /a.txt.*text/plain.*identity" + assert_match " /b.txt.*text/plain.*identity" } @test "uploads a file by name" { - echo "this is the file content" >uploaded.txt + echo "this is the file content" >uploaded.txt - icx_asset_upload uploaded.txt + icx_asset_upload uploaded.txt - icx_asset_list + icx_asset_list - assert_match " /uploaded.txt.*text/plain.*identity" + assert_match " /uploaded.txt.*text/plain.*identity" } @test "can override asset name" { - echo "this is the file content" >uploaded.txt + echo "this is the file content" >uploaded.txt - icx_asset_upload /abcd.txt=uploaded.txt + icx_asset_upload /abcd.txt=uploaded.txt - icx_asset_list + icx_asset_list - assert_match " /abcd.txt.*text/plain.*identity" + assert_match " /abcd.txt.*text/plain.*identity" } @test "uploads a directory by name" { - mkdir some_dir - echo "some stuff" >some_dir/a.txt - echo "more things" >some_dir/b.txt + mkdir some_dir + echo "some stuff" >some_dir/a.txt + echo "more things" >some_dir/b.txt - icx_asset_upload some_dir + icx_asset_upload some_dir - icx_asset_list + icx_asset_list - assert_match " /some_dir/a.txt.*text/plain.*identity" - assert_match " /some_dir/b.txt.*text/plain.*identity" + assert_match " /some_dir/a.txt.*text/plain.*identity" + assert_match " /some_dir/b.txt.*text/plain.*identity" } @test "uploads a directory by name as root" { - mkdir some_dir - echo "some stuff" >some_dir/a.txt - echo "more things" >some_dir/b.txt + mkdir some_dir + echo "some stuff" >some_dir/a.txt + echo "more things" >some_dir/b.txt - icx_asset_upload /=some_dir + icx_asset_upload /=some_dir - icx_asset_list + icx_asset_list - assert_match " /a.txt.*text/plain.*identity" - assert_match " /b.txt.*text/plain.*identity" + assert_match " /a.txt.*text/plain.*identity" + assert_match " /b.txt.*text/plain.*identity" } diff --git a/e2e/tests-replica/deploy.bash b/e2e/tests-replica/deploy.bash index 1faa10b72f..8ba88f5cf8 100644 --- a/e2e/tests-replica/deploy.bash +++ b/e2e/tests-replica/deploy.bash @@ -3,96 +3,96 @@ load ../utils/_ setup() { - standard_setup + standard_setup } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "deploy from a fresh project" { - dfx_new hello - dfx_start - install_asset greet - assert_command dfx deploy + dfx_new hello + dfx_start + install_asset greet + assert_command dfx deploy - assert_command dfx canister call hello_backend greet '("Banzai")' - assert_eq '("Hello, Banzai!")' + assert_command dfx canister call hello_backend greet '("Banzai")' + assert_eq '("Hello, Banzai!")' } @test "deploy a canister without dependencies" { - dfx_new hello - dfx_start - install_asset greet - assert_command dfx deploy hello_backend - assert_match 'Deploying: hello_backend' - assert_not_match 'hello_frontend' + dfx_new hello + dfx_start + install_asset greet + assert_command dfx deploy hello_backend + assert_match 'Deploying: hello_backend' + assert_not_match 'hello_frontend' } @test "deploy a canister with dependencies" { - dfx_new hello - dfx_start - install_asset greet - assert_command dfx deploy hello_frontend - assert_match 'Deploying: hello_backend hello_frontend' + dfx_new hello + dfx_start + install_asset greet + assert_command dfx deploy hello_frontend + assert_match 'Deploying: hello_backend hello_frontend' } @test "deploy a canister with non-circular shared dependencies" { - install_asset transitive_deps_canisters - dfx_start - assert_command dfx deploy canister_f - assert_match 'Deploying: canister_a canister_f canister_g canister_h' + install_asset transitive_deps_canisters + dfx_start + assert_command dfx deploy canister_f + assert_match 'Deploying: canister_a canister_f canister_g canister_h' } @test "report an error on attempt to deploy a canister with circular dependencies" { - install_asset transitive_deps_canisters - dfx_start - assert_command_fail dfx deploy canister_d - assert_match 'canister_d -> canister_e -> canister_d' + install_asset transitive_deps_canisters + dfx_start + assert_command_fail dfx deploy canister_d + assert_match 'canister_d -> canister_e -> canister_d' } @test "deploy with InstallMode::Install on an empty canister" { - dfx_new hello - install_asset greet - dfx_start - assert_command dfx canister create --all + dfx_new hello + install_asset greet + dfx_start + assert_command dfx canister create --all - assert_command dfx deploy - assert_match 'Installing code for canister' + assert_command dfx deploy + assert_match 'Installing code for canister' } @test "dfx deploy supports arguments" { - dfx_new hello - install_asset greet_arg - dfx_start - assert_command dfx canister create --all + dfx_new hello + install_asset greet_arg + dfx_start + assert_command dfx canister create --all - assert_command dfx deploy hello_backend --argument '("World")' + assert_command dfx deploy hello_backend --argument '("World")' - assert_command dfx canister call hello_backend greet - assert_match 'Hello, World' + assert_command dfx canister call hello_backend greet + assert_match 'Hello, World' } @test "dfx deploy with InstallMode::Install on first invocation, InstallMode::Upgrade on second" { - dfx_new hello - install_asset greet - dfx_start + dfx_new hello + install_asset greet + dfx_start - # In the normal case, whether for an initial install or a subsequent install, - # dfx deploy does the right thing, so it doesn't need to retry. - # Therefore, there is no "attempting (install|upgrade)" message. + # In the normal case, whether for an initial install or a subsequent install, + # dfx deploy does the right thing, so it doesn't need to retry. + # Therefore, there is no "attempting (install|upgrade)" message. - assert_command dfx deploy hello_backend - assert_match 'Installing code for canister' + assert_command dfx deploy hello_backend + assert_match 'Installing code for canister' - assert_command dfx canister call hello_backend greet '("First")' - assert_eq '("Hello, First!")' + assert_command dfx canister call hello_backend greet '("First")' + assert_eq '("Hello, First!")' - assert_command dfx deploy hello_backend --upgrade-unchanged - assert_match 'Upgrading code for canister' + assert_command dfx deploy hello_backend --upgrade-unchanged + assert_match 'Upgrading code for canister' - assert_command dfx canister call hello_backend greet '("Second")' - assert_eq '("Hello, Second!")' + assert_command dfx canister call hello_backend greet '("Second")' + assert_eq '("Hello, Second!")' } diff --git a/e2e/tests-replica/lifecycle.bash b/e2e/tests-replica/lifecycle.bash index 70e24a5ee2..a675477a76 100644 --- a/e2e/tests-replica/lifecycle.bash +++ b/e2e/tests-replica/lifecycle.bash @@ -3,57 +3,57 @@ load ../utils/_ setup() { - standard_setup + standard_setup - dfx_new hello + dfx_new hello } teardown() { - dfx_stop + dfx_stop - standard_teardown + standard_teardown } @test "test canister lifecycle" { - install_asset greet - dfx_start - dfx canister create --all - dfx build - dfx canister install hello_backend - assert_command dfx canister status hello_backend - assert_match "Status: Running." - - # Stop - assert_command dfx canister stop hello_backend - assert_command dfx canister status hello_backend - assert_match "Status: Stopped." - assert_command_fail dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' - assert_match "is stopped" - - # Start - assert_command dfx canister start hello_backend - assert_command dfx canister status hello_backend - assert_match "Status: Running." - - # Call - assert_command dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' - assert_match '("Hello, Names are difficult!")' - - # Id - assert_command dfx canister id hello_backend - assert_match "$(jq -r .hello_backend.local < .dfx/local/canister_ids.json)" - x="$(dfx canister id hello_backend)" - local old_id="$x" - - # Delete - assert_command_fail dfx canister delete hello_backend - assert_command dfx canister stop hello_backend - assert_command dfx canister delete hello_backend - assert_command_fail dfx canister status hello_backend - assert_match "Cannot find canister id. Please issue 'dfx canister create hello_backend'." - - # Create again - assert_command dfx canister create hello_backend - assert_command dfx canister id hello_backend - assert_neq "$old_id" + install_asset greet + dfx_start + dfx canister create --all + dfx build + dfx canister install hello_backend + assert_command dfx canister status hello_backend + assert_match "Status: Running." + + # Stop + assert_command dfx canister stop hello_backend + assert_command dfx canister status hello_backend + assert_match "Status: Stopped." + assert_command_fail dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' + assert_match "is stopped" + + # Start + assert_command dfx canister start hello_backend + assert_command dfx canister status hello_backend + assert_match "Status: Running." + + # Call + assert_command dfx canister call "$(dfx canister id hello_backend)" greet '("Names are difficult")' + assert_match '("Hello, Names are difficult!")' + + # Id + assert_command dfx canister id hello_backend + assert_match "$(jq -r .hello_backend.local < .dfx/local/canister_ids.json)" + x="$(dfx canister id hello_backend)" + local old_id="$x" + + # Delete + assert_command_fail dfx canister delete hello_backend + assert_command dfx canister stop hello_backend + assert_command dfx canister delete hello_backend + assert_command_fail dfx canister status hello_backend + assert_match "Cannot find canister id. Please issue 'dfx canister create hello_backend'." + + # Create again + assert_command dfx canister create hello_backend + assert_command dfx canister id hello_backend + assert_neq "$old_id" } diff --git a/e2e/utils/_.bash b/e2e/utils/_.bash index 7dd3d57aa3..391305a7ce 100644 --- a/e2e/utils/_.bash +++ b/e2e/utils/_.bash @@ -189,68 +189,6 @@ wait_until_replica_healthy() { echo "replica became healthy" } -# Start the replica in the background. -dfx_replica() { - local replica_port dfx_config_root - dfx_patchelf - determine_network_directory - - # Bats creates a FD 3 for test output, but child processes inherit it and Bats will - # wait for it to close. Because `dfx start` leaves child processes running, we need - # to close this pipe, otherwise Bats will wait indefinitely. - dfx replica --port 0 "$@" 3>&- & - export DFX_REPLICA_PID=$! - - timeout 60 sh -c \ - "until test -s \"$E2E_NETWORK_DATA_DIRECTORY/replica-configuration/replica-1.port\"; do echo waiting for replica port; sleep 1; done" \ - || (echo "replica did not write to port file" && exit 1) - - dfx_config_root="$E2E_NETWORK_DATA_DIRECTORY/replica-configuration" - test -f "${dfx_config_root}/replica-1.port" - replica_port=$(cat "${dfx_config_root}/replica-1.port") - - printf "Replica Configured Port: %s\n" "${replica_port}" - - timeout 5 sh -c \ - "until nc -z localhost ${replica_port}; do echo waiting for replica; sleep 1; done" \ - || (echo "could not connect to replica on port ${replica_port}" && exit 1) - - # ping the replica directly, because the bootstrap (that launches icx-proxy, which dfx ping usually connects to) - # is not running yet - dfx ping --wait-healthy "http://127.0.0.1:${replica_port}" -} - -dfx_bootstrap() { - # This only works because we use the network by name - # (implicitly: --network local) - # If we passed --network http://127.0.0.1:${replica_port} - # we would get errors like this: - # "Cannot find canister ryjl3-tyaaa-aaaaa-aaaba-cai for network http___127_0_0_1_54084" - dfx bootstrap --port 0 3>&- & - export DFX_BOOTSTRAP_PID=$! - - timeout 5 sh -c \ - "until nc -z localhost \$(cat \"$E2E_NETWORK_DATA_DIRECTORY/webserver-port\"); do echo waiting for webserver; sleep 1; done" \ - || (echo "could not connect to webserver on port $(get_webserver_port)" && exit 1) - - wait_until_replica_healthy - - webserver_port=$(cat "$E2E_NETWORK_DATA_DIRECTORY/webserver-port") - printf "Webserver Configured Port: %s\n", "${webserver_port}" -} - -# Stop the `dfx replica` process that is running in the background. -stop_dfx_replica() { - [ "$DFX_REPLICA_PID" ] && kill -TERM "$DFX_REPLICA_PID" - unset DFX_REPLICA_PID -} - -# Stop the `dfx bootstrap` process that is running in the background -stop_dfx_bootstrap() { - [ "$DFX_BOOTSTRAP_PID" ] && kill -TERM "$DFX_BOOTSTRAP_PID" - unset DFX_BOOTSTRAP_PID -} - # Stop the replica and verify it is very very stopped. dfx_stop() { # to help tell if other icx-proxy processes are from this test: diff --git a/nix/sources.json b/nix/sources.json index 188ec71182..14c77ab188 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -6,62 +6,62 @@ "homepage": "https://rustsec.org", "owner": "RustSec", "repo": "advisory-db", - "rev": "12719bd23b88573977c7ad7fe818b05e8fbc33b3", - "sha256": "14j5q3973wxrzxb2m1zxizrpl6k1zdmpcmlcwskq3bimykq7f5g6", + "rev": "9b6403d856f71291aabb91511b53fa79c2758745", + "sha256": "1b839a6bja6x1660737yfv7b7qfldcjd36g2s3a1v74q3f6zv5nw", "type": "tarball", - "url": "https://github.com/RustSec/advisory-db/archive/12719bd23b88573977c7ad7fe818b05e8fbc33b3.tar.gz", + "url": "https://github.com/RustSec/advisory-db/archive/9b6403d856f71291aabb91511b53fa79c2758745.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "canister_sandbox-x86_64-darwin": { "builtin": false, "description": "The canister_sandbox binary. It must be updated together with the replica binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0fhzrw051y8ac9qsc2n2g3bihfk3vkynrb3p6rysmp3mci8q5vm7", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "06dqva0my0hqraxwj5nqql58j3yry0vnjb8a37syjkz1fvjg78yg", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/canister_sandbox.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/canister_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/canister_sandbox.gz" }, "canister_sandbox-x86_64-linux": { "builtin": false, "description": "The canister_sandbox binary. It must be updated together with the replica binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "1aym92nlmi4yj18ww27jzkw7d3zpjd8g1dbkgj9dmjwz8m861j5x", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0n7axa0ymsj0nkd9iy3vj93b08q682f3r7i2bzg2bc4y96pvqjkv", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/canister_sandbox.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/canister_sandbox.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/canister_sandbox.gz" }, "ic-admin-x86_64-darwin": { "builtin": false, "description": "The ic-admin binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0ld705bg4xn82rn6pxhyijqkbkwdx9ylg2l8rwniiaaz19kdyfwp", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1l0xh0f8pw8qmvqdcbssps71i6qinpmicva5yw24dw6ycl66rnlw", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-admin.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-admin.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/ic-admin.gz" }, "ic-admin-x86_64-linux": { "builtin": false, "description": "The ic-admin binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "10qzyza0sp6lijlvkjphambmky8131avjfvsjsa16iqg0lla4fsz", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0dh9w6h9cv6ygqxv1b14vlpw32c0mlvf9x1kjihfd449xcbf4rpm", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-admin.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-admin.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/ic-admin.gz" }, "ic-btc-adapter-x86_64-darwin": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "04brndn2f26kkc2sqwc70kv6bprmgs0hcjxg6v3xgbby5d6ym20i", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1hd1vlgca2sy37gcafna9l4a1jmv9ndvdzk63084wsf0d8g8agx6", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-btc-adapter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-btc-adapter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/ic-btc-adapter.gz" }, "ic-btc-adapter-x86_64-linux": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "1hs03x9vxq00skwd6va604wh994wbc599j6h1yg5ridvivd4x795", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1k0vq2nbm3zjpnhm175h733aidgk7rm34zs8v2cw4f35h4x9n9n6", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-btc-adapter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-btc-adapter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/ic-btc-adapter.gz" }, "ic-btc-canister": { @@ -74,36 +74,36 @@ }, "ic-https-outcalls-adapter-x86_64-darwin": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "071gln7fwj558xxg0nk1gbkc3py5wych82qw8gm083j4wjhc3lnz", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1h0s6gw81f50kr93vnrgpylfq4nzm7ini6f90pbmbfh4r50iawyb", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-https-outcalls-adapter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-https-outcalls-adapter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/ic-https-outcalls-adapter.gz" }, "ic-https-outcalls-adapter-x86_64-linux": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0szk9cy030f99722sry5lpk0lk3cb05aygmsp2w1dx8y2jy1f801", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1cbjimj3jiy1y427mznwzasv019a68igki0c3nwq7crlm48xbn6n", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-https-outcalls-adapter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-https-outcalls-adapter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/ic-https-outcalls-adapter.gz" }, "ic-nns-init-x86_64-darwin": { "builtin": false, "description": "The ic-nns-init binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0fwbxw8sxpn6vl9090s880hmwwzqqh4lffglkik0hmgk9g0kgnlq", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0r7dy3wa96nf1f1r7dcsp1bhk1si0az6b6mvgj7icni213mf23ag", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-nns-init.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-nns-init.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/ic-nns-init.gz" }, "ic-nns-init-x86_64-linux": { "builtin": false, "description": "The ic-nns-init binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "11n1fpdsq4f06ws248mx0kjyb5hqgdd043df61bpm8z9fwvlz8gg", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "1zh39j3mlqgk0axkpppjzfqb406gmfb5yxi0jvn5xps01cfvsy5i", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-nns-init.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-nns-init.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/ic-nns-init.gz" }, "ic-ref-x86_64-darwin": { @@ -126,37 +126,35 @@ }, "ic-starter-x86_64-darwin": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0mg6gxmlqzpvz8w26ay281dh7pcldnmi7v7p1n8cbwapnj6q5igj", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0vf29dznkba48xjpacanrniqz03aj805k6kgc73inagkn2flmz9n", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-starter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-starter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/ic-starter.gz" }, "ic-starter-x86_64-linux": { "builtin": false, - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0r43igqm4xbgwrfxzpy1hfgzm4vh9bz99grgz7hwx7q6z1mmf6wx", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0dnhb3hgncn9m4430g78pz2yblqmhpnq2z2piqlkjrcb68ssm7b6", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-starter.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-starter.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/ic-starter.gz" }, "icx-proxy-x86_64-darwin": { "builtin": false, - "rev": "594b6c81cde6da4e08faee8aa8e5a2e6ae815602", - "sha256": "09rxh6kjwy7qfsvvsy6xjzyn4r4zlb78k1qipi4k3x0w0ajvp0sp", - "tag": "rev-c312760", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "09y3q0ri294qkfp5bywpm37f7c7gd44xq8a2fx5lafzfd98xb6v1", "type": "file", - "url": "https://github.com/dfinity/icx-proxy/releases/download/rev-c312760/binaries-macos.tar.gz", - "url_template": "https://github.com/dfinity/icx-proxy/releases/download//binaries-macos.tar.gz" + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/icx-proxy-dev.gz", + "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/icx-proxy-dev.gz" }, "icx-proxy-x86_64-linux": { "builtin": false, - "rev": "594b6c81cde6da4e08faee8aa8e5a2e6ae815602", - "sha256": "18czg11v5hiczqrahr962wmjig3gcafplqiprlnx44kmzfhi4mks", - "tag": "rev-c312760", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0h8k4hf42grjhhs6nnl8gcb9k02cs4dm1a25mjlkf6vi3f6sx307", "type": "file", - "url": "https://github.com/dfinity/icx-proxy/releases/download/rev-c312760/binaries-linux.tar.gz", - "url_template": "https://github.com/dfinity/icx-proxy/releases/download//binaries-linux.tar.gz" + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/icx-proxy-dev.gz", + "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/icx-proxy-dev.gz" }, "motoko-base": { "builtin": false, @@ -187,55 +185,55 @@ "replica-x86_64-darwin": { "builtin": false, "description": "The replica binary. It must be updated together with the canister_sandbox binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "05glmd38bb78lvmlyvwikxwyjlqxcnjrpsjkghf2mxsqndmsramn", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "08sh0rfj8ykmvr74a4hzm3j4kbhrz5d7c3vachsm2jla9dhvd6rl", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/replica.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/replica.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/replica.gz" }, "replica-x86_64-linux": { "builtin": false, "description": "The replica binary. It must be updated together with the canister_sandbox binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "1g5xfs2mfvca0havr8c1cmki6fpqq5ajsqacly0dz44pj25xkvgx", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0sbzdp9ydmv8lv055gw04gvr6lz3li7ibfnz7f1833qvcqwjacjv", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/replica.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/replica.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/replica.gz" }, "sandbox_launcher-x86_64-darwin": { "builtin": false, "description": "The sandbox_launcher binary. It must be updated together with the replica binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0lavk9j6hydas4vs5yay6949z64r93j04sjjgdph7f8llavp8y3k", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "19ma4n918bq7z9avlaazfdrq94xvsdl153kwmaysxw19gz4f01a0", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/sandbox_launcher.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/sandbox_launcher.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/sandbox_launcher.gz" }, "sandbox_launcher-x86_64-linux": { "builtin": false, "description": "The sandbox_launcher binary. It must be updated together with the replica binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "1pqfcyakgd14gj0n125j5ldayg2b6z1x3as0pww12wfdadar6qxw", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "10qq77zwbyas65y2msa55pjhiz24pi9ghhmg6gqldx96hf6z6xkn", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/sandbox_launcher.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/sandbox_launcher.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/sandbox_launcher.gz" }, "sns-x86_64-darwin": { "builtin": false, "description": "The sns binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "00yxyj52yya5pczxw37x86rsynd5jb95glfh26l8n38rg5fih1q1", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "0pm8ihnf4hyn9jsnb299lxlv2w8nix2zy7ax93z4wvzi2ngksfi4", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/sns.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/sns.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-darwin/sns.gz" }, "sns-x86_64-linux": { "builtin": false, "description": "The sns binary.", - "rev": "cabe2ae3ca115b1a3f24d75814d4f8e317b2964d", - "sha256": "0l7ibq8px4bqzrj224xy56b9l6rp3rbmfwgd47izmpaa6lg9s1ps", + "rev": "91bf38ff3cb927cb94027d9da513cd15f91a5b04", + "sha256": "151342zhm4hn9v1h4c9h6hdirjsx5g9ghnqf6k0zlxg6cxl2l1ky", "type": "file", - "url": "https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/sns.gz", + "url": "https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/sns.gz", "url_template": "https://download.dfinity.systems/ic//openssl-static-binaries/x86_64-linux/sns.gz" } } diff --git a/public/manifest.json b/public/manifest.json index 1816a4231c..861b203a03 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { "tags": { - "latest": "0.14.4" + "latest": "0.15.0" }, "versions": [ "0.5.0", diff --git a/scripts/update-replica.sh b/scripts/update-replica.sh index 2df26a8e1f..ed1319faf7 100755 --- a/scripts/update-replica.sh +++ b/scripts/update-replica.sh @@ -20,6 +20,8 @@ niv update ic-nns-init-x86_64-darwin -a rev=$SHA niv update ic-nns-init-x86_64-linux -a rev=$SHA niv update ic-starter-x86_64-darwin -a rev=$SHA niv update ic-starter-x86_64-linux -a rev=$SHA +niv update icx-proxy-x86_64-darwin -a rev=$SHA +niv update icx-proxy-x86_64-linux -a rev=$SHA niv update replica-x86_64-darwin -a rev=$SHA niv update replica-x86_64-linux -a rev=$SHA niv update canister_sandbox-x86_64-darwin -a rev=$SHA diff --git a/scripts/workflows/e2e-matrix.py b/scripts/workflows/e2e-matrix.py index f079c925dd..ecde27fa60 100755 --- a/scripts/workflows/e2e-matrix.py +++ b/scripts/workflows/e2e-matrix.py @@ -16,17 +16,7 @@ def test_scripts(prefix): matrix = { "test": ["dfx/ledger"], "backend": ["replica"], - "os": ["macos-12", "ubuntu-20.04"], - "exclude": [ - {"backend": "ic-ref", "test": "dfx/bitcoin"}, - {"backend": "ic-ref", "test": "dfx/canister_http"}, - {"backend": "ic-ref", "test": "dfx/dfx_install"}, - {"backend": "ic-ref", "test": "dfx/leak"}, - {"backend": "ic-ref", "test": "dfx/ledger"}, - {"backend": "ic-ref", "test": "dfx/new"}, - {"backend": "ic-ref", "test": "dfx/print"}, - {"backend": "ic-ref", "test": "dfx/signals"}, - ], + "os": ["macos-12", "ubuntu-20.04"] } print(json.dumps(matrix)) diff --git a/src/canisters/frontend/ic-asset/src/asset/config.rs b/src/canisters/frontend/ic-asset/src/asset/config.rs index 19b0dbace4..68a883dde3 100644 --- a/src/canisters/frontend/ic-asset/src/asset/config.rs +++ b/src/canisters/frontend/ic-asset/src/asset/config.rs @@ -23,7 +23,7 @@ pub struct AssetConfig { pub(crate) headers: Option, pub(crate) ignore: Option, pub(crate) enable_aliasing: Option, - #[derivative(Default(value = "Some(false)"))] + #[derivative(Default(value = "Some(true)"))] pub(crate) allow_raw_access: Option, } @@ -35,7 +35,7 @@ pub(crate) struct CacheConfig { } fn default_raw_access() -> Option { - Some(false) + Some(true) } /// A single configuration object, from `.ic-assets.json` config file @@ -931,7 +931,7 @@ mod with_tempdir { .get_asset_config(assets_dir.join("index.html").as_path()) .unwrap(), AssetConfig { - allow_raw_access: Some(false), + allow_raw_access: Some(true), ..Default::default() }, ); diff --git a/src/canisters/frontend/ic-certified-assets/src/state_machine.rs b/src/canisters/frontend/ic-certified-assets/src/state_machine.rs index 01a9b3f697..4bcc82b90a 100644 --- a/src/canisters/frontend/ic-certified-assets/src/state_machine.rs +++ b/src/canisters/frontend/ic-certified-assets/src/state_machine.rs @@ -243,7 +243,7 @@ pub struct StableState { impl Asset { fn allow_raw_access(&self) -> bool { - self.allow_raw_access.unwrap_or(false) + self.allow_raw_access.unwrap_or(true) } fn update_ic_certificate_expressions(&mut self) { diff --git a/src/canisters/frontend/ic-certified-assets/src/tests.rs b/src/canisters/frontend/ic-certified-assets/src/tests.rs index 6e1f5e4e07..3afe42767f 100644 --- a/src/canisters/frontend/ic-certified-assets/src/tests.rs +++ b/src/canisters/frontend/ic-certified-assets/src/tests.rs @@ -1601,7 +1601,9 @@ mod allow_raw_access { "https://a-b-c.ic0.app/page" ); - state.create_test_asset(AssetBuilder::new("/page2.html", "text/html")); + state.create_test_asset( + AssetBuilder::new("/page2.html", "text/html").with_allow_raw_access(Some(false)), + ); let response = state.fake_http_request("a-b-c.raw.icp0.io", "/page2"); dbg!(&response); assert_eq!(response.status_code, 308); @@ -1610,7 +1612,9 @@ mod allow_raw_access { "https://a-b-c.icp0.io/page2" ); - state.create_test_asset(AssetBuilder::new("/index.html", "text/html")); + state.create_test_asset( + AssetBuilder::new("/index.html", "text/html").with_allow_raw_access(Some(false)), + ); let response = state.fake_http_request("a-b-c.raw.icp0.io", "/"); dbg!(&response); assert_eq!(response.status_code, 308); diff --git a/src/dfx-core/src/config/directories.rs b/src/dfx-core/src/config/directories.rs index 8553de3ae6..f37e0e2ec7 100644 --- a/src/dfx-core/src/config/directories.rs +++ b/src/dfx-core/src/config/directories.rs @@ -3,15 +3,15 @@ use crate::error::config::ConfigError::{ DetermineConfigDirectoryFailed, DetermineSharedNetworkDirectoryFailed, EnsureConfigDirectoryExistsFailed, }; -use crate::error::foundation::FoundationError; -use crate::error::foundation::FoundationError::NoHomeInEnvironment; +use crate::error::get_user_home::GetUserHomeError; +use crate::error::get_user_home::GetUserHomeError::NoHomeInEnvironment; #[cfg(not(windows))] use crate::foundation::get_user_home; use crate::fs::composite::ensure_dir_exists; use directories_next::ProjectDirs; use std::path::PathBuf; -pub fn project_dirs() -> Result<&'static ProjectDirs, FoundationError> { +pub fn project_dirs() -> Result<&'static ProjectDirs, GetUserHomeError> { lazy_static::lazy_static! { static ref DIRS: Option = ProjectDirs::from("org", "dfinity", "dfx"); } @@ -23,7 +23,7 @@ pub fn get_shared_network_data_directory(network: &str) -> Result Result { +pub fn get_user_dfx_config_dir() -> Result { let config_root = std::env::var_os("DFX_CONFIG_ROOT"); // dirs-next is not used for *nix to preserve existing paths #[cfg(not(windows))] diff --git a/src/dfx-core/src/config/model/dfinity.rs b/src/dfx-core/src/config/model/dfinity.rs index 22ba2ad573..e67d9a7c38 100644 --- a/src/dfx-core/src/config/model/dfinity.rs +++ b/src/dfx-core/src/config/model/dfinity.rs @@ -1,13 +1,20 @@ #![allow(dead_code)] #![allow(clippy::should_implement_trait)] // for from_str. why now? -use crate::config::directories::get_config_dfx_dir_path; +use crate::config::directories::get_user_dfx_config_dir; use crate::config::model::bitcoin_adapter::BitcoinAdapterLogLevel; use crate::config::model::canister_http_adapter::HttpAdapterLogLevel; -use crate::error::dfx_config::DfxConfigError; -use crate::error::dfx_config::DfxConfigError::{ - CanisterCircularDependency, CanisterNotFound, CanistersFieldDoesNotExist, - GetCanistersWithDependenciesFailed, GetComputeAllocationFailed, GetFreezingThresholdFailed, - GetMemoryAllocationFailed, GetRemoteCanisterIdFailed, PullCanistersSameId, +use crate::error::dfx_config::AddDependenciesError::CanisterCircularDependency; +use crate::error::dfx_config::GetCanisterNamesWithDependenciesError::AddDependenciesFailed; +use crate::error::dfx_config::GetComputeAllocationError::GetComputeAllocationFailed; +use crate::error::dfx_config::GetFreezingThresholdError::GetFreezingThresholdFailed; +use crate::error::dfx_config::GetMemoryAllocationError::GetMemoryAllocationFailed; +use crate::error::dfx_config::GetPullCanistersError::PullCanistersSameId; +use crate::error::dfx_config::GetRemoteCanisterIdError::GetRemoteCanisterIdFailed; +use crate::error::dfx_config::GetReservedCyclesLimitError::GetReservedCyclesLimitFailed; +use crate::error::dfx_config::{ + AddDependenciesError, GetCanisterConfigError, GetCanisterNamesWithDependenciesError, + GetComputeAllocationError, GetFreezingThresholdError, GetMemoryAllocationError, + GetPullCanistersError, GetRemoteCanisterIdError, GetReservedCyclesLimitError, }; use crate::error::load_dfx_config::LoadDfxConfigError; use crate::error::load_dfx_config::LoadDfxConfigError::{ @@ -341,6 +348,19 @@ pub struct InitializationValues { #[serde(with = "humantime_serde")] #[schemars(with = "Option")] pub freezing_threshold: Option, + + /// # Reserved Cycles Limit + /// Specifies the upper limit of the canister's reserved cycles balance. + /// + /// Reserved cycles are cycles that the system sets aside for future use by the canister. + /// If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, + /// the system sets aside some amount of cycles from the main balance of the canister. + /// These reserved cycles will be used to cover future payments for the newly allocated bytes. + /// The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is. + /// + /// A setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB. + #[schemars(with = "Option")] + pub reserved_cycles_limit: Option, } /// # Declarations Configuration @@ -441,6 +461,7 @@ fn default_as_true() -> bool { } /// # Bootstrap Server Configuration +/// The bootstrap command has been removed. All of these fields are ignored. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct ConfigDefaultsBootstrap { /// Specifies the IP address that the bootstrap server listens on. Defaults to 127.0.0.1. @@ -749,36 +770,34 @@ impl ConfigInterface { pub fn get_canister_names_with_dependencies( &self, some_canister: Option<&str>, - ) -> Result, DfxConfigError> { + ) -> Result, GetCanisterNamesWithDependenciesError> { self.canisters .as_ref() - .ok_or(CanistersFieldDoesNotExist()) + .ok_or(GetCanisterNamesWithDependenciesError::CanistersFieldDoesNotExist()) .and_then(|canister_map| match some_canister { Some(specific_canister) => { let mut names = HashSet::new(); let mut path = vec![]; add_dependencies(canister_map, &mut names, &mut path, specific_canister) .map(|_| names.into_iter().collect()) + .map_err(|err| AddDependenciesFailed(specific_canister.to_string(), err)) } None => Ok(canister_map.keys().cloned().collect()), }) - .map_err(|cause| { - GetCanistersWithDependenciesFailed(some_canister.map(String::from), Box::new(cause)) - }) } pub fn get_remote_canister_id( &self, canister: &str, network: &str, - ) -> Result, DfxConfigError> { + ) -> Result, GetRemoteCanisterIdError> { let maybe_principal = self .get_canister_config(canister) .map_err(|e| { GetRemoteCanisterIdFailed( Box::new(canister.to_string()), Box::new(network.to_string()), - Box::new(e), + e, ) })? .remote @@ -793,17 +812,17 @@ impl ConfigInterface { &self, canister: &str, network: &str, - ) -> Result { + ) -> Result { Ok(self.get_remote_canister_id(canister, network)?.is_some()) } pub fn get_compute_allocation( &self, canister_name: &str, - ) -> Result, DfxConfigError> { + ) -> Result, GetComputeAllocationError> { Ok(self .get_canister_config(canister_name) - .map_err(|e| GetComputeAllocationFailed(canister_name.to_string(), Box::new(e)))? + .map_err(|e| GetComputeAllocationFailed(canister_name.to_string(), e))? .initialization_values .compute_allocation .map(|x| x.0)) @@ -812,10 +831,10 @@ impl ConfigInterface { pub fn get_memory_allocation( &self, canister_name: &str, - ) -> Result, DfxConfigError> { + ) -> Result, GetMemoryAllocationError> { Ok(self .get_canister_config(canister_name) - .map_err(|e| GetMemoryAllocationFailed(canister_name.to_string(), Box::new(e)))? + .map_err(|e| GetMemoryAllocationFailed(canister_name.to_string(), e))? .initialization_values .memory_allocation) } @@ -823,26 +842,37 @@ impl ConfigInterface { pub fn get_freezing_threshold( &self, canister_name: &str, - ) -> Result, DfxConfigError> { + ) -> Result, GetFreezingThresholdError> { Ok(self .get_canister_config(canister_name) - .map_err(|e| GetFreezingThresholdFailed(canister_name.to_string(), Box::new(e)))? + .map_err(|e| GetFreezingThresholdFailed(canister_name.to_string(), e))? .initialization_values .freezing_threshold) } + pub fn get_reserved_cycles_limit( + &self, + canister_name: &str, + ) -> Result, GetReservedCyclesLimitError> { + Ok(self + .get_canister_config(canister_name) + .map_err(|e| GetReservedCyclesLimitFailed(canister_name.to_string(), e))? + .initialization_values + .reserved_cycles_limit) + } + fn get_canister_config( &self, canister_name: &str, - ) -> Result<&ConfigCanistersCanister, DfxConfigError> { + ) -> Result<&ConfigCanistersCanister, GetCanisterConfigError> { self.canisters .as_ref() - .ok_or(CanistersFieldDoesNotExist())? + .ok_or(GetCanisterConfigError::CanistersFieldDoesNotExist())? .get(canister_name) - .ok_or_else(|| CanisterNotFound(canister_name.to_string())) + .ok_or_else(|| GetCanisterConfigError::CanisterNotFound(canister_name.to_string())) } - pub fn get_pull_canisters(&self) -> Result, DfxConfigError> { + pub fn get_pull_canisters(&self) -> Result, GetPullCanistersError> { let mut res = BTreeMap::new(); let mut id_to_name: BTreeMap = BTreeMap::new(); if let Some(map) = &self.canisters { @@ -865,7 +895,7 @@ fn add_dependencies( names: &mut HashSet, path: &mut Vec, canister_name: &str, -) -> Result<(), DfxConfigError> { +) -> Result<(), AddDependenciesError> { let inserted = names.insert(String::from(canister_name)); if !inserted { @@ -879,7 +909,7 @@ fn add_dependencies( let canister_config = all_canisters .get(canister_name) - .ok_or_else(|| CanisterNotFound(canister_name.to_string()))?; + .ok_or_else(|| AddDependenciesError::CanisterNotFound(canister_name.to_string()))?; path.push(String::from(canister_name)); @@ -1075,7 +1105,7 @@ impl NetworksConfig { } pub fn new() -> Result { - let dir = get_config_dfx_dir_path().map_err(GetConfigPathFailed)?; + let dir = get_user_dfx_config_dir().map_err(GetConfigPathFailed)?; let path = dir.join("networks.json"); if path.exists() { diff --git a/src/dfx-core/src/config/model/local_server_descriptor.rs b/src/dfx-core/src/config/model/local_server_descriptor.rs index b7e8db559d..ff4403247c 100644 --- a/src/dfx-core/src/config/model/local_server_descriptor.rs +++ b/src/dfx-core/src/config/model/local_server_descriptor.rs @@ -1,15 +1,14 @@ use crate::config::model::bitcoin_adapter; use crate::config::model::canister_http_adapter::HttpAdapterLogLevel; use crate::config::model::dfinity::{ - to_socket_addr, ConfigDefaultsBitcoin, ConfigDefaultsBootstrap, ConfigDefaultsCanisterHttp, - ConfigDefaultsReplica, ReplicaLogLevel, ReplicaSubnetType, DEFAULT_PROJECT_LOCAL_BIND, - DEFAULT_SHARED_LOCAL_BIND, + to_socket_addr, ConfigDefaultsBitcoin, ConfigDefaultsCanisterHttp, ConfigDefaultsReplica, + ReplicaLogLevel, ReplicaSubnetType, DEFAULT_PROJECT_LOCAL_BIND, DEFAULT_SHARED_LOCAL_BIND, }; use crate::error::network_config::{ NetworkConfigError, NetworkConfigError::ParseBindAddressFailed, }; use slog::{debug, info, Logger}; -use std::net::{IpAddr, SocketAddr}; +use std::net::SocketAddr; use std::path::{Path, PathBuf}; #[derive(Clone, Debug, PartialEq, Eq)] @@ -30,7 +29,6 @@ pub struct LocalServerDescriptor { pub bind_address: SocketAddr, pub bitcoin: ConfigDefaultsBitcoin, - pub bootstrap: ConfigDefaultsBootstrap, pub canister_http: ConfigDefaultsCanisterHttp, pub replica: ConfigDefaultsReplica, @@ -52,7 +50,6 @@ impl LocalServerDescriptor { data_directory: PathBuf, bind: String, bitcoin: ConfigDefaultsBitcoin, - bootstrap: ConfigDefaultsBootstrap, canister_http: ConfigDefaultsCanisterHttp, replica: ConfigDefaultsReplica, scope: LocalNetworkScopeDescriptor, @@ -63,7 +60,6 @@ impl LocalServerDescriptor { data_directory, bind_address, bitcoin, - bootstrap, canister_http, replica, scope, @@ -203,30 +199,6 @@ impl LocalServerDescriptor { }; Self { bitcoin, ..self } } - - pub fn with_bootstrap_ip(self, ip: IpAddr) -> LocalServerDescriptor { - let bootstrap = ConfigDefaultsBootstrap { - ip, - ..self.bootstrap - }; - Self { bootstrap, ..self } - } - - pub fn with_bootstrap_port(self, port: u16) -> LocalServerDescriptor { - let bootstrap = ConfigDefaultsBootstrap { - port, - ..self.bootstrap - }; - Self { bootstrap, ..self } - } - - pub fn with_bootstrap_timeout(self, timeout: u64) -> LocalServerDescriptor { - let bootstrap = ConfigDefaultsBootstrap { - timeout, - ..self.bootstrap - }; - Self { bootstrap, ..self } - } } impl LocalServerDescriptor { @@ -310,31 +282,6 @@ impl LocalServerDescriptor { debug!(log, ""); } - pub fn describe_bootstrap(&self, log: &Logger) { - debug!(log, "Bootstrap configuration:"); - let default: ConfigDefaultsBootstrap = Default::default(); - let diffs = if self.bootstrap.ip != default.ip { - format!(" (default: {:?})", default.ip) - } else { - "".to_string() - }; - debug!(log, " ip: {:?}{}", self.bootstrap.ip, diffs); - - let diffs = if self.bootstrap.port != default.port { - format!(" (default: {})", default.port) - } else { - "".to_string() - }; - debug!(log, " port: {}{}", self.bootstrap.port, diffs); - - let diffs = if self.bootstrap.timeout != default.timeout { - format!(" (default: {})", default.timeout) - } else { - "".to_string() - }; - debug!(log, " timeout: {}{}", self.bootstrap.timeout, diffs); - debug!(log, ""); - } /// Gets the port of a local replica. /// /// # Prerequisites diff --git a/src/dfx-core/src/config/model/network_descriptor.rs b/src/dfx-core/src/config/model/network_descriptor.rs index f5922b7bb7..73c2e217a8 100644 --- a/src/dfx-core/src/config/model/network_descriptor.rs +++ b/src/dfx-core/src/config/model/network_descriptor.rs @@ -10,7 +10,7 @@ use slog::Logger; use std::path::{Path, PathBuf}; use url::Url; -const MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID: Principal = +pub const MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID: Principal = Principal::from_slice(&[0, 0, 0, 0, 0, 48, 0, 97, 1, 1]); pub const PLAYGROUND_NETWORK_NAME: &str = "playground"; pub const MOTOKO_PLAYGROUND_CANISTER_TIMEOUT_SECONDS: u64 = 1200; diff --git a/src/dfx-core/src/error/cache.rs b/src/dfx-core/src/error/cache.rs index 6b2f3a099c..ef24108060 100644 --- a/src/dfx-core/src/error/cache.rs +++ b/src/dfx-core/src/error/cache.rs @@ -2,12 +2,17 @@ use super::{ archive::ArchiveError, fs::FsError, structured_file::StructuredFileError, unified_io::UnifiedIoError, }; +use crate::error::get_current_exe::GetCurrentExeError; +use crate::error::get_user_home::GetUserHomeError; use thiserror::Error; #[derive(Error, Debug)] pub enum CacheError { #[error(transparent)] - FoundationError(#[from] crate::error::foundation::FoundationError), + GetCurrentExeError(#[from] GetCurrentExeError), + + #[error(transparent)] + GetUserHomeError(#[from] GetUserHomeError), #[error(transparent)] UnifiedIo(#[from] crate::error::unified_io::UnifiedIoError), diff --git a/src/dfx-core/src/error/canister_id_store.rs b/src/dfx-core/src/error/canister_id_store.rs index 835b84a6ae..3ebc307149 100644 --- a/src/dfx-core/src/error/canister_id_store.rs +++ b/src/dfx-core/src/error/canister_id_store.rs @@ -1,5 +1,5 @@ use crate::error::{ - dfx_config::DfxConfigError, fs::FsError, structured_file::StructuredFileError, + dfx_config::GetPullCanistersError, fs::FsError, structured_file::StructuredFileError, unified_io::UnifiedIoError, }; use thiserror::Error; @@ -37,7 +37,7 @@ pub enum CanisterIdStoreError { }, #[error(transparent)] - DfxConfigError(#[from] DfxConfigError), + GetPullCanistersFailed(#[from] GetPullCanistersError), } impl From for CanisterIdStoreError { diff --git a/src/dfx-core/src/error/config.rs b/src/dfx-core/src/error/config.rs index 8ca2312784..1121332e42 100644 --- a/src/dfx-core/src/error/config.rs +++ b/src/dfx-core/src/error/config.rs @@ -1,5 +1,5 @@ -use crate::error::foundation::FoundationError; use crate::error::fs::FsError; +use crate::error::get_user_home::GetUserHomeError; use thiserror::Error; #[derive(Error, Debug)] @@ -8,8 +8,8 @@ pub enum ConfigError { EnsureConfigDirectoryExistsFailed(FsError), #[error("Failed to determine config directory path: {0}")] - DetermineConfigDirectoryFailed(FoundationError), + DetermineConfigDirectoryFailed(GetUserHomeError), #[error("Failed to determine shared network data directory: {0}")] - DetermineSharedNetworkDirectoryFailed(FoundationError), + DetermineSharedNetworkDirectoryFailed(GetUserHomeError), } diff --git a/src/dfx-core/src/error/dfx_config.rs b/src/dfx-core/src/error/dfx_config.rs index 02bf5c93ad..943484f11b 100644 --- a/src/dfx-core/src/error/dfx_config.rs +++ b/src/dfx-core/src/error/dfx_config.rs @@ -2,31 +2,64 @@ use candid::Principal; use thiserror::Error; #[derive(Error, Debug)] -pub enum DfxConfigError { +pub enum AddDependenciesError { #[error("Circular canister dependencies: {}", _0.join(" -> "))] CanisterCircularDependency(Vec), #[error("Canister '{0}' not found in dfx.json.")] CanisterNotFound(String), +} + +#[derive(Error, Debug)] +pub enum GetCanisterConfigError { + #[error("No canisters in the configuration file.")] + CanistersFieldDoesNotExist(), + + #[error("Canister '{0}' not found in dfx.json.")] + CanisterNotFound(String), +} +#[derive(Error, Debug)] +pub enum GetCanisterNamesWithDependenciesError { #[error("No canisters in the configuration file.")] CanistersFieldDoesNotExist(), - #[error("Failed to get canisters with their dependencies (for {}): {1}", _0.as_deref().unwrap_or("all canisters"))] - GetCanistersWithDependenciesFailed(Option, Box), + #[error("Failed to add dependencies for canister '{0}': {1}")] + AddDependenciesFailed(String, AddDependenciesError), +} +#[derive(Error, Debug)] +pub enum GetComputeAllocationError { #[error("Failed to get compute allocation for canister '{0}': {1}")] - GetComputeAllocationFailed(String, Box), + GetComputeAllocationFailed(String, GetCanisterConfigError), +} +#[derive(Error, Debug)] +pub enum GetFreezingThresholdError { #[error("Failed to get freezing threshold for canister '{0}': {1}")] - GetFreezingThresholdFailed(String, Box), + GetFreezingThresholdFailed(String, GetCanisterConfigError), +} - #[error("Failed to get memory allocation for canister '{0}': {1}")] - GetMemoryAllocationFailed(String, Box), +#[derive(Error, Debug)] +pub enum GetReservedCyclesLimitError { + #[error("Failed to get reserved cycles limit for canister '{0}': {1}")] + GetReservedCyclesLimitFailed(String, GetCanisterConfigError), +} - #[error("Failed to figure out if canister '{0}' has a remote id on network '{1}': {2}")] - GetRemoteCanisterIdFailed(Box, Box, Box), +#[derive(Error, Debug)] +pub enum GetMemoryAllocationError { + #[error("Failed to get memory allocation for canister '{0}': {1}")] + GetMemoryAllocationFailed(String, GetCanisterConfigError), +} +#[derive(Error, Debug)] +pub enum GetPullCanistersError { #[error("Pull dependencies '{0}' and '{1}' have the same canister ID: {2}")] PullCanistersSameId(String, String, Principal), } + +#[derive(Error, Debug)] +pub enum GetRemoteCanisterIdError { + #[error("Failed to figure out if canister '{0}' has a remote id on network '{1}': {2}")] + GetRemoteCanisterIdFailed(Box, Box, GetCanisterConfigError), +} diff --git a/src/dfx-core/src/error/foundation.rs b/src/dfx-core/src/error/get_current_exe.rs similarity index 53% rename from src/dfx-core/src/error/foundation.rs rename to src/dfx-core/src/error/get_current_exe.rs index 0669640d2b..cf74d7d4a4 100644 --- a/src/dfx-core/src/error/foundation.rs +++ b/src/dfx-core/src/error/get_current_exe.rs @@ -1,10 +1,7 @@ use thiserror::Error; #[derive(Error, Debug)] -pub enum FoundationError { - #[error("Cannot find home directory (no HOME environment variable).")] - NoHomeInEnvironment(), - +pub enum GetCurrentExeError { #[error("Failed to identify currently running executable: {0}")] NoCurrentExe(std::io::Error), } diff --git a/src/dfx-core/src/error/get_user_home.rs b/src/dfx-core/src/error/get_user_home.rs new file mode 100644 index 0000000000..7ac0ca2fb5 --- /dev/null +++ b/src/dfx-core/src/error/get_user_home.rs @@ -0,0 +1,7 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum GetUserHomeError { + #[error("Cannot find home directory (no HOME environment variable).")] + NoHomeInEnvironment(), +} diff --git a/src/dfx-core/src/error/identity/call_sender_from_wallet.rs b/src/dfx-core/src/error/identity/call_sender_from_wallet.rs new file mode 100644 index 0000000000..38ce501692 --- /dev/null +++ b/src/dfx-core/src/error/identity/call_sender_from_wallet.rs @@ -0,0 +1,8 @@ +use candid::types::principal::PrincipalError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CallSenderFromWalletError { + #[error("Failed to read principal from id '{0}': {1}")] + ParsePrincipalFromIdFailed(String, PrincipalError), +} diff --git a/src/dfx-core/src/error/identity/create_identity_config.rs b/src/dfx-core/src/error/identity/create_identity_config.rs new file mode 100644 index 0000000000..2367238749 --- /dev/null +++ b/src/dfx-core/src/error/identity/create_identity_config.rs @@ -0,0 +1,8 @@ +use crate::error::encryption::EncryptionError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum CreateIdentityConfigError { + #[error("Failed to generate a fresh encryption configuration: {0}")] + GenerateFreshEncryptionConfigurationFailed(EncryptionError), +} diff --git a/src/dfx-core/src/error/identity/create_new_identity.rs b/src/dfx-core/src/error/identity/create_new_identity.rs index 1e7076ee0b..1d1f242a33 100644 --- a/src/dfx-core/src/error/identity/create_new_identity.rs +++ b/src/dfx-core/src/error/identity/create_new_identity.rs @@ -1,8 +1,13 @@ use crate::error::fs::FsError; use crate::error::identity::convert_mnemonic_to_key::ConvertMnemonicToKeyError; +use crate::error::identity::create_identity_config::CreateIdentityConfigError; use crate::error::identity::generate_key::GenerateKeyError; +use crate::error::identity::load_pem_from_file::LoadPemFromFileError; +use crate::error::identity::remove_identity::RemoveIdentityError; +use crate::error::identity::save_identity_configuration::SaveIdentityConfigurationError; use crate::error::identity::save_pem::SavePemError; -use crate::error::identity::IdentityError; +use crate::error::identity::use_identity_by_name::UseIdentityByNameError; +use crate::error::identity::validate_pem_file::ValidatePemFileError; use thiserror::Error; #[derive(Error, Debug)] @@ -20,7 +25,7 @@ pub enum CreateNewIdentityError { ConvertSecretKeyToSec1PemFailed(Box), #[error("Failed to create identity config: {0}")] - CreateIdentityConfigFailed(IdentityError), + CreateIdentityConfigFailed(CreateIdentityConfigError), #[error("Failed to create mnemonic from phrase: {0}")] CreateMnemonicFromPhraseFailed(String), @@ -35,26 +40,26 @@ pub enum CreateNewIdentityError { IdentityAlreadyExists(), #[error("Failed to load pem file: {0}")] - LoadPemFromFileFailed(IdentityError), + LoadPemFromFileFailed(LoadPemFromFileError), #[error("Failed to remove identity: {0}")] - RemoveIdentityFailed(IdentityError), + RemoveIdentityFailed(RemoveIdentityError), #[error("Failed to rename temporary directory to permanent identity directory: {0}")] RenameTemporaryIdentityDirectoryFailed(FsError), #[error("Failed to save identity configuration: {0}")] - SaveIdentityConfigurationFailed(IdentityError), + SaveIdentityConfigurationFailed(SaveIdentityConfigurationError), #[error("Failed to save pem: {0}")] SavePemFailed(SavePemError), #[error("Failed to switch back over to the identity you're replacing: {0}")] - SwitchBackToIdentityFailed(IdentityError), + SwitchBackToIdentityFailed(UseIdentityByNameError), #[error("Failed to temporarily switch over to anonymous identity: {0}")] - SwitchToAnonymousIdentityFailed(IdentityError), + SwitchToAnonymousIdentityFailed(UseIdentityByNameError), #[error("Failed to validate pem file: {0}")] - ValidatePemFileFailed(IdentityError), + ValidatePemFileFailed(ValidatePemFileError), } diff --git a/src/dfx-core/src/error/identity/export_identity.rs b/src/dfx-core/src/error/identity/export_identity.rs index ff21d6e6cd..5a8f5c27ae 100644 --- a/src/dfx-core/src/error/identity/export_identity.rs +++ b/src/dfx-core/src/error/identity/export_identity.rs @@ -1,21 +1,24 @@ -use crate::error::identity::IdentityError; +use crate::error::identity::get_identity_config_or_default::GetIdentityConfigOrDefaultError; +use crate::error::identity::load_pem::LoadPemError; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use crate::error::identity::validate_pem_file::ValidatePemFileError; use std::string::FromUtf8Error; use thiserror::Error; #[derive(Error, Debug)] pub enum ExportIdentityError { #[error("Failed to get identity config: {0}")] - GetIdentityConfigFailed(IdentityError), + GetIdentityConfigFailed(GetIdentityConfigOrDefaultError), #[error("The specified identity does not exist: {0}")] - IdentityDoesNotExist(IdentityError), + IdentityDoesNotExist(RequireIdentityExistsError), #[error("Failed to load pem file: {0}")] - LoadPemFailed(IdentityError), + LoadPemFailed(LoadPemError), #[error("Could not translate pem file to text: {0}")] TranslatePemContentToTextFailed(FromUtf8Error), #[error("Failed to validate pem file: {0}")] - ValidatePemFileFailed(IdentityError), + ValidatePemFileFailed(ValidatePemFileError), } diff --git a/src/dfx-core/src/error/identity/get_identity_config_or_default.rs b/src/dfx-core/src/error/identity/get_identity_config_or_default.rs new file mode 100644 index 0000000000..d7cd9fe0fc --- /dev/null +++ b/src/dfx-core/src/error/identity/get_identity_config_or_default.rs @@ -0,0 +1,8 @@ +use crate::error::structured_file::StructuredFileError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum GetIdentityConfigOrDefaultError { + #[error("Failed to load configuration for identity '{0}': {1}")] + LoadIdentityConfigurationFailed(String, StructuredFileError), +} diff --git a/src/dfx-core/src/error/identity/get_legacy_credentials_pem_path.rs b/src/dfx-core/src/error/identity/get_legacy_credentials_pem_path.rs index 731c2a2edc..9d9ed36c84 100644 --- a/src/dfx-core/src/error/identity/get_legacy_credentials_pem_path.rs +++ b/src/dfx-core/src/error/identity/get_legacy_credentials_pem_path.rs @@ -1,8 +1,8 @@ -use crate::error::foundation::FoundationError; +use crate::error::get_user_home::GetUserHomeError; use thiserror::Error; #[derive(Error, Debug)] pub enum GetLegacyCredentialsPemPathError { #[error("Failed to get legacy pem path: {0}")] - GetLegacyPemPathFailed(FoundationError), + GetLegacyPemPathFailed(GetUserHomeError), } diff --git a/src/dfx-core/src/error/identity/instantiate_identity_from_name.rs b/src/dfx-core/src/error/identity/instantiate_identity_from_name.rs new file mode 100644 index 0000000000..68f9992780 --- /dev/null +++ b/src/dfx-core/src/error/identity/instantiate_identity_from_name.rs @@ -0,0 +1,15 @@ +use crate::error::identity::load_identity::LoadIdentityError; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum InstantiateIdentityFromNameError { + #[error("Failed to get principal of identity: {0}")] + GetIdentityPrincipalFailed(String), + + #[error("Failed to load identity: {0}")] + LoadIdentityFailed(LoadIdentityError), + + #[error("Identity must exist: {0}")] + RequireIdentityExistsFailed(RequireIdentityExistsError), +} diff --git a/src/dfx-core/src/error/identity/load_identity.rs b/src/dfx-core/src/error/identity/load_identity.rs new file mode 100644 index 0000000000..b237ed2c96 --- /dev/null +++ b/src/dfx-core/src/error/identity/load_identity.rs @@ -0,0 +1,12 @@ +use crate::error::identity::get_identity_config_or_default::GetIdentityConfigOrDefaultError; +use crate::error::identity::new_identity::NewIdentityError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LoadIdentityError { + #[error("Failed to get identity config: {0}")] + GetIdentityConfigOrDefaultFailed(GetIdentityConfigOrDefaultError), + + #[error("Failed to instantiate identity: {0}")] + NewIdentityFailed(NewIdentityError), +} diff --git a/src/dfx-core/src/error/identity/load_pem.rs b/src/dfx-core/src/error/identity/load_pem.rs new file mode 100644 index 0000000000..5cf73dea45 --- /dev/null +++ b/src/dfx-core/src/error/identity/load_pem.rs @@ -0,0 +1,12 @@ +use crate::error::identity::load_pem_from_file::LoadPemFromFileError; +use crate::error::keyring::KeyringError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LoadPemError { + #[error("Failed to load PEM file from file : {0}")] + LoadFromFileFailed(LoadPemFromFileError), + + #[error("Failed to load PEM file from keyring for identity '{0}': {1}")] + LoadFromKeyringFailed(Box, KeyringError), +} diff --git a/src/dfx-core/src/error/identity/load_pem_from_file.rs b/src/dfx-core/src/error/identity/load_pem_from_file.rs new file mode 100644 index 0000000000..6fcfa57229 --- /dev/null +++ b/src/dfx-core/src/error/identity/load_pem_from_file.rs @@ -0,0 +1,13 @@ +use crate::error::encryption::EncryptionError; +use crate::error::fs::FsError; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LoadPemFromFileError { + #[error("Failed to decrypt PEM file: {0}")] + DecryptPemFileFailed(PathBuf, EncryptionError), + + #[error("Failed to read pem file: {0}")] + ReadPemFileFailed(FsError), +} diff --git a/src/dfx-core/src/error/identity/load_pem_identity.rs b/src/dfx-core/src/error/identity/load_pem_identity.rs new file mode 100644 index 0000000000..74033d3afb --- /dev/null +++ b/src/dfx-core/src/error/identity/load_pem_identity.rs @@ -0,0 +1,8 @@ +use ic_agent::identity::PemError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum LoadPemIdentityError { + #[error("Cannot read identity file '{0}': {1:#}")] + ReadIdentityFileFailed(String, Box), +} diff --git a/src/dfx-core/src/error/identity/map_wallets_to_renamed_identity.rs b/src/dfx-core/src/error/identity/map_wallets_to_renamed_identity.rs new file mode 100644 index 0000000000..6fef2e6fb3 --- /dev/null +++ b/src/dfx-core/src/error/identity/map_wallets_to_renamed_identity.rs @@ -0,0 +1,15 @@ +use crate::error::config::ConfigError; +use crate::error::identity::rename_wallet_global_config_key::RenameWalletGlobalConfigKeyError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum MapWalletsToRenamedIdentityError { + #[error("Failed to get config directory for identity manager: {0}")] + GetConfigDirectoryFailed(ConfigError), + + #[error("Failed to get shared network data directory: {0}")] + GetSharedNetworkDataDirectoryFailed(ConfigError), + + #[error("Failed to rename wallet global config key: {0}")] + RenameWalletGlobalConfigKeyFailed(RenameWalletGlobalConfigKeyError), +} diff --git a/src/dfx-core/src/error/identity/mod.rs b/src/dfx-core/src/error/identity/mod.rs index 07231f5291..ba4412624b 100644 --- a/src/dfx-core/src/error/identity/mod.rs +++ b/src/dfx-core/src/error/identity/mod.rs @@ -1,105 +1,28 @@ +pub mod call_sender_from_wallet; pub mod convert_mnemonic_to_key; +pub mod create_identity_config; pub mod create_new_identity; pub mod export_identity; pub mod generate_key; +pub mod get_identity_config_or_default; pub mod get_legacy_credentials_pem_path; pub mod initialize_identity_manager; +pub mod instantiate_identity_from_name; +pub mod load_identity; +pub mod load_pem; +pub mod load_pem_from_file; +pub mod load_pem_identity; +pub mod map_wallets_to_renamed_identity; +pub mod new_hardware_identity; +pub mod new_identity; pub mod new_identity_manager; +pub mod remove_identity; pub mod rename_identity; +pub mod rename_wallet_global_config_key; +pub mod require_identity_exists; +pub mod save_identity_configuration; pub mod save_pem; +pub mod use_identity_by_name; +pub mod validate_pem_file; +pub mod write_default_identity; pub mod write_pem_to_file; - -use crate::error::config::ConfigError; -use crate::error::encryption::EncryptionError; -use crate::error::fs::FsError; -use crate::error::keyring::KeyringError; -use crate::error::structured_file::StructuredFileError; -use crate::error::wallet_config::WalletConfigError; -use ic_agent::export::PrincipalError; -use ic_agent::identity::PemError; -use ic_identity_hsm::HardwareIdentityError; -use std::path::PathBuf; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum IdentityError { - #[error("Cannot delete the default identity.")] - CannotDeleteDefaultIdentity(), - - #[error("Cannot delete the anonymous identity.")] - CannotDeleteAnonymousIdentity(), - - #[error("Failed to decrypt PEM file: {0}")] - DecryptPemFileFailed(PathBuf, EncryptionError), - - #[error("Failed to display linked wallets: {0}")] - DisplayLinkedWalletsFailed(WalletConfigError), - - #[error("If you want to remove an identity with configured wallets, please use the --drop-wallets flag.")] - DropWalletsFlagRequiredToRemoveIdentityWithWallets(), - - #[error("Failed to ensure identity configuration directory exists: {0}")] - EnsureIdentityConfigurationDirExistsFailed(FsError), - - #[error("Failed to generate a fresh encryption configuration: {0}")] - GenerateFreshEncryptionConfigurationFailed(EncryptionError), - - #[error("Failed to get config directory for identity manager: {0}")] - GetConfigDirectoryFailed(ConfigError), - - #[error("Failed to get shared network data directory: {0}")] - GetSharedNetworkDataDirectoryFailed(ConfigError), - - #[error("Failed to get principal of identity: {0}")] - GetIdentityPrincipalFailed(String), - - #[error("Identity {0} does not exist at '{1}'.")] - IdentityDoesNotExist(String, PathBuf), - - #[error("Failed to instantiate hardware identity for identity '{0}': {1}.")] - InstantiateHardwareIdentityFailed(String, Box), - - #[error("Failed to load configuration for identity '{0}': {1}")] - LoadIdentityConfigurationFailed(String, StructuredFileError), - - #[error("Failed to load PEM file from keyring for identity '{0}': {1}")] - LoadPemFromKeyringFailed(Box, KeyringError), - - #[error("Failed to read principal from id '{0}': {1}")] - ParsePrincipalFromIdFailed(String, PrincipalError), - - #[error("Cannot read identity file '{0}': {1:#}")] - ReadIdentityFileFailed(String, Box), - - #[error("Failed to read pem file: {0}")] - ReadPemFileFailed(FsError), - - #[error("Failed to remove identity directory: {0}")] - RemoveIdentityDirectoryFailed(FsError), - - #[error("Failed to remove identity from keyring: {0}")] - RemoveIdentityFromKeyringFailed(KeyringError), - - #[error("Failed to remove identity file: {0}")] - RemoveIdentityFileFailed(FsError), - - #[error("Failed to rename '{0}' to '{1}' in the global wallet config: {2}")] - RenameWalletFailed(Box, Box, WalletConfigError), - - #[error("An Identity named {0} cannot be created as it is reserved for internal use.")] - ReservedIdentityName(String), - - #[error("Failed to save identity configuration: {0}")] - SaveIdentityConfigurationFailed(StructuredFileError), - - #[error("Failed to save identity manager configuration: {0}")] - SaveIdentityManagerConfigurationFailed(StructuredFileError), - - #[error( - "Ed25519 v1 keys (those generated by OpenSSL) are not supported. Try again with a v2 key" - )] - UnsupportedKeyVersion(), - - #[error("Failed to validate PEM content: {0}")] - ValidatePemContentFailed(Box), -} diff --git a/src/dfx-core/src/error/identity/new_hardware_identity.rs b/src/dfx-core/src/error/identity/new_hardware_identity.rs new file mode 100644 index 0000000000..30aefa112e --- /dev/null +++ b/src/dfx-core/src/error/identity/new_hardware_identity.rs @@ -0,0 +1,8 @@ +use ic_identity_hsm::HardwareIdentityError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NewHardwareIdentityError { + #[error("Failed to instantiate hardware identity for identity '{0}': {1}.")] + InstantiateHardwareIdentityFailed(String, Box), +} diff --git a/src/dfx-core/src/error/identity/new_identity.rs b/src/dfx-core/src/error/identity/new_identity.rs new file mode 100644 index 0000000000..f46c0a65d3 --- /dev/null +++ b/src/dfx-core/src/error/identity/new_identity.rs @@ -0,0 +1,16 @@ +use crate::error::identity::load_pem::LoadPemError; +use crate::error::identity::load_pem_identity::LoadPemIdentityError; +use crate::error::identity::new_hardware_identity::NewHardwareIdentityError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NewIdentityError { + #[error("Failed to load PEM: {0}")] + LoadPemFailed(LoadPemError), + + #[error("Failed to load PEM identity: {0}")] + LoadPemIdentityFailed(LoadPemIdentityError), + + #[error("Failed to instantiate hardware identity: {0}")] + NewHardwareIdentityFailed(NewHardwareIdentityError), +} diff --git a/src/dfx-core/src/error/identity/new_identity_manager.rs b/src/dfx-core/src/error/identity/new_identity_manager.rs index 137862014d..d5016e06c9 100644 --- a/src/dfx-core/src/error/identity/new_identity_manager.rs +++ b/src/dfx-core/src/error/identity/new_identity_manager.rs @@ -1,6 +1,6 @@ use crate::error::config::ConfigError; use crate::error::identity::initialize_identity_manager::InitializeIdentityManagerError; -use crate::error::identity::IdentityError; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; use crate::error::structured_file::StructuredFileError; use thiserror::Error; @@ -16,5 +16,5 @@ pub enum NewIdentityManagerError { InitializeFailed(InitializeIdentityManagerError), #[error("The specified identity must exist: {0}")] - OverrideIdentityMustExist(IdentityError), + OverrideIdentityMustExist(RequireIdentityExistsError), } diff --git a/src/dfx-core/src/error/identity/remove_identity.rs b/src/dfx-core/src/error/identity/remove_identity.rs new file mode 100644 index 0000000000..a7e93fe0e1 --- /dev/null +++ b/src/dfx-core/src/error/identity/remove_identity.rs @@ -0,0 +1,32 @@ +use crate::error::fs::FsError; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use crate::error::keyring::KeyringError; +use crate::error::wallet_config::WalletConfigError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum RemoveIdentityError { + #[error("Cannot delete the anonymous identity.")] + CannotDeleteAnonymousIdentity(), + + #[error("Cannot delete the default identity.")] + CannotDeleteDefaultIdentity(), + + #[error("Failed to display linked wallets: {0}")] + DisplayLinkedWalletsFailed(WalletConfigError), + + #[error("If you want to remove an identity with configured wallets, please use the --drop-wallets flag.")] + DropWalletsFlagRequiredToRemoveIdentityWithWallets(), + + #[error("Failed to remove identity directory: {0}")] + RemoveIdentityDirectoryFailed(FsError), + + #[error("Failed to remove identity file: {0}")] + RemoveIdentityFileFailed(FsError), + + #[error("Failed to remove identity from keyring: {0}")] + RemoveIdentityFromKeyringFailed(KeyringError), + + #[error("Identity must exist: {0}")] + RequireIdentityExistsFailed(RequireIdentityExistsError), +} diff --git a/src/dfx-core/src/error/identity/rename_identity.rs b/src/dfx-core/src/error/identity/rename_identity.rs index 9c267cabf1..658f728ce6 100644 --- a/src/dfx-core/src/error/identity/rename_identity.rs +++ b/src/dfx-core/src/error/identity/rename_identity.rs @@ -1,6 +1,11 @@ use crate::error::fs::FsError; +use crate::error::identity::get_identity_config_or_default::GetIdentityConfigOrDefaultError; +use crate::error::identity::load_pem::LoadPemError; +use crate::error::identity::map_wallets_to_renamed_identity::MapWalletsToRenamedIdentityError; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use crate::error::identity::save_identity_configuration::SaveIdentityConfigurationError; use crate::error::identity::save_pem::SavePemError; -use crate::error::identity::IdentityError; +use crate::error::identity::write_default_identity::WriteDefaultIdentityError; use crate::error::keyring::KeyringError; use thiserror::Error; @@ -10,19 +15,19 @@ pub enum RenameIdentityError { CannotCreateAnonymousIdentity(), #[error("Failed to get identity config: {0}")] - GetIdentityConfigFailed(IdentityError), + GetIdentityConfigFailed(GetIdentityConfigOrDefaultError), #[error("Identity already exists.")] IdentityAlreadyExists(), #[error("Identity does not exist: {0}")] - IdentityDoesNotExist(IdentityError), + IdentityDoesNotExist(RequireIdentityExistsError), #[error("Failed to load pem: {0}")] - LoadPemFailed(IdentityError), + LoadPemFailed(LoadPemError), #[error("Failed to map wallets to renamed identity: {0}")] - MapWalletsToRenamedIdentityFailed(IdentityError /*MapWalletsToRenamedIdentityError*/), + MapWalletsToRenamedIdentityFailed(MapWalletsToRenamedIdentityError), #[error("Failed to remove identity from keyring: {0}")] RemoveIdentityFromKeyringFailed(KeyringError), @@ -31,11 +36,11 @@ pub enum RenameIdentityError { RenameIdentityDirectoryFailed(FsError), #[error("Failed to save identity configuration: {0}")] - SaveIdentityConfigurationFailed(IdentityError), + SaveIdentityConfigurationFailed(SaveIdentityConfigurationError), #[error("Failed to save pem: {0}")] SavePemFailed(SavePemError), #[error("Failed to switch over default identity settings: {0}")] - SwitchDefaultIdentitySettingsFailed(IdentityError), + SwitchDefaultIdentitySettingsFailed(WriteDefaultIdentityError), } diff --git a/src/dfx-core/src/error/identity/rename_wallet_global_config_key.rs b/src/dfx-core/src/error/identity/rename_wallet_global_config_key.rs new file mode 100644 index 0000000000..3e67cef07e --- /dev/null +++ b/src/dfx-core/src/error/identity/rename_wallet_global_config_key.rs @@ -0,0 +1,8 @@ +use crate::error::wallet_config::WalletConfigError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum RenameWalletGlobalConfigKeyError { + #[error("Failed to rename '{0}' to '{1}' in the global wallet config: {2}")] + RenameWalletFailed(Box, Box, WalletConfigError), +} diff --git a/src/dfx-core/src/error/identity/require_identity_exists.rs b/src/dfx-core/src/error/identity/require_identity_exists.rs new file mode 100644 index 0000000000..12edc3f6c8 --- /dev/null +++ b/src/dfx-core/src/error/identity/require_identity_exists.rs @@ -0,0 +1,11 @@ +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum RequireIdentityExistsError { + #[error("Identity {0} does not exist at '{1}'.")] + IdentityDoesNotExist(String, PathBuf), + + #[error("An Identity named {0} cannot be created as it is reserved for internal use.")] + ReservedIdentityName(String), +} diff --git a/src/dfx-core/src/error/identity/save_identity_configuration.rs b/src/dfx-core/src/error/identity/save_identity_configuration.rs new file mode 100644 index 0000000000..b4e4921e9e --- /dev/null +++ b/src/dfx-core/src/error/identity/save_identity_configuration.rs @@ -0,0 +1,12 @@ +use crate::error::fs::FsError; +use crate::error::structured_file::StructuredFileError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SaveIdentityConfigurationError { + #[error("Failed to ensure identity configuration directory exists: {0}")] + EnsureIdentityConfigurationDirExistsFailed(FsError), + + #[error("Failed to save identity configuration: {0}")] + SaveIdentityConfigurationFailed(StructuredFileError), +} diff --git a/src/dfx-core/src/error/identity/use_identity_by_name.rs b/src/dfx-core/src/error/identity/use_identity_by_name.rs new file mode 100644 index 0000000000..25d0f871a3 --- /dev/null +++ b/src/dfx-core/src/error/identity/use_identity_by_name.rs @@ -0,0 +1,12 @@ +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use crate::error::identity::write_default_identity::WriteDefaultIdentityError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum UseIdentityByNameError { + #[error("Identity must exist: {0}")] + RequireIdentityExistsFailed(RequireIdentityExistsError), + + #[error("Failed to write default identity: {0}")] + WriteDefaultIdentityFailed(WriteDefaultIdentityError), +} diff --git a/src/dfx-core/src/error/identity/validate_pem_file.rs b/src/dfx-core/src/error/identity/validate_pem_file.rs new file mode 100644 index 0000000000..4be6561ca4 --- /dev/null +++ b/src/dfx-core/src/error/identity/validate_pem_file.rs @@ -0,0 +1,16 @@ +use ic_agent::identity::PemError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ValidatePemFileError { + #[error(transparent)] + PemError(#[from] ic_agent::identity::PemError), + + #[error( + "Ed25519 v1 keys (those generated by OpenSSL) are not supported. Try again with a v2 key" + )] + UnsupportedKeyVersion(), + + #[error("Failed to validate PEM content: {0}")] + ValidatePemContentFailed(Box), +} diff --git a/src/dfx-core/src/error/identity/write_default_identity.rs b/src/dfx-core/src/error/identity/write_default_identity.rs new file mode 100644 index 0000000000..8c15ea9ef3 --- /dev/null +++ b/src/dfx-core/src/error/identity/write_default_identity.rs @@ -0,0 +1,8 @@ +use crate::error::structured_file::StructuredFileError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum WriteDefaultIdentityError { + #[error("Failed to save identity manager configuration: {0}")] + SaveIdentityManagerConfigurationFailed(StructuredFileError), +} diff --git a/src/dfx-core/src/error/mod.rs b/src/dfx-core/src/error/mod.rs index 22f44a4afa..6cd3a42441 100644 --- a/src/dfx-core/src/error/mod.rs +++ b/src/dfx-core/src/error/mod.rs @@ -7,8 +7,9 @@ pub mod config; pub mod dfx_config; pub mod encryption; pub mod extension; -pub mod foundation; pub mod fs; +pub mod get_current_exe; +pub mod get_user_home; pub mod identity; pub mod keyring; pub mod load_dfx_config; diff --git a/src/dfx-core/src/extension/manager/install.rs b/src/dfx-core/src/extension/manager/install.rs index 11d48c1716..f25b550d2d 100644 --- a/src/dfx-core/src/extension/manager/install.rs +++ b/src/dfx-core/src/extension/manager/install.rs @@ -17,6 +17,7 @@ impl ExtensionManager { &self, extension_name: &str, install_as: Option<&str>, + version: Option<&Version>, ) -> Result<(), ExtensionError> { let effective_extension_name = install_as.unwrap_or(extension_name); @@ -29,7 +30,10 @@ impl ExtensionManager { )); } - let extension_version = self.get_extension_compatible_version(extension_name)?; + let extension_version = match version { + Some(version) => version.clone(), + None => self.get_extension_compatible_version(extension_name)?, + }; let github_release_tag = get_git_release_tag(extension_name, &extension_version); let extension_archive = get_extension_archive_name(extension_name)?; let url = get_extension_download_url(&github_release_tag, &extension_archive)?; diff --git a/src/dfx-core/src/foundation/mod.rs b/src/dfx-core/src/foundation/mod.rs index bf191c04ec..23f9bd577f 100644 --- a/src/dfx-core/src/foundation/mod.rs +++ b/src/dfx-core/src/foundation/mod.rs @@ -1,12 +1,14 @@ -use crate::error::foundation::FoundationError; -use crate::error::foundation::FoundationError::NoHomeInEnvironment; +use crate::error::get_current_exe::GetCurrentExeError; +use crate::error::get_current_exe::GetCurrentExeError::NoCurrentExe; +use crate::error::get_user_home::GetUserHomeError; +use crate::error::get_user_home::GetUserHomeError::NoHomeInEnvironment; use std::ffi::OsString; use std::path::PathBuf; -pub fn get_user_home() -> Result { +pub fn get_user_home() -> Result { std::env::var_os("HOME").ok_or(NoHomeInEnvironment()) } -pub fn get_current_exe() -> Result { - std::env::current_exe().map_err(FoundationError::NoCurrentExe) +pub fn get_current_exe() -> Result { + std::env::current_exe().map_err(NoCurrentExe) } diff --git a/src/dfx-core/src/identity/identity_manager.rs b/src/dfx-core/src/identity/identity_manager.rs index 8d0e81c318..f1a514c02e 100644 --- a/src/dfx-core/src/identity/identity_manager.rs +++ b/src/dfx-core/src/identity/identity_manager.rs @@ -1,11 +1,13 @@ use super::pem_utils::validate_pem_file; use super::{keyring_mock, WALLET_CONFIG_FILENAME}; -use crate::config::directories::get_config_dfx_dir_path; +use crate::config::directories::get_user_dfx_config_dir; use crate::error::encryption::EncryptionError; use crate::error::encryption::EncryptionError::{NonceGenerationFailed, SaltGenerationFailed}; use crate::error::fs::FsError; use crate::error::identity::convert_mnemonic_to_key::ConvertMnemonicToKeyError; use crate::error::identity::convert_mnemonic_to_key::ConvertMnemonicToKeyError::DeriveExtendedKeyFromPathFailed; +use crate::error::identity::create_identity_config::CreateIdentityConfigError; +use crate::error::identity::create_identity_config::CreateIdentityConfigError::GenerateFreshEncryptionConfigurationFailed; use crate::error::identity::create_new_identity::CreateNewIdentityError; use crate::error::identity::create_new_identity::CreateNewIdentityError::{ CleanupPreviousCreationAttemptsFailed, ConvertSecretKeyToSec1PemFailed, @@ -17,6 +19,8 @@ use crate::error::identity::export_identity::ExportIdentityError; use crate::error::identity::export_identity::ExportIdentityError::TranslatePemContentToTextFailed; use crate::error::identity::generate_key::GenerateKeyError; use crate::error::identity::generate_key::GenerateKeyError::GenerateFreshSecp256k1KeyFailed; +use crate::error::identity::get_identity_config_or_default::GetIdentityConfigOrDefaultError; +use crate::error::identity::get_identity_config_or_default::GetIdentityConfigOrDefaultError::LoadIdentityConfigurationFailed; use crate::error::identity::get_legacy_credentials_pem_path::GetLegacyCredentialsPemPathError; use crate::error::identity::get_legacy_credentials_pem_path::GetLegacyCredentialsPemPathError::GetLegacyPemPathFailed; use crate::error::identity::initialize_identity_manager::InitializeIdentityManagerError; @@ -24,20 +28,30 @@ use crate::error::identity::initialize_identity_manager::InitializeIdentityManag CreateIdentityDirectoryFailed, GenerateKeyFailed, MigrateLegacyIdentityFailed, WritePemToFileFailed, }; +use crate::error::identity::instantiate_identity_from_name::InstantiateIdentityFromNameError; +use crate::error::identity::instantiate_identity_from_name::InstantiateIdentityFromNameError::{ + GetIdentityPrincipalFailed, LoadIdentityFailed, +}; +use crate::error::identity::load_identity::LoadIdentityError; use crate::error::identity::new_identity_manager::NewIdentityManagerError; use crate::error::identity::new_identity_manager::NewIdentityManagerError::LoadIdentityManagerConfigurationFailed; +use crate::error::identity::remove_identity::RemoveIdentityError; +use crate::error::identity::remove_identity::RemoveIdentityError::{ + DisplayLinkedWalletsFailed, DropWalletsFlagRequiredToRemoveIdentityWithWallets, + RemoveIdentityDirectoryFailed, RemoveIdentityFileFailed, +}; use crate::error::identity::rename_identity::RenameIdentityError; use crate::error::identity::rename_identity::RenameIdentityError::{ GetIdentityConfigFailed, LoadPemFailed, MapWalletsToRenamedIdentityFailed, RenameIdentityDirectoryFailed, SavePemFailed, SwitchDefaultIdentitySettingsFailed, }; -use crate::error::identity::IdentityError; -use crate::error::identity::IdentityError::{ - DisplayLinkedWalletsFailed, DropWalletsFlagRequiredToRemoveIdentityWithWallets, - EnsureIdentityConfigurationDirExistsFailed, GenerateFreshEncryptionConfigurationFailed, - GetIdentityPrincipalFailed, LoadIdentityConfigurationFailed, RemoveIdentityDirectoryFailed, - RemoveIdentityFileFailed, RemoveIdentityFromKeyringFailed, SaveIdentityConfigurationFailed, -}; +use crate::error::identity::require_identity_exists::RequireIdentityExistsError; +use crate::error::identity::save_identity_configuration::SaveIdentityConfigurationError; +use crate::error::identity::save_identity_configuration::SaveIdentityConfigurationError::EnsureIdentityConfigurationDirExistsFailed; +use crate::error::identity::use_identity_by_name::UseIdentityByNameError; +use crate::error::identity::use_identity_by_name::UseIdentityByNameError::WriteDefaultIdentityFailed; +use crate::error::identity::write_default_identity::WriteDefaultIdentityError; +use crate::error::identity::write_default_identity::WriteDefaultIdentityError::SaveIdentityManagerConfigurationFailed; use crate::error::structured_file::StructuredFileError; use crate::foundation::get_user_home; use crate::fs::composite::ensure_parent_dir_exists; @@ -194,7 +208,7 @@ impl IdentityManager { identity_override: &Option, ) -> Result { let config_dfx_dir_path = - get_config_dfx_dir_path().map_err(NewIdentityManagerError::GetConfigDirectoryFailed)?; + get_user_dfx_config_dir().map_err(NewIdentityManagerError::GetConfigDirectoryFailed)?; let identity_root_path = config_dfx_dir_path.join("identity"); let identity_json_path = config_dfx_dir_path.join("identity.json"); @@ -235,7 +249,7 @@ impl IdentityManager { pub fn instantiate_selected_identity( &mut self, log: &Logger, - ) -> Result, IdentityError> { + ) -> Result, InstantiateIdentityFromNameError> { let name = self.selected_identity.clone(); self.instantiate_identity_from_name(name.as_str(), log) } @@ -245,12 +259,16 @@ impl IdentityManager { &mut self, identity_name: &str, log: &Logger, - ) -> Result, IdentityError> { + ) -> Result, InstantiateIdentityFromNameError> { let identity = match identity_name { ANONYMOUS_IDENTITY_NAME => Box::new(DfxIdentity::anonymous()), identity_name => { - self.require_identity_exists(log, identity_name)?; - Box::new(self.load_identity(identity_name, log)?) + self.require_identity_exists(log, identity_name) + .map_err(InstantiateIdentityFromNameError::RequireIdentityExistsFailed)?; + Box::new( + self.load_identity(identity_name, log) + .map_err(LoadIdentityFailed)?, + ) } }; use ic_agent::identity::Identity; @@ -259,9 +277,12 @@ impl IdentityManager { Ok(identity) } - fn load_identity(&self, name: &str, log: &Logger) -> Result { - let config = self.get_identity_config_or_default(name)?; + fn load_identity(&self, name: &str, log: &Logger) -> Result { + let config = self + .get_identity_config_or_default(name) + .map_err(LoadIdentityError::GetIdentityConfigOrDefaultFailed)?; DfxIdentity::new(name, config, self.file_locations(), log) + .map_err(LoadIdentityError::NewIdentityFailed) } /// Create a new identity (name -> generated key) @@ -302,7 +323,7 @@ impl IdentityManager { mode: IdentityStorageMode, name: &str, hardware_config: Option, - ) -> Result { + ) -> Result { if let Some(hsm) = hardware_config { Ok(IdentityConfiguration { hsm: Some(hsm), @@ -478,15 +499,16 @@ impl IdentityManager { name: &str, drop_wallets: bool, display_linked_wallets_to: Option<&Logger>, - ) -> Result<(), IdentityError> { - self.require_identity_exists(log, name)?; + ) -> Result<(), RemoveIdentityError> { + self.require_identity_exists(log, name) + .map_err(RemoveIdentityError::RequireIdentityExistsFailed)?; if name == ANONYMOUS_IDENTITY_NAME { - return Err(IdentityError::CannotDeleteAnonymousIdentity()); + return Err(RemoveIdentityError::CannotDeleteAnonymousIdentity()); } if self.configuration.default == name { - return Err(IdentityError::CannotDeleteDefaultIdentity()); + return Err(RemoveIdentityError::CannotDeleteDefaultIdentity()); } let wallet_config_file = self.get_persistent_wallet_config_file(name); @@ -505,7 +527,7 @@ impl IdentityManager { if let Ok(config) = self.get_identity_config_or_default(name) { if let Some(suffix) = config.keyring_identity_suffix { keyring_mock::delete_pem_from_keyring(&suffix) - .map_err(RemoveIdentityFromKeyringFailed)?; + .map_err(RemoveIdentityError::RemoveIdentityFromKeyringFailed)?; } } remove_identity_file(&self.get_identity_json_path(name))?; @@ -577,32 +599,44 @@ impl IdentityManager { } /// Select an identity by name to use by default - pub fn use_identity_named(&mut self, log: &Logger, name: &str) -> Result<(), IdentityError> { - self.require_identity_exists(log, name)?; - self.write_default_identity(name)?; + pub fn use_identity_named( + &mut self, + log: &Logger, + name: &str, + ) -> Result<(), UseIdentityByNameError> { + self.require_identity_exists(log, name) + .map_err(UseIdentityByNameError::RequireIdentityExistsFailed)?; + self.write_default_identity(name) + .map_err(WriteDefaultIdentityFailed)?; self.configuration.default = name.to_string(); Ok(()) } - fn write_default_identity(&self, name: &str) -> Result<(), IdentityError> { + fn write_default_identity(&self, name: &str) -> Result<(), WriteDefaultIdentityError> { let config = Configuration { default: String::from(name), }; save_configuration(&self.identity_json_path, &config) - .map_err(IdentityError::SaveIdentityManagerConfigurationFailed)?; + .map_err(SaveIdentityManagerConfigurationFailed)?; Ok(()) } /// Determines if there are enough files present to consider the identity as existing. /// Does NOT guarantee that the identity will load correctly. - pub fn require_identity_exists(&self, log: &Logger, name: &str) -> Result<(), IdentityError> { + pub fn require_identity_exists( + &self, + log: &Logger, + name: &str, + ) -> Result<(), RequireIdentityExistsError> { trace!(log, "Checking if identity '{name}' exists."); if name == ANONYMOUS_IDENTITY_NAME { return Ok(()); } if name.starts_with(TEMP_IDENTITY_PREFIX) { - return Err(IdentityError::ReservedIdentityName(String::from(name))); + return Err(RequireIdentityExistsError::ReservedIdentityName( + String::from(name), + )); } let json_path = self.get_identity_json_path(name); @@ -610,7 +644,7 @@ impl IdentityManager { let encrypted_pem_path = self.file_locations.get_encrypted_identity_pem_path(name); if !plaintext_pem_path.exists() && !encrypted_pem_path.exists() && !json_path.exists() { - Err(IdentityError::IdentityDoesNotExist( + Err(RequireIdentityExistsError::IdentityDoesNotExist( String::from(name), json_path, )) @@ -637,7 +671,7 @@ impl IdentityManager { pub fn get_identity_config_or_default( &self, identity: &str, - ) -> Result { + ) -> Result { let json_path = self.get_identity_json_path(identity); if json_path.exists() { load_json_file(&json_path) @@ -750,15 +784,16 @@ pub(super) fn save_identity_configuration( log: &Logger, path: &Path, config: &IdentityConfiguration, -) -> Result<(), IdentityError> { +) -> Result<(), SaveIdentityConfigurationError> { trace!(log, "Writing identity configuration to {}", path.display()); ensure_parent_dir_exists(path).map_err(EnsureIdentityConfigurationDirExistsFailed)?; - save_json_file(path, &config).map_err(SaveIdentityConfigurationFailed) + save_json_file(path, &config) + .map_err(SaveIdentityConfigurationError::SaveIdentityConfigurationFailed) } /// Removes the file if it exists. -fn remove_identity_file(file: &Path) -> Result<(), IdentityError> { +fn remove_identity_file(file: &Path) -> Result<(), RemoveIdentityError> { if file.exists() { crate::fs::remove_file(file).map_err(RemoveIdentityFileFailed)?; } diff --git a/src/dfx-core/src/identity/mod.rs b/src/dfx-core/src/identity/mod.rs index 44cb4b7fe2..b9f91258f1 100644 --- a/src/dfx-core/src/identity/mod.rs +++ b/src/dfx-core/src/identity/mod.rs @@ -2,12 +2,18 @@ //! //! Wallets are a map of network-identity, but don't have their own types or manager //! type. -use crate::config::directories::{get_config_dfx_dir_path, get_shared_network_data_directory}; -use crate::error::identity::IdentityError; -use crate::error::identity::IdentityError::{ - GetConfigDirectoryFailed, GetSharedNetworkDataDirectoryFailed, - InstantiateHardwareIdentityFailed, ReadIdentityFileFailed, RenameWalletFailed, -}; +use crate::config::directories::{get_shared_network_data_directory, get_user_dfx_config_dir}; +use crate::error::identity::call_sender_from_wallet::CallSenderFromWalletError; +use crate::error::identity::call_sender_from_wallet::CallSenderFromWalletError::ParsePrincipalFromIdFailed; +use crate::error::identity::load_pem_identity::LoadPemIdentityError; +use crate::error::identity::load_pem_identity::LoadPemIdentityError::ReadIdentityFileFailed; +use crate::error::identity::map_wallets_to_renamed_identity::MapWalletsToRenamedIdentityError; +use crate::error::identity::map_wallets_to_renamed_identity::MapWalletsToRenamedIdentityError::RenameWalletGlobalConfigKeyFailed; +use crate::error::identity::new_hardware_identity::NewHardwareIdentityError; +use crate::error::identity::new_hardware_identity::NewHardwareIdentityError::InstantiateHardwareIdentityFailed; +use crate::error::identity::new_identity::NewIdentityError; +use crate::error::identity::rename_wallet_global_config_key::RenameWalletGlobalConfigKeyError; +use crate::error::identity::rename_wallet_global_config_key::RenameWalletGlobalConfigKeyError::RenameWalletFailed; use crate::error::wallet_config::WalletConfigError; use crate::error::wallet_config::WalletConfigError::{ EnsureWalletConfigDirFailed, LoadWalletConfigFailed, SaveWalletConfigFailed, @@ -16,7 +22,9 @@ use crate::identity::identity_file_locations::IdentityFileLocations; use crate::json::{load_json_file, save_json_file}; use candid::Principal; use ic_agent::agent::EnvelopeContent; -use ic_agent::identity::{AnonymousIdentity, BasicIdentity, Secp256k1Identity}; +use ic_agent::identity::{ + AnonymousIdentity, BasicIdentity, Delegation, Secp256k1Identity, SignedDelegation, +}; use ic_agent::Signature; use ic_identity_hsm::HardwareIdentity; pub use identity_manager::{ @@ -72,7 +80,11 @@ impl Identity { } } - fn basic(name: &str, pem_content: &[u8], was_encrypted: bool) -> Result { + fn basic( + name: &str, + pem_content: &[u8], + was_encrypted: bool, + ) -> Result { let inner = Box::new( BasicIdentity::from_pem(pem_content) .map_err(|e| ReadIdentityFileFailed(name.into(), Box::new(e)))?, @@ -89,7 +101,7 @@ impl Identity { name: &str, pem_content: &[u8], was_encrypted: bool, - ) -> Result { + ) -> Result { let inner = Box::new( Secp256k1Identity::from_pem(pem_content) .map_err(|e| ReadIdentityFileFailed(name.into(), Box::new(e)))?, @@ -102,7 +114,10 @@ impl Identity { }) } - fn hardware(name: &str, hsm: HardwareIdentityConfiguration) -> Result { + fn hardware( + name: &str, + hsm: HardwareIdentityConfiguration, + ) -> Result { let inner = Box::new( HardwareIdentity::new( hsm.pkcs11_lib_path, @@ -124,14 +139,16 @@ impl Identity { config: IdentityConfiguration, locations: &IdentityFileLocations, log: &Logger, - ) -> Result { + ) -> Result { if let Some(hsm) = config.hsm { - Identity::hardware(name, hsm) + Identity::hardware(name, hsm).map_err(NewIdentityError::NewHardwareIdentityFailed) } else { let (pem_content, was_encrypted) = - pem_safekeeping::load_pem(log, locations, name, &config)?; + pem_safekeeping::load_pem(log, locations, name, &config) + .map_err(NewIdentityError::LoadPemFailed)?; Identity::secp256k1(name, &pem_content, was_encrypted) .or_else(|e| Identity::basic(name, &pem_content, was_encrypted).map_err(|_| e)) + .map_err(NewIdentityError::LoadPemIdentityFailed) } } @@ -181,7 +198,7 @@ impl Identity { original_identity: &str, renamed_identity: &str, wallet_path: PathBuf, - ) -> Result<(), IdentityError> { + ) -> Result<(), RenameWalletGlobalConfigKeyError> { Identity::load_wallet_config(&wallet_path) .and_then(|mut config| { let identities = &mut config.identities; @@ -207,9 +224,9 @@ impl Identity { project_temp_dir: Option, original_identity: &str, renamed_identity: &str, - ) -> Result<(), IdentityError> { - let persistent_wallet_path = get_config_dfx_dir_path() - .map_err(GetConfigDirectoryFailed)? + ) -> Result<(), MapWalletsToRenamedIdentityError> { + let persistent_wallet_path = get_user_dfx_config_dir() + .map_err(MapWalletsToRenamedIdentityError::GetConfigDirectoryFailed)? .join("identity") .join(original_identity) .join(WALLET_CONFIG_FILENAME); @@ -218,17 +235,19 @@ impl Identity { original_identity, renamed_identity, persistent_wallet_path, - )?; + ) + .map_err(RenameWalletGlobalConfigKeyFailed)?; } let shared_local_network_wallet_path = get_shared_network_data_directory("local") - .map_err(GetSharedNetworkDataDirectoryFailed)? + .map_err(MapWalletsToRenamedIdentityError::GetSharedNetworkDataDirectoryFailed)? .join(WALLET_CONFIG_FILENAME); if shared_local_network_wallet_path.exists() { Identity::rename_wallet_global_config_key( original_identity, renamed_identity, shared_local_network_wallet_path, - )?; + ) + .map_err(RenameWalletGlobalConfigKeyFailed)?; } if let Some(temp_dir) = project_temp_dir { let local_wallet_path = temp_dir.join("local").join(WALLET_CONFIG_FILENAME); @@ -237,7 +256,8 @@ impl Identity { original_identity, renamed_identity, local_wallet_path, - )?; + ) + .map_err(RenameWalletGlobalConfigKeyFailed)?; } } Ok(()) @@ -249,9 +269,25 @@ impl ic_agent::Identity for Identity { self.inner.sender() } + fn public_key(&self) -> Option> { + self.inner.public_key() + } + + fn delegation_chain(&self) -> Vec { + self.inner.delegation_chain() + } + fn sign(&self, content: &EnvelopeContent) -> Result { self.inner.sign(content) } + + fn sign_arbitrary(&self, content: &[u8]) -> Result { + self.inner.sign_arbitrary(content) + } + + fn sign_delegation(&self, content: &Delegation) -> Result { + self.inner.sign_delegation(content) + } } impl AsRef for Identity { @@ -269,11 +305,10 @@ pub enum CallSender { // Determine whether the selected Identity // or the provided wallet canister ID should be the Sender of the call. impl CallSender { - pub fn from(wallet: &Option) -> Result { + pub fn from(wallet: &Option) -> Result { let sender = if let Some(id) = wallet { CallSender::Wallet( - Principal::from_text(id) - .map_err(|e| IdentityError::ParsePrincipalFromIdFailed(id.clone(), e))?, + Principal::from_text(id).map_err(|e| ParsePrincipalFromIdFailed(id.clone(), e))?, ) } else { CallSender::SelectedId diff --git a/src/dfx-core/src/identity/pem_safekeeping.rs b/src/dfx-core/src/identity/pem_safekeeping.rs index a3103ee4ad..ca520114a5 100644 --- a/src/dfx-core/src/identity/pem_safekeeping.rs +++ b/src/dfx-core/src/identity/pem_safekeeping.rs @@ -3,6 +3,12 @@ use super::IdentityConfiguration; use crate::error::encryption::EncryptionError; use crate::error::encryption::EncryptionError::{DecryptContentFailed, HashPasswordFailed}; use crate::error::fs::FsError; +use crate::error::identity::load_pem::LoadPemError; +use crate::error::identity::load_pem::LoadPemError::LoadFromKeyringFailed; +use crate::error::identity::load_pem_from_file::LoadPemFromFileError; +use crate::error::identity::load_pem_from_file::LoadPemFromFileError::{ + DecryptPemFileFailed, ReadPemFileFailed, +}; use crate::error::identity::save_pem::SavePemError; use crate::error::identity::save_pem::SavePemError::{ CannotSavePemContentForHsm, WritePemToKeyringFailed, @@ -11,10 +17,6 @@ use crate::error::identity::write_pem_to_file::WritePemToFileError; use crate::error::identity::write_pem_to_file::WritePemToFileError::{ EncryptPemFileFailed, WritePemContentFailed, }; -use crate::error::identity::IdentityError; -use crate::error::identity::IdentityError::{ - DecryptPemFileFailed, LoadPemFromKeyringFailed, ReadPemFileFailed, -}; use crate::identity::identity_file_locations::IdentityFileLocations; use crate::identity::keyring_mock; use crate::identity::pem_safekeeping::PromptMode::{DecryptingToUse, EncryptingToCreate}; @@ -30,7 +32,7 @@ pub(crate) fn load_pem( locations: &IdentityFileLocations, identity_name: &str, identity_config: &IdentityConfiguration, -) -> Result<(Vec, bool), IdentityError> { +) -> Result<(Vec, bool), LoadPemError> { if identity_config.hsm.is_some() { unreachable!("Cannot load pem content for an HSM identity.") } else if identity_config.keyring_identity_suffix.is_some() { @@ -39,11 +41,12 @@ pub(crate) fn load_pem( "Found keyring identity suffix - PEM file is stored in keyring." ); let pem = keyring_mock::load_pem_from_keyring(identity_name) - .map_err(|err| LoadPemFromKeyringFailed(Box::new(identity_name.to_string()), err))?; + .map_err(|err| LoadFromKeyringFailed(Box::new(identity_name.to_string()), err))?; Ok((pem, true)) } else { let pem_path = locations.get_identity_pem_path(identity_name, identity_config); load_pem_from_file(&pem_path, Some(identity_config)) + .map_err(LoadPemError::LoadFromFileFailed) } } @@ -80,7 +83,7 @@ pub(crate) fn save_pem( pub fn load_pem_from_file( path: &Path, config: Option<&IdentityConfiguration>, -) -> Result<(Vec, bool), IdentityError> { +) -> Result<(Vec, bool), LoadPemFromFileError> { let content = crate::fs::read(path).map_err(ReadPemFileFailed)?; let (content, was_encrypted) = maybe_decrypt_pem(content.as_slice(), config) diff --git a/src/dfx-core/src/identity/pem_utils.rs b/src/dfx-core/src/identity/pem_utils.rs index 0466a0c78f..8e157341aa 100644 --- a/src/dfx-core/src/identity/pem_utils.rs +++ b/src/dfx-core/src/identity/pem_utils.rs @@ -1,10 +1,12 @@ -use crate::error::identity::IdentityError; -use crate::error::identity::IdentityError::{UnsupportedKeyVersion, ValidatePemContentFailed}; +use crate::error::identity::validate_pem_file::ValidatePemFileError; +use crate::error::identity::validate_pem_file::ValidatePemFileError::{ + UnsupportedKeyVersion, ValidatePemContentFailed, +}; use ic_agent::identity::BasicIdentity; use ic_agent::identity::PemError; use ic_agent::identity::Secp256k1Identity; -pub fn validate_pem_file(pem_content: &[u8]) -> Result<(), IdentityError> { +pub fn validate_pem_file(pem_content: &[u8]) -> Result<(), ValidatePemFileError> { let secp_res = Secp256k1Identity::from_pem(pem_content).map_err(|e| ValidatePemContentFailed(Box::new(e))); if let Err(e) = secp_res { diff --git a/src/dfx-core/src/network/provider.rs b/src/dfx-core/src/network/provider.rs index 4f383c468a..dcc67b2cd6 100644 --- a/src/dfx-core/src/network/provider.rs +++ b/src/dfx-core/src/network/provider.rs @@ -91,11 +91,6 @@ fn config_network_to_network_descriptor( .clone() .or_else(|| project_defaults.and_then(|x| x.bitcoin.clone())) .unwrap_or_default(); - let bootstrap = local_provider - .bootstrap - .clone() - .or_else(|| project_defaults.and_then(|x| x.bootstrap.clone())) - .unwrap_or_default(); let canister_http = local_provider .canister_http .clone() @@ -125,7 +120,6 @@ fn config_network_to_network_descriptor( data_directory, bind_address, bitcoin, - bootstrap, canister_http, replica, local_scope, @@ -353,10 +347,6 @@ fn create_project_network_descriptor( network_name, config.get_path().display(), ); - warn!( - logger, - "Project-specific networks are deprecated and will be removed after February 2023." - ); let data_directory = config.get_temp_path().join("network").join(network_name); let legacy_pid_path = Some(config.get_temp_path().join("pid")); @@ -480,8 +470,8 @@ mod tests { use crate::config::model::canister_http_adapter::HttpAdapterLogLevel; use crate::config::model::dfinity::ReplicaSubnetType::{System, VerifiedApplication}; use crate::config::model::dfinity::{ - to_socket_addr, ConfigDefaultsBitcoin, ConfigDefaultsBootstrap, ConfigDefaultsCanisterHttp, - ConfigDefaultsReplica, ReplicaLogLevel, + to_socket_addr, ConfigDefaultsBitcoin, ConfigDefaultsCanisterHttp, ConfigDefaultsReplica, + ReplicaLogLevel, }; use std::fs; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; @@ -1021,47 +1011,6 @@ mod tests { ); } - #[test] - fn bootstrap_config_on_local_network() { - let config = Config::from_str( - r#"{ - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "bootstrap": { - "ip": "0.0.0.0", - "port": 12002, - "timeout": 60000 - } - } - } - }"#, - ) - .unwrap(); - - let network_descriptor = create_network_descriptor( - Some(Arc::new(config)), - Arc::new(NetworksConfig::new().unwrap()), - None, - None, - LocalBindDetermination::AsConfigured, - ) - .unwrap(); - let bootstrap_config = &network_descriptor - .local_server_descriptor() - .unwrap() - .bootstrap; - - assert_eq!( - bootstrap_config, - &ConfigDefaultsBootstrap { - ip: IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), - port: 12002, - timeout: 60000 - } - ); - } - #[test] fn url_is_url() { assert_eq!( diff --git a/src/dfx/Cargo.toml b/src/dfx/Cargo.toml index 8ac73541a9..4f6f1ae8a2 100644 --- a/src/dfx/Cargo.toml +++ b/src/dfx/Cargo.toml @@ -118,6 +118,7 @@ url.workspace = true walkdir.workspace = true walrus = "0.20.1" which = "4.2.5" +ci_info = "0.14" [target.'cfg(windows)'.dependencies] junction = "1.0.0" diff --git a/src/dfx/assets/dfx-asset-sources.toml b/src/dfx/assets/dfx-asset-sources.toml index 1b4fce71df..2ab11fd4f1 100644 --- a/src/dfx/assets/dfx-asset-sources.toml +++ b/src/dfx/assets/dfx-asset-sources.toml @@ -1,33 +1,33 @@ # generated by write-dfx-asset-sources.sh -replica-rev = 'cabe2ae3ca115b1a3f24d75814d4f8e317b2964d' +replica-rev = '91bf38ff3cb927cb94027d9da513cd15f91a5b04' [x86_64-darwin.ic-ref] url = 'https://download.dfinity.systems/ic-ref/ic-ref-0.0.1-a9f73dba-x86_64-darwin.tar.gz' sha256 = 'e1c1694579f46d544aa87f6387d7e5a4b096fe65015b1609a459efcbaf15890f' [x86_64-darwin.icx-proxy] -url = 'https://github.com/dfinity/icx-proxy/releases/download/rev-c312760/binaries-macos.tar.gz' -sha256 = '5783bba5021cf43149bc118789cea29f6462fd97dd78bdb776f8782ea7813d27' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/icx-proxy-dev.gz' +sha256 = '619bd5516aee3b454b774221dc0969efb0e3cea897fb55ae9b98241133c0c327' [x86_64-darwin.ic-admin] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-admin.gz' -sha256 = '973bdf660a5fa9182dcf888a477dea8dcf35b18c1ef66b6c16c876f25601a751' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-admin.gz' +sha256 = '9cda6c0c65def04604f7456d16ebb5119b188ebe5a2fd6f0ae18f18b1c801dd0' [x86_64-darwin.ic-btc-adapter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-btc-adapter.gz' -sha256 = '1188ea4d2b7eadd7c736af4b06817e35df65f6048771ac059bd308276cb37911' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-btc-adapter.gz' +sha256 = 'a63f851e6ac0694e101866feb69b4dbbcaa0084dca3ac5de195e0bc51edda1c1' [x86_64-darwin.ic-https-outcalls-adapter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-https-outcalls-adapter.gz' -sha256 = 'dfd2c1a0e4440e04ea431c0b0499e7c5dfc1e67a615af07a47a548ee8ea52f1c' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-https-outcalls-adapter.gz' +sha256 = 'cb731541c904ba55d705c99968e3a9df12eca8bf2fdb3d529ea0b880f8331ac0' [x86_64-darwin.ic-nns-init] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-nns-init.gz' -sha256 = '98da37c14bf35508669cf4394709c4f8735e214048830412ddc6deae11ef8b3b' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-nns-init.gz' +sha256 = '4f0de1ea08225a168f7cbb9a65be0251870957b89ab593830bce9aa4f8f0ed64' [x86_64-darwin.ic-starter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/ic-starter.gz' -sha256 = 'f2c5828db457f1c5900df7ec13ab6d94dd035b40c22b2338fafb7e4c6b7fe655' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/ic-starter.gz' +sha256 = '36fd4a9db0f3291bc7616f9a5900926a808fa3cd563175654744ad697f4bc26d' [x86_64-darwin.motoko] url = 'https://github.com/dfinity/motoko/releases/download/0.9.8/motoko-Darwin-x86_64-0.9.8.tar.gz' @@ -35,21 +35,21 @@ sha256 = '27b75c676d294f8b8b5b287ab27b2419799d9523c4d7fdcdd92d29dbe6b57494' # The replica and canister_sandbox binaries must have the same revision. [x86_64-darwin.replica] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/replica.gz' -sha256 = 'b6aaac6bb358f72a1c7c53ea9ba5651d53e9799f916f4feba6e8ac8546abf415' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/replica.gz' +sha256 = '349bb6614b8a4a5135646a0f765af919ae49e4a81f12454ede757a245d065023' # The replica and canister_sandbox binaries must have the same revision. [x86_64-darwin.canister_sandbox] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/canister_sandbox.gz' -sha256 = 'a7ee82516475dcaa7d3677ac6cfddc633a18d778c20aa671620af95000cf1f3a' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/canister_sandbox.gz' +sha256 = 'cfa3f3e476e14fe9f5190a2d6937f0d90f890ac5d816c9bbca18025f81dab819' [x86_64-darwin.sandbox_launcher] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/sandbox_launcher.gz' -sha256 = '737874b7a214b9036f7b526a02e44899989f48325ef9a237d1aa7968649a5b51' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/sandbox_launcher.gz' +sha256 = '4005e0c87f29f0aebdaa7c8e1268d3bb938473735f29ba55fa072f149225aaa6' [x86_64-darwin.sns] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-darwin/sns.gz' -sha256 = '0107185d79190d8ba811d0d157d292a559afb341fd0cde3fbb45792f8af4dd03' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-darwin/sns.gz' +sha256 = '243a3d9f15f16f4efe485d1dff458f1671b169a7298965b54cd643e22c8ca85e' [x86_64-darwin.motoko-base] url = 'https://github.com/dfinity/motoko/releases/download/0.9.8/motoko-base-library.tar.gz' @@ -64,28 +64,28 @@ url = 'https://download.dfinity.systems/ic-ref/ic-ref-0.0.1-a9f73dba-x86_64-linu sha256 = '5c4967764e87d1b2945b1db027422633b48f08cd01d96ba2f622753fcb62c2a4' [x86_64-linux.icx-proxy] -url = 'https://github.com/dfinity/icx-proxy/releases/download/rev-c312760/binaries-linux.tar.gz' -sha256 = '7a5612a1fb7512d22dcd37627a9d626fbc282b172665a832fe2cc2b243789fa1' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/icx-proxy-dev.gz' +sha256 = '078cae8d1b711b37a9ac45a8501bd14c8099167b885a6b3484323f411c241341' [x86_64-linux.ic-admin] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-admin.gz' -sha256 = '5f3ba228050f471394967a3bb9551801f9595755f0cab9a98cd45c0dd4f71f83' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-admin.gz' +sha256 = 'f566e216eb8990e6609433f4e436ad8089c12fdd24acb03b7ede6c96a0e10936' [x86_64-linux.ic-btc-adapter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-btc-adapter.gz' -sha256 = '259d4eda8ebbc55c9e0fd0c8940a5b9ca4043901466dd3f8d400e0be531f40c3' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-btc-adapter.gz' +sha256 = 'c6269b3a816538c299d8487f326a3ef3b5a8c638b09c50a1bdf28fbaacc01bcc' [x86_64-linux.ic-https-outcalls-adapter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-https-outcalls-adapter.gz' -sha256 = '012017bc141ef516b8b8ba3eaf0a586c4c0ae6a5c5672dc449c981013c4bf36b' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-https-outcalls-adapter.gz' +sha256 = 'd6d8d511a934b383b91d0cc4f922322a05b0b5fadcfe7a04f1c14739648d72b1' [x86_64-linux.ic-nns-init] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-nns-init.gz' -sha256 = 'efa14f3777e9a37a5730ae0d025a7b1896e5e504bd22223437c011acdb75c186' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-nns-init.gz' +sha256 = 'b178bd1d0b40df5eec9620765f96abcf00b2b0fbf2de3bbb02f3615a874c03fe' [x86_64-linux.ic-starter] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/ic-starter.gz' -sha256 = '9d1b576bf8069fcee1f92fbf94fe4a7093fa9f83c1dfdf5de66f7552f18b8364' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/ic-starter.gz' +sha256 = '669daa35328b6539298e577c81ed8515d3e5c5bfe83c3008a9c932fbe058d036' [x86_64-linux.motoko] url = 'https://github.com/dfinity/motoko/releases/download/0.9.8/motoko-Linux-x86_64-0.9.8.tar.gz' @@ -93,21 +93,21 @@ sha256 = '513c437ed23e6a7b40f840469766e7e5b321162ff0259ef164ce60377064ca87' # The replica and canister_sandbox binaries must have the same revision. [x86_64-linux.replica] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/replica.gz' -sha256 = 'fdedd98b909790df80a74c612d55c1f83a13676581a1bc15048a6d578576bdbc' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/replica.gz' +sha256 = '5b322539661b8f81823bdfba154fa4e35393f72380bf52c0a668d7e6d36d7f69' # The replica and canister_sandbox binaries must have the same revision. [x86_64-linux.canister_sandbox] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/canister_sandbox.gz' -sha256 = 'bdc86050459fcbda927c73b5f05093f78f76f8fcf208ce51909ec44aad48d5ab' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/canister_sandbox.gz' +sha256 = '7b4abcaf499eb025de5f229e3c9c400623b046927bf898dab440eaea81eaea58' [x86_64-linux.sandbox_launcher] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/sandbox_launcher.gz' -sha256 = 'bc63935553cd711138bf40abd1c3374b3caf1a2db28860817c24b43795670edf' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/sandbox_launcher.gz' +sha256 = '7676f38d8326f546f133af42f852bc44fc08e52d45e92a7c315af9c5ff391883' [x86_64-linux.sns] -url = 'https://download.dfinity.systems/ic/cabe2ae3ca115b1a3f24d75814d4f8e317b2964d/openssl-static-binaries/x86_64-linux/sns.gz' -sha256 = 'fa069d1e354addfae321ed7157571e371b9a9629be132164fe78917e115ef150' +url = 'https://download.dfinity.systems/ic/91bf38ff3cb927cb94027d9da513cd15f91a5b04/openssl-static-binaries/x86_64-linux/sns.gz' +sha256 = '7e062a6867e675fac1340e5bf8d22b5dcb1c1b34303102c34e16920abf202394' [x86_64-linux.motoko-base] url = 'https://github.com/dfinity/motoko/releases/download/0.9.8/motoko-base-library.tar.gz' diff --git a/src/dfx/assets/new_project_node_files/src/__project_name___frontend/assets/.ic-assets.json5 b/src/dfx/assets/new_project_node_files/src/__project_name___frontend/assets/.ic-assets.json5 index 8dc76d1ee6..ccdea94aa1 100644 --- a/src/dfx/assets/new_project_node_files/src/__project_name___frontend/assets/.ic-assets.json5 +++ b/src/dfx/assets/new_project_node_files/src/__project_name___frontend/assets/.ic-assets.json5 @@ -24,7 +24,7 @@ // See: https://github.com/WebAssembly/content-security-policy/blob/main/proposals/CSP.md. // - We added img-src data: because data: images are used often. // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. // To configure permissions go here https://www.permissionspolicy.com/ @@ -50,7 +50,8 @@ // See: https://owasp.org/www-community/attacks/xss/ "X-XSS-Protection": "1; mode=block" }, - // redirect all requests from .raw.icp0.io to .icp0.io (this redirection is the default) - "allow_raw_access": false + // Set the allow_raw_access field to false to redirect all requests from .raw.icp0.io to .icp0.io + // The default behavior is to allow raw access. + // "allow_raw_access": true }, ] diff --git a/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5 b/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5 index 8dc76d1ee6..ccdea94aa1 100644 --- a/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5 +++ b/src/dfx/assets/new_project_node_files/src/__project_name___frontend/src/.ic-assets.json5 @@ -24,7 +24,7 @@ // See: https://github.com/WebAssembly/content-security-policy/blob/main/proposals/CSP.md. // - We added img-src data: because data: images are used often. // - frame-ancestors: none mitigates clickjacking attacks. See https://owasp.org/www-community/attacks/Clickjacking. - "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", + "Content-Security-Policy": "default-src 'self';script-src 'self' 'unsafe-eval';connect-src 'self' https://icp0.io https://*.icp0.io https://icp-api.io;img-src 'self' data:;style-src * 'unsafe-inline';style-src-elem * 'unsafe-inline';font-src *;object-src 'none';base-uri 'self';frame-ancestors 'none';form-action 'self';upgrade-insecure-requests;", // Security: The permissions policy disables all features for security reasons. If your site needs such permissions, activate them. // To configure permissions go here https://www.permissionspolicy.com/ @@ -50,7 +50,8 @@ // See: https://owasp.org/www-community/attacks/xss/ "X-XSS-Protection": "1; mode=block" }, - // redirect all requests from .raw.icp0.io to .icp0.io (this redirection is the default) - "allow_raw_access": false + // Set the allow_raw_access field to false to redirect all requests from .raw.icp0.io to .icp0.io + // The default behavior is to allow raw access. + // "allow_raw_access": true }, ] diff --git a/src/dfx/assets/prepare_assets.rs b/src/dfx/assets/prepare_assets.rs index 2c73ffca7b..f2cdcdf2a9 100644 --- a/src/dfx/assets/prepare_assets.rs +++ b/src/dfx/assets/prepare_assets.rs @@ -113,7 +113,7 @@ fn write_binary_cache( Compression::new(6), )); for (path, bin) in bins.into_iter().chain( - ["icx-proxy", "ic-ref", "moc", "mo-doc", "mo-ide"] + ["ic-ref", "moc", "mo-doc", "mo-ide"] .map(|bin| (bin.into(), bin_tars.remove(Path::new(bin)).unwrap())), ) { let mut header = Header::new_gnu(); @@ -182,6 +182,7 @@ async fn download_binaries( "ic-btc-adapter", "ic-https-outcalls-adapter", "ic-nns-init", + "icx-proxy", "replica", "canister_sandbox", "sandbox_launcher", @@ -219,13 +220,13 @@ async fn download_bin_tarballs( sources: Arc>, ) -> HashMap { let mut map = HashMap::new(); - let [motoko, icx_proxy, ic_ref] = ["motoko", "icx-proxy", "ic-ref"].map(|pkg| { + let [motoko, ic_ref] = ["motoko", "ic-ref"].map(|pkg| { let client = client.clone(); let source = sources[pkg].clone(); spawn(download_and_check_sha(client, source)) }); - let (motoko, icx_proxy, ic_ref) = tokio::try_join!(motoko, icx_proxy, ic_ref).unwrap(); - for tar in [motoko, icx_proxy, ic_ref] { + let (motoko, ic_ref) = tokio::try_join!(motoko, ic_ref).unwrap(); + for tar in [motoko, ic_ref] { tar_xzf(&tar, |path, content| { map.insert(path, content); }); diff --git a/src/dfx/src/commands/bootstrap.rs b/src/dfx/src/commands/bootstrap.rs deleted file mode 100644 index a639b073ee..0000000000 --- a/src/dfx/src/commands/bootstrap.rs +++ /dev/null @@ -1,181 +0,0 @@ -use crate::actors::icx_proxy::IcxProxyConfig; -use crate::actors::{start_icx_proxy_actor, start_shutdown_controller}; -use crate::lib::environment::Environment; -use crate::lib::error::DfxResult; -use crate::util::get_reusable_socket_addr; -use crate::NetworkOpt; -use dfx_core::config::model::network_descriptor::NetworkDescriptor; -use dfx_core::network::provider::{create_network_descriptor, LocalBindDetermination}; -use anyhow::{anyhow, Context, Error}; -use clap::Parser; -use fn_error_context::context; -use slog::warn; -use std::fs::create_dir_all; -use std::net::{IpAddr, SocketAddr}; - -/// Starts the bootstrap server. -#[derive(Parser, Clone)] -pub struct BootstrapOpts { - /// Specifies the IP address that the bootstrap server listens on. Defaults to 127.0.0.1. - #[arg(long)] - ip: Option, - - /// Specifies the port number that the bootstrap server listens on. Defaults to 8081. - #[arg(long)] - port: Option, - - #[command(flatten)] - network: NetworkOpt, - - /// Specifies the maximum number of seconds that the bootstrap server - /// will wait for upstream requests to complete. Defaults to 30. - #[arg(long)] - timeout: Option, -} - -/// Runs the bootstrap server. -pub fn exec( - env: &dyn Environment, - BootstrapOpts { - ip, - port, - network, - timeout, - }: BootstrapOpts, -) -> DfxResult { - warn!( - env.get_logger(), - "The bootstrap command is deprecated. \ - Please use the start command instead. \ - If you have a good reason to use the bootstrap command, \ - please contribute to the discussion at https://github.com/dfinity/sdk/discussions/3163" - ); - let network_descriptor = create_network_descriptor( - env.get_config(), - env.get_networks_config(), - network.to_network_name(), - Some(env.get_logger().clone()), - LocalBindDetermination::AsConfigured, - )?; - let network_descriptor = - apply_arguments(network_descriptor, ip, port.as_deref(), timeout.as_deref())?; - let local_server_descriptor = network_descriptor.local_server_descriptor()?; - local_server_descriptor.describe_bootstrap(env.get_logger()); - let config_bootstrap = &local_server_descriptor.bootstrap; - - create_dir_all(&local_server_descriptor.data_directory).with_context(|| { - format!( - "Failed to create network temp directory {}.", - local_server_descriptor.data_directory.to_string_lossy() - ) - })?; - - let icx_proxy_pid_file_path = local_server_descriptor.icx_proxy_pid_path(); - - let replica_urls = network_descriptor.get_replica_urls(Some(env.get_logger()))?; - - // Since the user may have provided port "0", we need to grab a dynamically - // allocated port and construct a resuable SocketAddr which the actix - // HttpServer will bind to - let socket_addr = get_reusable_socket_addr(config_bootstrap.ip, config_bootstrap.port) - .context("Failed to find socket address for the HTTP server.")?; - - let webserver_port_path = local_server_descriptor.webserver_port_path(); - std::fs::write(&webserver_port_path, "").with_context(|| { - format!( - "Failed to write/clear webserver port file {}.", - webserver_port_path.to_string_lossy() - ) - })?; - std::fs::write(&webserver_port_path, socket_addr.port().to_string()).with_context(|| { - format!( - "Failed to write port to webserver port file {}.", - webserver_port_path.to_string_lossy() - ) - })?; - - verify_unique_ports(&replica_urls, &socket_addr)?; - - let system = actix::System::new(); - let _proxy = system - .block_on(async move { - let shutdown_controller = start_shutdown_controller(env)?; - - let icx_proxy_config = IcxProxyConfig { - bind: socket_addr, - replica_urls, - fetch_root_key: !network_descriptor.is_ic, - verbose: env.get_verbose_level() > 0, - }; - - let port_ready_subscribe = None; - let proxy = start_icx_proxy_actor( - env, - icx_proxy_config, - port_ready_subscribe, - shutdown_controller, - icx_proxy_pid_file_path, - )?; - Ok::<_, Error>(proxy) - }) - .context("Failed to start proxy.")?; - system.run().context("Failed to run system runner.")?; - - Ok(()) -} - -#[context("Cannot bind to and serve from the same port.")] -fn verify_unique_ports(providers: &[url::Url], bind: &SocketAddr) -> DfxResult { - // Verify that we cannot bind to a port that we forward to. - let bound_port = bind.port(); - let bind_and_forward_on_same_port = providers.iter().any(|url| { - Some(bound_port) == url.port() - && match url.host_str() { - Some(h) => h == "localhost" || h == "::1" || h == "127.0.0.1", - None => true, - } - }); - if bind_and_forward_on_same_port { - return Err(anyhow!( - "Cannot forward API calls to the same bootstrap server." - )); - } - Ok(()) -} - -#[context("Failed to determine bootstrap server configuration.")] -fn apply_arguments( - network_descriptor: NetworkDescriptor, - ip: Option, - port: Option<&str>, - timeout: Option<&str>, -) -> DfxResult { - let _ = network_descriptor.local_server_descriptor()?; - let mut local_server_descriptor = network_descriptor.local_server_descriptor.unwrap(); - - if let Some(ip) = ip { - let ip: IpAddr = ip - .parse() - .context("Invalid argument: Invalid IP address.")?; - local_server_descriptor = local_server_descriptor.with_bootstrap_ip(ip); - } - - if let Some(port) = port { - let port: u16 = port - .parse() - .context("Invalid argument: Invalid port number.")?; - local_server_descriptor = local_server_descriptor.with_bootstrap_port(port); - } - - if let Some(timeout) = timeout { - let timeout: u64 = timeout - .parse() - .context("Invalid argument: Invalid timeout.")?; - local_server_descriptor = local_server_descriptor.with_bootstrap_timeout(timeout); - } - - Ok(NetworkDescriptor { - local_server_descriptor: Some(local_server_descriptor), - ..network_descriptor - }) -} diff --git a/src/dfx/src/commands/canister/call.rs b/src/dfx/src/commands/canister/call.rs index c8bfc11fbe..9f8eb53418 100644 --- a/src/dfx/src/commands/canister/call.rs +++ b/src/dfx/src/commands/canister/call.rs @@ -111,7 +111,6 @@ async fn do_wallet_call(wallet: &WalletCanister<'_>, args: &CallIn) -> DfxResult wallet.update_("wallet_call").with_arg(args64) }; let (result,): (Result,) = builder - .with_arg(args) .build() .call_and_wait() .await diff --git a/src/dfx/src/commands/canister/create.rs b/src/dfx/src/commands/canister/create.rs index 28ff893e15..1e40a05e47 100644 --- a/src/dfx/src/commands/canister/create.rs +++ b/src/dfx/src/commands/canister/create.rs @@ -2,7 +2,8 @@ use crate::lib::deps::get_pull_canisters_in_config; use crate::lib::environment::Environment; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::ic_attributes::{ - get_compute_allocation, get_freezing_threshold, get_memory_allocation, CanisterSettings, + get_compute_allocation, get_freezing_threshold, get_memory_allocation, + get_reserved_cycles_limit, CanisterSettings, }; use crate::lib::identity::wallet::get_or_create_wallet_canister; use crate::lib::operations::canister::{create_canister, Funding}; @@ -10,12 +11,13 @@ use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::clap::parsers::cycle_amount_parser; use crate::util::clap::parsers::{ compute_allocation_parser, freezing_threshold_parser, memory_allocation_parser, + reserved_cycles_limit_parser, }; use anyhow::{bail, Context}; use byte_unit::Byte; use candid::Principal as CanisterId; use clap::{ArgAction, Parser}; -use dfx_core::error::identity::IdentityError::GetIdentityPrincipalFailed; +use dfx_core::error::identity::instantiate_identity_from_name::InstantiateIdentityFromNameError::GetIdentityPrincipalFailed; use dfx_core::identity::CallSender; use ic_agent::Identity as _; use slog::info; @@ -62,6 +64,18 @@ pub struct CanisterCreateOpts { #[arg(long, value_parser = freezing_threshold_parser, hide = true)] freezing_threshold: Option, + /// Specifies the upper limit of the canister's reserved cycles balance. + /// + /// Reserved cycles are cycles that the system sets aside for future use by the canister. + /// If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, + /// the system sets aside some amount of cycles from the main balance of the canister. + /// These reserved cycles will be used to cover future payments for the newly allocated bytes. + /// The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is. + /// + /// A setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB. + #[arg(long, value_parser = reserved_cycles_limit_parser, hide = true)] + reserved_cycles_limit: Option, + /// Performs the call with the user Identity as the Sender of messages. /// Bypasses the Wallet canister. #[arg(long)] @@ -160,6 +174,12 @@ pub async fn exec( Some(canister_name), ) .with_context(|| format!("Failed to read freezing threshold of {}.", canister_name))?; + let reserved_cycles_limit = get_reserved_cycles_limit( + opts.reserved_cycles_limit, + Some(config_interface), + Some(canister_name), + ) + .with_context(|| format!("Failed to read reserved cycles limit of {}.", canister_name))?; create_canister( env, canister_name, @@ -171,6 +191,7 @@ pub async fn exec( compute_allocation, memory_allocation, freezing_threshold, + reserved_cycles_limit, }, ) .await?; @@ -219,6 +240,14 @@ pub async fn exec( .with_context(|| { format!("Failed to read freezing threshold of {}.", canister_name) })?; + let reserved_cycles_limit = get_reserved_cycles_limit( + opts.reserved_cycles_limit, + Some(config_interface), + Some(canister_name), + ) + .with_context(|| { + format!("Failed to read reserved cycles limit of {}.", canister_name) + })?; create_canister( env, canister_name, @@ -230,6 +259,7 @@ pub async fn exec( compute_allocation, memory_allocation, freezing_threshold, + reserved_cycles_limit, }, ) .await?; diff --git a/src/dfx/src/commands/canister/delete.rs b/src/dfx/src/commands/canister/delete.rs index 1f4e5eff29..88c2b0de58 100644 --- a/src/dfx/src/commands/canister/delete.rs +++ b/src/dfx/src/commands/canister/delete.rs @@ -19,7 +19,7 @@ use dfx_core::identity::CallSender; use fn_error_context::context; use ic_utils::call::AsyncCall; use ic_utils::interfaces::management_canister::attributes::{ - ComputeAllocation, FreezingThreshold, MemoryAllocation, + ComputeAllocation, FreezingThreshold, MemoryAllocation, ReservedCyclesLimit, }; use ic_utils::interfaces::management_canister::builders::InstallMode; use ic_utils::interfaces::management_canister::CanisterStatus; @@ -36,6 +36,7 @@ const DANK_PRINCIPAL: Principal = // "Couldn't send message" when deleting a canister: increase WITHDRAWAL_COST const WITHDRAWAL_COST: u128 = 10_606_030_000; // 5% higher than a value observed ok locally const MAX_MEMORY_ALLOCATION: u64 = 8589934592; +const DEFAULT_RESERVED_CYCLES_LIMIT: u128 = 5_000_000_000_000; /// Deletes a currently stopped canister. #[derive(Parser)] @@ -161,6 +162,9 @@ async fn delete_canister( compute_allocation: Some(ComputeAllocation::try_from(0u8).unwrap()), memory_allocation: Some(MemoryAllocation::try_from(MAX_MEMORY_ALLOCATION).unwrap()), freezing_threshold: Some(FreezingThreshold::try_from(0u8).unwrap()), + reserved_cycles_limit: Some( + ReservedCyclesLimit::try_from(DEFAULT_RESERVED_CYCLES_LIMIT).unwrap(), + ), }; info!(log, "Setting the controller to identity principal."); update_settings(env, canister_id, settings, call_sender).await?; diff --git a/src/dfx/src/commands/canister/request_status.rs b/src/dfx/src/commands/canister/request_status.rs index b28f82e064..ba20dcccc6 100644 --- a/src/dfx/src/commands/canister/request_status.rs +++ b/src/dfx/src/commands/canister/request_status.rs @@ -8,7 +8,7 @@ use backoff::backoff::Backoff; use backoff::ExponentialBackoff; use candid::Principal; use clap::Parser; -use ic_agent::agent::{Replied, RequestStatusResponse}; +use ic_agent::agent::RequestStatusResponse; use ic_agent::{AgentError, RequestId}; use std::str::FromStr; @@ -49,7 +49,7 @@ pub async fn exec(env: &dyn Environment, opts: RequestStatusOpts) -> DfxResult { .or_else(|_| canister_id_store.get(callee_canister))?; let mut retry_policy = ExponentialBackoff::default(); - let Replied::CallReplied(blob) = async { + let blob = async { let mut request_accepted = false; loop { match agent @@ -57,7 +57,7 @@ pub async fn exec(env: &dyn Environment, opts: RequestStatusOpts) -> DfxResult { .await .context("Failed to fetch request status.")? { - RequestStatusResponse::Replied { reply } => return Ok(reply), + RequestStatusResponse::Replied(reply) => return Ok(reply.arg), RequestStatusResponse::Rejected(response) => { return Err(DfxError::new(AgentError::ReplicaError(response))) } diff --git a/src/dfx/src/commands/canister/status.rs b/src/dfx/src/commands/canister/status.rs index 0d08ac04c9..bfa2a4787d 100644 --- a/src/dfx/src/commands/canister/status.rs +++ b/src/dfx/src/commands/canister/status.rs @@ -41,7 +41,13 @@ async fn canister_status( .collect(); controllers.sort(); - info!(log, "Canister status call result for {}.\nStatus: {}\nControllers: {}\nMemory allocation: {}\nCompute allocation: {}\nFreezing threshold: {}\nMemory Size: {:?}\nBalance: {} Cycles\nModule hash: {}", + let reserved_cycles_limit = if let Some(limit) = status.settings.reserved_cycles_limit { + format!("{} Cycles", limit) + } else { + "Not Set".to_string() + }; + + info!(log, "Canister status call result for {}.\nStatus: {}\nControllers: {}\nMemory allocation: {}\nCompute allocation: {}\nFreezing threshold: {}\nMemory Size: {:?}\nBalance: {} Cycles\nReserved: {} Cycles\nReserved Cycles Limit: {}\nModule hash: {}", canister, status.status, controllers.join(" "), @@ -50,6 +56,8 @@ async fn canister_status( status.settings.freezing_threshold, status.memory_size, status.cycles, + status.reserved_cycles, + reserved_cycles_limit, status.module_hash.map_or_else(|| "None".to_string(), |v| format!("0x{}", hex::encode(v))) ); Ok(()) diff --git a/src/dfx/src/commands/canister/update_settings.rs b/src/dfx/src/commands/canister/update_settings.rs index 58d5a1d43d..b06fae6ae9 100644 --- a/src/dfx/src/commands/canister/update_settings.rs +++ b/src/dfx/src/commands/canister/update_settings.rs @@ -2,19 +2,21 @@ use crate::lib::diagnosis::DiagnosedError; use crate::lib::environment::Environment; use crate::lib::error::{DfxError, DfxResult}; use crate::lib::ic_attributes::{ - get_compute_allocation, get_freezing_threshold, get_memory_allocation, CanisterSettings, + get_compute_allocation, get_freezing_threshold, get_memory_allocation, + get_reserved_cycles_limit, CanisterSettings, }; use crate::lib::operations::canister::{get_canister_status, update_settings}; use crate::lib::root_key::fetch_root_key_if_needed; use crate::util::clap::parsers::{ compute_allocation_parser, freezing_threshold_parser, memory_allocation_parser, + reserved_cycles_limit_parser, }; use anyhow::{bail, Context}; use byte_unit::Byte; use candid::Principal as CanisterId; use clap::{ArgAction, Parser}; use dfx_core::cli::ask_for_consent; -use dfx_core::error::identity::IdentityError::GetIdentityPrincipalFailed; +use dfx_core::error::identity::instantiate_identity_from_name::InstantiateIdentityFromNameError::GetIdentityPrincipalFailed; use dfx_core::identity::CallSender; use fn_error_context::context; use ic_agent::identity::Identity; @@ -62,6 +64,18 @@ pub struct UpdateSettingsOpts { #[arg(long, value_parser = freezing_threshold_parser)] freezing_threshold: Option, + /// Sets the upper limit of the canister's reserved cycles balance. + /// + /// Reserved cycles are cycles that the system sets aside for future use by the canister. + /// If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, + /// the system sets aside some amount of cycles from the main balance of the canister. + /// These reserved cycles will be used to cover future payments for the newly allocated bytes. + /// The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is. + /// + /// A setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB. + #[arg(long, value_parser = reserved_cycles_limit_parser)] + reserved_cycles_limit: Option, + /// Freezing thresholds above ~1.5 years require this flag as confirmation. #[arg(long)] confirm_very_long_freezing_threshold: bool, @@ -122,6 +136,8 @@ pub async fn exec( get_memory_allocation(opts.memory_allocation, config_interface, canister_name)?; let freezing_threshold = get_freezing_threshold(opts.freezing_threshold, config_interface, canister_name)?; + let reserved_cycles_limit = + get_reserved_cycles_limit(opts.reserved_cycles_limit, config_interface, canister_name)?; if let Some(added) = &opts.add_controller { let status = get_canister_status(env, canister_id, call_sender).await?; let mut existing_controllers = status.settings.controllers; @@ -153,6 +169,7 @@ pub async fn exec( compute_allocation, memory_allocation, freezing_threshold, + reserved_cycles_limit, }; update_settings(env, canister_id, settings, call_sender).await?; display_controller_update(&opts, canister_name_or_id); @@ -188,6 +205,14 @@ pub async fn exec( .with_context(|| { format!("Failed to get freezing threshold for {}.", canister_name) })?; + let reserved_cycles_limit = get_reserved_cycles_limit( + opts.reserved_cycles_limit, + Some(config_interface), + Some(canister_name), + ) + .with_context(|| { + format!("Failed to get reserved cycles limit for {}.", canister_name) + })?; if let Some(added) = &opts.add_controller { let status = get_canister_status(env, canister_id, call_sender).await?; let mut existing_controllers = status.settings.controllers; @@ -219,6 +244,7 @@ pub async fn exec( compute_allocation, memory_allocation, freezing_threshold, + reserved_cycles_limit, }; update_settings(env, canister_id, settings, call_sender).await?; display_controller_update(&opts, canister_name); diff --git a/src/dfx/src/commands/extension/install.rs b/src/dfx/src/commands/extension/install.rs index db2abd9c95..6435915951 100644 --- a/src/dfx/src/commands/extension/install.rs +++ b/src/dfx/src/commands/extension/install.rs @@ -4,6 +4,7 @@ use crate::lib::error::DfxResult; use clap::Parser; use clap::Subcommand; use dfx_core::error::extension::ExtensionError; +use semver::Version; #[derive(Parser)] pub struct InstallOpts { @@ -12,6 +13,9 @@ pub struct InstallOpts { /// Installs the extension under different name. Useful when installing an extension with the same name as: already installed extension, or a built-in command. #[clap(long)] install_as: Option, + /// Installs a specific version of the extension, bypassing version checks + #[clap(long)] + version: Option, } pub fn exec(env: &dyn Environment, opts: InstallOpts) -> DfxResult<()> { @@ -22,7 +26,11 @@ pub fn exec(env: &dyn Environment, opts: InstallOpts) -> DfxResult<()> { return Err(ExtensionError::CommandAlreadyExists(opts.name).into()); } - mgr.install_extension(&opts.name, opts.install_as.as_deref())?; + mgr.install_extension( + &opts.name, + opts.install_as.as_deref(), + opts.version.as_ref(), + )?; spinner.finish_with_message( format!( "Extension '{}' installed successfully{}", diff --git a/src/dfx/src/commands/replica.rs b/src/dfx/src/commands/replica.rs deleted file mode 100644 index e32dbaa84e..0000000000 --- a/src/dfx/src/commands/replica.rs +++ /dev/null @@ -1,279 +0,0 @@ -use crate::actors::{ - start_btc_adapter_actor, start_canister_http_adapter_actor, start_emulator_actor, - start_replica_actor, start_shutdown_controller, -}; -use crate::commands::start::{ - apply_command_line_parameters, configure_btc_adapter_if_enabled, - configure_canister_http_adapter_if_enabled, empty_writable_path, -}; -use crate::lib::environment::Environment; -use crate::lib::error::DfxResult; -use crate::lib::network::id::write_network_id; -use crate::lib::replica_config::{HttpHandlerConfig, ReplicaConfig}; -use dfx_core::config::model::dfinity::DEFAULT_REPLICA_PORT; -use dfx_core::config::model::local_server_descriptor::LocalServerDescriptor; -use dfx_core::json::{load_json_file, save_json_file}; -use dfx_core::network::provider::{create_network_descriptor, LocalBindDetermination}; -use anyhow::{bail, Context}; -use clap::{ArgAction, Parser}; -use fn_error_context::context; -use slog::warn; -use std::default::Default; -use std::fs; -use std::fs::create_dir_all; -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; -use super::start::CachedConfig; - -/// Starts a local Internet Computer replica. -#[derive(Parser)] -pub struct ReplicaOpts { - /// Specifies the port the local replica should listen to. - #[arg(long)] - port: Option, - - /// Runs a dedicated emulator instead of the replica - #[arg(long)] - emulator: bool, - - /// Address of bitcoind node. Implies --enable-bitcoin. - #[arg(long, conflicts_with("emulator"), action = ArgAction::Append)] - bitcoin_node: Vec, - - /// enable bitcoin integration - #[arg(long, conflicts_with("emulator"))] - enable_bitcoin: bool, - - /// enable canister http requests - #[arg(long, conflicts_with("emulator"))] - enable_canister_http: bool, - - /// The delay (in milliseconds) an update call should take. Lower values may be expedient in CI. - #[arg(long, conflicts_with("emulator"), default_value_t = 600)] - artificial_delay: u32, - - /// Start even if the network config was modified. - #[arg(long)] - force: bool, -} - -/// Gets the configuration options for the Internet Computer replica. -#[context("Failed to get replica config.")] -fn get_config( - local_server_descriptor: &LocalServerDescriptor, - replica_port_path: PathBuf, - state_root: &Path, - artificial_delay: u32, -) -> DfxResult { - let config = &local_server_descriptor.replica; - let port = config.port.unwrap_or(DEFAULT_REPLICA_PORT); - let mut http_handler: HttpHandlerConfig = Default::default(); - if port == 0 { - http_handler.write_port_to = Some(replica_port_path); - } else { - http_handler.port = Some(port); - }; - - let mut replica_config = ReplicaConfig::new( - state_root, - config.subnet_type.unwrap_or_default(), - config.log_level.unwrap_or_default(), - artificial_delay, - ); - replica_config.http_handler = http_handler; - - Ok(replica_config) -} - -/// Start the Internet Computer locally. Spawns a proxy to forward and -/// manage browser requests. Responsible for running the network (one -/// replica at the moment), the proxy, and (if configured) the bitcoin adapter. -pub fn exec( - env: &dyn Environment, - ReplicaOpts { - port, - emulator, - bitcoin_node, - enable_bitcoin, - enable_canister_http, - artificial_delay, - force, - }: ReplicaOpts, -) -> DfxResult { - warn!( - env.get_logger(), - "The replica command is deprecated. \ - Please use the start command instead. \ - If you have a good reason to use the replica command, \ - please contribute to the discussion at https://github.com/dfinity/sdk/discussions/3163" - ); - let system = actix::System::new(); - - let network_descriptor = create_network_descriptor( - env.get_config(), - env.get_networks_config(), - None, - Some(env.get_logger().clone()), - LocalBindDetermination::AsConfigured, - )?; - let network_descriptor = apply_command_line_parameters( - env.get_logger(), - network_descriptor, - None, - port, - enable_bitcoin, - bitcoin_node, - enable_canister_http, - emulator - )?; - - let local_server_descriptor = network_descriptor.local_server_descriptor()?; - local_server_descriptor.describe(env.get_logger()); - - let temp_dir = &local_server_descriptor.data_directory; - create_dir_all(temp_dir).with_context(|| { - format!( - "Failed to create network temp directory {}.", - temp_dir.to_string_lossy() - ) - })?; - if !local_server_descriptor.network_id_path().exists() { - write_network_id(local_server_descriptor)?; - } - - let btc_adapter_pid_file_path = - empty_writable_path(local_server_descriptor.btc_adapter_pid_path())?; - let btc_adapter_config_path = - empty_writable_path(local_server_descriptor.btc_adapter_config_path())?; - let btc_adapter_socket_holder_path = local_server_descriptor.btc_adapter_socket_holder_path(); - let canister_http_adapter_pid_file_path = - empty_writable_path(local_server_descriptor.canister_http_adapter_pid_path())?; - let canister_http_adapter_config_path = - empty_writable_path(local_server_descriptor.canister_http_adapter_config_path())?; - let canister_http_adapter_socket_holder_path = - local_server_descriptor.canister_http_adapter_socket_holder_path(); - - let previous_config_path = local_server_descriptor.effective_config_path(); - - // dfx bootstrap will read these port files to find out which port to use, - // so we need to make sure only one has a valid port in it. - let replica_config_dir = local_server_descriptor.replica_configuration_dir(); - fs::create_dir_all(&replica_config_dir).with_context(|| { - format!( - "Failed to create replica config directory {}.", - replica_config_dir.display() - ) - })?; - let replica_port_path = empty_writable_path(local_server_descriptor.replica_port_path())?; - let emulator_port_path = empty_writable_path(local_server_descriptor.ic_ref_port_path())?; - let state_root = local_server_descriptor.state_dir(); - - let btc_adapter_config = configure_btc_adapter_if_enabled( - local_server_descriptor, - &btc_adapter_config_path, - &btc_adapter_socket_holder_path, - )?; - let btc_adapter_socket_path = btc_adapter_config - .as_ref() - .and_then(|cfg| cfg.get_socket_path()); - - let canister_http_adapter_config = configure_canister_http_adapter_if_enabled( - local_server_descriptor, - &canister_http_adapter_config_path, - &canister_http_adapter_socket_holder_path, - )?; - let canister_http_socket_path = canister_http_adapter_config - .as_ref() - .and_then(|cfg| cfg.get_socket_path()); - let replica_config = { - let mut replica_config = get_config( - local_server_descriptor, - replica_port_path, - &state_root, - artificial_delay, - )?; - if let Some(btc_adapter_config) = btc_adapter_config.as_ref() { - replica_config = replica_config.with_btc_adapter_enabled(); - if let Some(btc_adapter_socket) = btc_adapter_config.get_socket_path() { - replica_config = replica_config.with_btc_adapter_socket(btc_adapter_socket); - } - } - if let Some(canister_http_adapter_config) = canister_http_adapter_config.as_ref() { - replica_config = replica_config.with_canister_http_adapter_enabled(); - if let Some(socket_path) = canister_http_adapter_config.get_socket_path() { - replica_config = replica_config.with_canister_http_adapter_socket(socket_path); - } - } - replica_config - }; - - let effective_config = if emulator { - CachedConfig::emulator() - } else { - CachedConfig::replica(&replica_config) - }; - if !force && previous_config_path.exists() { - let previous_config = load_json_file(&previous_config_path) - .context("Failed to read replica configuration. Run `dfx start` with `--clean`.")?; - if effective_config != previous_config { - bail!("The network configuration was changed. Run `dfx start` with `--clean`.") - } - } - save_json_file(&previous_config_path, &effective_config) - .context("Failed to write replica configuration")?; - - system.block_on(async move { - let shutdown_controller = start_shutdown_controller(env)?; - if emulator { - start_emulator_actor( - env, - local_server_descriptor, - shutdown_controller, - emulator_port_path, - )?; - } else { - let btc_adapter_ready_subscribe = btc_adapter_config - .map(|btc_adapter_config| { - start_btc_adapter_actor( - env, - btc_adapter_config_path, - btc_adapter_config.get_socket_path(), - shutdown_controller.clone(), - btc_adapter_pid_file_path, - ) - }) - .transpose()?; - let canister_http_adapter_ready_subscribe = canister_http_adapter_config - .map(|canister_http_adapter_config| { - start_canister_http_adapter_actor( - env, - canister_http_adapter_config_path, - canister_http_adapter_config.get_socket_path(), - shutdown_controller.clone(), - canister_http_adapter_pid_file_path, - ) - }) - .transpose()?; - - start_replica_actor( - env, - replica_config, - local_server_descriptor, - shutdown_controller, - btc_adapter_ready_subscribe, - canister_http_adapter_ready_subscribe, - )?; - } - DfxResult::Ok(()) - })?; - system.run()?; - - if let Some(btc_adapter_socket_path) = btc_adapter_socket_path { - let _ = std::fs::remove_file(&btc_adapter_socket_path); - } - if let Some(canister_http_socket_path) = canister_http_socket_path { - let _ = std::fs::remove_file(&canister_http_socket_path); - } - - Ok(()) -} diff --git a/src/dfx/src/commands/start.rs b/src/dfx/src/commands/start.rs index 06c799fd36..03515f5740 100644 --- a/src/dfx/src/commands/start.rs +++ b/src/dfx/src/commands/start.rs @@ -227,7 +227,7 @@ pub fn exec( let previous_config_path = local_server_descriptor.effective_config_path(); - // dfx bootstrap will read these port files to find out which port to use, + // dfx info replica-port will read these port files to find out which port to use, // so we need to make sure only one has a valid port in it. let replica_config_dir = local_server_descriptor.replica_configuration_dir(); fs::create_dir_all(&replica_config_dir).with_context(|| { diff --git a/src/dfx/src/lib/builders/motoko.rs b/src/dfx/src/lib/builders/motoko.rs index 0f2fcb8219..3b3f526d38 100644 --- a/src/dfx/src/lib/builders/motoko.rs +++ b/src/dfx/src/lib/builders/motoko.rs @@ -8,6 +8,7 @@ use crate::lib::error::{BuildError, DfxError, DfxResult}; use crate::lib::metadata::names::{CANDID_ARGS, CANDID_SERVICE}; use crate::lib::models::canister::CanisterPool; use crate::lib::package_arguments::{self, PackageArguments}; +use crate::util::assets::management_idl; use anyhow::Context; use candid::Principal as CanisterId; use dfx_core::config::cache::Cache; @@ -38,6 +39,48 @@ impl MotokoBuilder { } } +#[context("Failed to find imports for canister at '{}'.", info.get_main_path().display())] +fn get_imports(cache: &dyn Cache, info: &MotokoCanisterInfo) -> DfxResult> { + #[context("Failed recursive dependency detection at {}.", file.display())] + fn get_imports_recursive( + cache: &dyn Cache, + file: &Path, + result: &mut BTreeSet, + ) -> DfxResult { + if result.contains(&MotokoImport::Relative(file.to_path_buf())) { + return Ok(()); + } + + result.insert(MotokoImport::Relative(file.to_path_buf())); + + let mut command = cache.get_binary_command("moc")?; + let command = command.arg("--print-deps").arg(file); + let output = command + .output() + .with_context(|| format!("Error executing {:#?}", command))?; + let output = String::from_utf8_lossy(&output.stdout); + + for line in output.lines() { + let import = MotokoImport::try_from(line).context("Failed to create MotokoImport.")?; + match import { + MotokoImport::Relative(path) => { + get_imports_recursive(cache, path.as_path(), result)?; + } + _ => { + result.insert(import); + } + } + } + + Ok(()) + } + + let mut result = BTreeSet::new(); + get_imports_recursive(cache, info.get_main_path(), &mut result)?; + + Ok(result) +} + impl CanisterBuilder for MotokoBuilder { #[context("Failed to get dependencies for canister '{}'.", info.get_name())] fn get_dependencies( @@ -45,52 +88,10 @@ impl CanisterBuilder for MotokoBuilder { pool: &CanisterPool, info: &CanisterInfo, ) -> DfxResult> { - let mut result = BTreeSet::new(); let motoko_info = info.as_info::()?; + let imports = get_imports(self.cache.as_ref(), &motoko_info)?; - #[context("Failed recursive dependency detection at {}.", file.to_string_lossy())] - fn find_deps_recursive( - cache: &dyn Cache, - file: &Path, - result: &mut BTreeSet, - ) -> DfxResult { - if result.contains(&MotokoImport::Relative(file.to_path_buf())) { - return Ok(()); - } - - result.insert(MotokoImport::Relative(file.to_path_buf())); - - let mut command = cache.get_binary_command("moc")?; - let command = command.arg("--print-deps").arg(file); - let output = command - .output() - .with_context(|| format!("Error executing {:#?}", command))?; - - let output = String::from_utf8_lossy(&output.stdout); - for line in output.lines() { - let import = - MotokoImport::try_from(line).context("Failed to create MotokoImport.")?; - match import { - MotokoImport::Canister(_) => { - result.insert(import); - } - MotokoImport::Relative(path) => { - find_deps_recursive(cache, path.as_path(), result)?; - } - MotokoImport::Lib(_) => (), - MotokoImport::Ic(_) => (), - } - } - - Ok(()) - } - find_deps_recursive( - self.cache.as_ref(), - motoko_info.get_main_path(), - &mut result, - )?; - - Ok(result + Ok(imports .iter() .filter_map(|import| { if let MotokoImport::Canister(name) = import { @@ -132,6 +133,14 @@ impl CanisterBuilder for MotokoBuilder { std::fs::create_dir_all(idl_dir_path) .with_context(|| format!("Failed to create {}.", idl_dir_path.to_string_lossy()))?; + // If the management canister is being imported, emit the candid file. + if get_imports(cache.as_ref(), &motoko_info)? + .contains(&MotokoImport::Ic("aaaaa-aa".to_string())) + { + let management_idl_path = idl_dir_path.join("aaaaa-aa.did"); + dfx_core::fs::write(management_idl_path, management_idl()?)?; + } + let package_arguments = package_arguments::load(cache.as_ref(), motoko_info.get_packtool())?; diff --git a/src/dfx/src/lib/error/mod.rs b/src/dfx/src/lib/error/mod.rs index 7051e55c7f..576c74fa65 100644 --- a/src/dfx/src/lib/error/mod.rs +++ b/src/dfx/src/lib/error/mod.rs @@ -5,7 +5,6 @@ pub mod project; pub use build::BuildError; pub use dfx_core::error::extension::ExtensionError; -pub use dfx_core::error::identity::IdentityError; pub use notify_create_canister::NotifyCreateCanisterError; pub use notify_top_up::NotifyTopUpError; pub use project::ProjectError; diff --git a/src/dfx/src/lib/ic_attributes/mod.rs b/src/dfx/src/lib/ic_attributes/mod.rs index 77e0cea05f..09d495338e 100644 --- a/src/dfx/src/lib/ic_attributes/mod.rs +++ b/src/dfx/src/lib/ic_attributes/mod.rs @@ -5,7 +5,7 @@ use candid::Principal; use dfx_core::config::model::dfinity::ConfigInterface; use fn_error_context::context; use ic_utils::interfaces::management_canister::attributes::{ - ComputeAllocation, FreezingThreshold, MemoryAllocation, + ComputeAllocation, FreezingThreshold, MemoryAllocation, ReservedCyclesLimit, }; use std::convert::TryFrom; @@ -15,6 +15,7 @@ pub struct CanisterSettings { pub compute_allocation: Option, pub memory_allocation: Option, pub freezing_threshold: Option, + pub reserved_cycles_limit: Option, } #[context("Failed to get compute allocation.")] @@ -80,3 +81,24 @@ pub fn get_freezing_threshold( }) .transpose() } + +#[context("Failed to get reserved cycles limit")] +pub fn get_reserved_cycles_limit( + reserved_cycles_limit: Option, + config_interface: Option<&ConfigInterface>, + canister_name: Option<&str>, +) -> DfxResult> { + let reserved_cycles_limit = match (reserved_cycles_limit, config_interface, canister_name) { + (Some(reserved_cycles_limit), _, _) => Some(reserved_cycles_limit), + (None, Some(config_interface), Some(canister_name)) => { + config_interface.get_reserved_cycles_limit(canister_name)? + } + _ => None, + }; + reserved_cycles_limit + .map(|arg| { + ReservedCyclesLimit::try_from(arg) + .context("Must be a limit between 0 and 2^128-1 inclusive.") + }) + .transpose() +} diff --git a/src/dfx/src/lib/identity/wallet.rs b/src/dfx/src/lib/identity/wallet.rs index 1860f4beb5..93b0c0b907 100644 --- a/src/dfx/src/lib/identity/wallet.rs +++ b/src/dfx/src/lib/identity/wallet.rs @@ -6,7 +6,7 @@ use crate::Environment; use anyhow::{anyhow, bail, Context}; use candid::Principal; use dfx_core::canister::build_wallet_canister; -use dfx_core::config::directories::get_config_dfx_dir_path; +use dfx_core::config::directories::get_user_dfx_config_dir; use dfx_core::config::model::network_descriptor::{NetworkDescriptor, NetworkTypeDescriptor}; use dfx_core::error::wallet_config::WalletConfigError; use dfx_core::error::wallet_config::WalletConfigError::{ @@ -59,7 +59,7 @@ pub fn get_wallet_config_path( Ok(match &network.r#type { NetworkTypeDescriptor::Persistent | NetworkTypeDescriptor::Playground { .. } => { // Using the global - get_config_dfx_dir_path() + get_user_dfx_config_dir() .map_err(|e| { GetWalletConfigPathFailed( Box::new(name.to_string()), diff --git a/src/dfx/src/lib/migrate.rs b/src/dfx/src/lib/migrate.rs index 62845ab4e0..18d7710868 100644 --- a/src/dfx/src/lib/migrate.rs +++ b/src/dfx/src/lib/migrate.rs @@ -114,6 +114,7 @@ async fn migrate_canister( compute_allocation: None, freezing_threshold: None, memory_allocation: None, + reserved_cycles_limit: None, }, },)), 0, diff --git a/src/dfx/src/lib/operations/canister/create_canister.rs b/src/dfx/src/lib/operations/canister/create_canister.rs index 33d9da18ed..8f0ee36133 100644 --- a/src/dfx/src/lib/operations/canister/create_canister.rs +++ b/src/dfx/src/lib/operations/canister/create_canister.rs @@ -191,6 +191,7 @@ async fn create_with_management_canister( .with_optional_compute_allocation(settings.compute_allocation) .with_optional_memory_allocation(settings.memory_allocation) .with_optional_freezing_threshold(settings.freezing_threshold) + .with_optional_reserved_cycles_limit(settings.reserved_cycles_limit) .call_and_wait() .await; const NEEDS_WALLET: &str = "In order to create a canister on this network, you must use a wallet in order to allocate cycles to the new canister. \ @@ -222,6 +223,10 @@ async fn create_with_wallet( ) -> DfxResult { let wallet = build_wallet_canister(*wallet_id, agent).await?; let cycles = with_cycles.unwrap_or(CANISTER_CREATE_FEE + CANISTER_INITIAL_CYCLE_BALANCE); + if settings.reserved_cycles_limit.is_some() { + bail!( + "Cannot create a canister using a wallet if the reserved_cycles_limit is set. Please create with --no-wallet or use dfx canister update-settings instead.") + } match wallet .wallet_create_canister( cycles, diff --git a/src/dfx/src/lib/operations/canister/deploy_canisters.rs b/src/dfx/src/lib/operations/canister/deploy_canisters.rs index 91eaa95e39..d6ab10326f 100644 --- a/src/dfx/src/lib/operations/canister/deploy_canisters.rs +++ b/src/dfx/src/lib/operations/canister/deploy_canisters.rs @@ -23,7 +23,7 @@ use dfx_core::config::model::dfinity::Config; use dfx_core::identity::CallSender; use fn_error_context::context; use ic_utils::interfaces::management_canister::attributes::{ - ComputeAllocation, FreezingThreshold, MemoryAllocation, + ComputeAllocation, FreezingThreshold, MemoryAllocation, ReservedCyclesLimit, }; use ic_utils::interfaces::management_canister::builders::InstallMode; use slog::info; @@ -273,6 +273,12 @@ async fn register_canisters( FreezingThreshold::try_from(arg.as_secs()) .expect("Freezing threshold must be between 0 and 2^64-1, inclusively.") }); + let reserved_cycles_limit = config_interface + .get_reserved_cycles_limit(canister_name)? + .map(|arg| { + ReservedCyclesLimit::try_from(arg) + .expect("Reserved cycles limit must be between 0 and 2^128-1, inclusively.") + }); let controllers = None; create_canister( env, @@ -285,6 +291,7 @@ async fn register_canisters( compute_allocation, memory_allocation, freezing_threshold, + reserved_cycles_limit, }, ) .await?; diff --git a/src/dfx/src/lib/operations/canister/mod.rs b/src/dfx/src/lib/operations/canister/mod.rs index c89b7b72a2..f87fad3c98 100644 --- a/src/dfx/src/lib/operations/canister/mod.rs +++ b/src/dfx/src/lib/operations/canister/mod.rs @@ -185,6 +185,10 @@ pub async fn update_settings( .freezing_threshold .map(u64::from) .map(candid::Nat::from), + reserved_cycles_limit: settings + .reserved_cycles_limit + .map(u128::from) + .map(candid::Nat::from), }, }, call_sender, diff --git a/src/dfx/src/lib/operations/canister/motoko_playground.rs b/src/dfx/src/lib/operations/canister/motoko_playground.rs index 6dc644f302..b53293582c 100644 --- a/src/dfx/src/lib/operations/canister/motoko_playground.rs +++ b/src/dfx/src/lib/operations/canister/motoko_playground.rs @@ -1,4 +1,6 @@ -use dfx_core::config::model::network_descriptor::NetworkTypeDescriptor; +use dfx_core::config::model::network_descriptor::{ + NetworkTypeDescriptor, MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID, +}; use num_traits::ToPrimitive; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -49,6 +51,25 @@ pub struct InstallArgs<'a> { pub mode: InstallMode, pub canister_id: Principal, } +#[derive(CandidType)] +struct InstallConfig<'a> { + profiling: bool, + is_whitelisted: bool, + origin: Origin<'a>, +} +#[derive(CandidType)] +struct Origin<'a> { + origin: &'a str, + tags: &'a [&'a str], +} +impl<'a> Origin<'a> { + fn new() -> Self { + Self { + origin: "dfx", + tags: &[], + } + } +} #[context("Failed to reserve canister '{}'.", canister_name)] pub async fn reserve_canister_with_playground( @@ -67,9 +88,13 @@ pub async fn reserve_canister_with_playground( } else { bail!("Trying to reserve canister with playground on non-playground network.") }; + if ci_info::is_ci() && playground_canister == MAINNET_MOTOKO_PLAYGROUND_CANISTER_ID { + bail!("Cannot reserve playground canister in CI, please run `dfx start` to use the local replica.") + } + let mut canister_id_store = env.get_canister_id_store()?; let (timestamp, nonce) = create_nonce(); - let get_can_arg = Encode!(&GetCanisterIdArgs { timestamp, nonce })?; + let get_can_arg = Encode!(&GetCanisterIdArgs { timestamp, nonce }, &Origin::new())?; let result = agent .update(&playground_canister, "getCanisterId") .with_arg(get_can_arg) @@ -148,7 +173,12 @@ pub async fn playground_install_code( mode, canister_id: canister_info.id, }; - let encoded_arg = encode_args((canister_info, install_arg, false, is_asset_canister))?; + let install_config = InstallConfig { + profiling: false, + is_whitelisted: is_asset_canister, + origin: Origin::new(), + }; + let encoded_arg = encode_args((canister_info, install_arg, install_config))?; let result = agent .update(&playground_canister, "installCode") .with_arg(encoded_arg.as_slice()) diff --git a/src/dfx/src/util/assets.rs b/src/dfx/src/util/assets.rs index 9500024f54..d4a417368a 100644 --- a/src/dfx/src/util/assets.rs +++ b/src/dfx/src/util/assets.rs @@ -107,3 +107,206 @@ pub fn bitcoin_wasm(logger: &slog::Logger) -> DfxResult> { bail!("Failed to find bitcoin canister archive entry"); } } + +pub fn management_idl() -> DfxResult { + // FIXME get idl from replica when it's available + // Pulled from https://github.com/dfinity/interface-spec/blob/master/spec/_attachments/ic.did + Ok(r##" +type canister_id = principal; +type wasm_module = blob; + +type canister_settings = record { + controllers : opt vec principal; + compute_allocation : opt nat; + memory_allocation : opt nat; + freezing_threshold : opt nat; +}; + +type definite_canister_settings = record { + controllers : vec principal; + compute_allocation : nat; + memory_allocation : nat; + freezing_threshold : nat; +}; + +type change_origin = variant { + from_user : record { + user_id : principal; + }; + from_canister : record { + canister_id : principal; + canister_version : opt nat64; + }; +}; + +type change_details = variant { + creation : record { + controllers : vec principal; + }; + code_uninstall; + code_deployment : record { + mode : variant {install; reinstall; upgrade}; + module_hash : blob; + }; + controllers_change : record { + controllers : vec principal; + }; +}; + +type change = record { + timestamp_nanos : nat64; + canister_version : nat64; + origin : change_origin; + details : change_details; +}; + +type http_header = record { name: text; value: text }; + +type http_response = record { + status: nat; + headers: vec http_header; + body: blob; +}; + +type ecdsa_curve = variant { secp256k1; }; + +type satoshi = nat64; + +type bitcoin_network = variant { + mainnet; + testnet; +}; + +type bitcoin_address = text; + +type block_hash = blob; + +type outpoint = record { + txid : blob; + vout : nat32 +}; + +type utxo = record { + outpoint: outpoint; + value: satoshi; + height: nat32; +}; + +type get_utxos_request = record { + address : bitcoin_address; + network: bitcoin_network; + filter: opt variant { + min_confirmations: nat32; + page: blob; + }; +}; + +type get_current_fee_percentiles_request = record { + network: bitcoin_network; +}; + +type get_utxos_response = record { + utxos: vec utxo; + tip_block_hash: block_hash; + tip_height: nat32; + next_page: opt blob; +}; + +type get_balance_request = record { + address : bitcoin_address; + network: bitcoin_network; + min_confirmations: opt nat32; +}; + +type send_transaction_request = record { + transaction: blob; + network: bitcoin_network; +}; + +type millisatoshi_per_byte = nat64; + +service ic : { + create_canister : (record { + settings : opt canister_settings; + sender_canister_version : opt nat64; + }) -> (record {canister_id : canister_id}); + update_settings : (record { + canister_id : principal; + settings : canister_settings; + sender_canister_version : opt nat64; + }) -> (); + install_code : (record { + mode : variant {install; reinstall; upgrade}; + canister_id : canister_id; + wasm_module : wasm_module; + arg : blob; + sender_canister_version : opt nat64; + }) -> (); + uninstall_code : (record { + canister_id : canister_id; + sender_canister_version : opt nat64; + }) -> (); + start_canister : (record {canister_id : canister_id}) -> (); + stop_canister : (record {canister_id : canister_id}) -> (); + canister_status : (record {canister_id : canister_id}) -> (record { + status : variant { running; stopping; stopped }; + settings: definite_canister_settings; + module_hash: opt blob; + memory_size: nat; + cycles: nat; + idle_cycles_burned_per_day: nat; + }); + canister_info : (record { + canister_id : canister_id; + num_requested_changes : opt nat64; + }) -> (record { + total_num_changes : nat64; + recent_changes : vec change; + module_hash : opt blob; + controllers : vec principal; + }); + delete_canister : (record {canister_id : canister_id}) -> (); + deposit_cycles : (record {canister_id : canister_id}) -> (); + raw_rand : () -> (blob); + http_request : (record { + url : text; + max_response_bytes: opt nat64; + method : variant { get; head; post }; + headers: vec http_header; + body : opt blob; + transform : opt record { + function : func (record {response : http_response; context : blob}) -> (http_response) query; + context : blob + }; + }) -> (http_response); + + // Threshold ECDSA signature + ecdsa_public_key : (record { + canister_id : opt canister_id; + derivation_path : vec blob; + key_id : record { curve: ecdsa_curve; name: text }; + }) -> (record { public_key : blob; chain_code : blob; }); + sign_with_ecdsa : (record { + message_hash : blob; + derivation_path : vec blob; + key_id : record { curve: ecdsa_curve; name: text }; + }) -> (record { signature : blob }); + + // bitcoin interface + bitcoin_get_balance: (get_balance_request) -> (satoshi); + bitcoin_get_utxos: (get_utxos_request) -> (get_utxos_response); + bitcoin_send_transaction: (send_transaction_request) -> (); + bitcoin_get_current_fee_percentiles: (get_current_fee_percentiles_request) -> (vec millisatoshi_per_byte); + + // provisional interfaces for the pre-ledger world + provisional_create_canister_with_cycles : (record { + amount: opt nat; + settings : opt canister_settings; + specified_id: opt canister_id; + sender_canister_version : opt nat64; + }) -> (record {canister_id : canister_id}); + provisional_top_up_canister : + (record { canister_id: canister_id; amount: nat }) -> (); +} +"##.to_string()) +} diff --git a/src/dfx/src/util/clap/parsers.rs b/src/dfx/src/util/clap/parsers.rs index e8b5f8360c..290e3da662 100644 --- a/src/dfx/src/util/clap/parsers.rs +++ b/src/dfx/src/util/clap/parsers.rs @@ -84,6 +84,12 @@ pub fn freezing_threshold_parser(freezing_threshold: &str) -> Result Result { + reserved_cycles_limit + .parse::() + .map_err(|_| "Must be a value between 0 and 2^128-1 inclusive".to_string()) +} + /// Validate a String can be a valid project name. /// A project name is valid if it starts with a letter, and is alphanumeric (with hyphens). /// It cannot end with a dash. diff --git a/src/dfx/src/util/mod.rs b/src/dfx/src/util/mod.rs index 7f18abbeee..98787ea76e 100644 --- a/src/dfx/src/util/mod.rs +++ b/src/dfx/src/util/mod.rs @@ -27,8 +27,8 @@ pub mod stderr_wrapper; const DECIMAL_POINT: char = '.'; -// The user can pass in port "0" to dfx start or dfx bootstrap i.e. "127.0.0.1:0" or "[::1]:0", -// thus, we need to recreate SocketAddr with the kernel provided dynmically allocated port here. +// The user can pass in port "0" to dfx start i.e. "127.0.0.1:0" or "[::1]:0", +// thus, we need to recreate SocketAddr with the kernel-provided dynamically allocated port here. // TcpBuilder is used with reuse_address and reuse_port set to "true" because // the Actix HttpServer in webserver.rs will bind to this SocketAddr. #[context("Failed to find reusable socket address")] diff --git a/src/distributed/assetstorage.wasm.gz b/src/distributed/assetstorage.wasm.gz index f0773cf036..db405979dd 100755 Binary files a/src/distributed/assetstorage.wasm.gz and b/src/distributed/assetstorage.wasm.gz differ diff --git a/src/distributed/ui.did b/src/distributed/ui.did index cc6b0e9fe3..092cfa44f0 100644 --- a/src/distributed/ui.did +++ b/src/distributed/ui.did @@ -1,5 +1,6 @@ service : { did_to_js : (text) -> (opt text) query; binding : (text, text) -> (opt text) query; - subtype : (text, text) -> (variant {Ok; Err : text}) query; + subtype : (new: text, old: text) -> (variant { Ok: null; Err: text }) query; + merge_init_args : (text, text) -> (opt text) query } diff --git a/src/distributed/ui.wasm b/src/distributed/ui.wasm index da88ab10be..9458baa7fa 100644 Binary files a/src/distributed/ui.wasm and b/src/distributed/ui.wasm differ