Support Deno "import map" in tests
Some libraries in denoland require an import map to be specified. For example, secp256k1.
I patched my local instance of Clarinet to support this via my manifest file:
diff --git a/components/clarinet-cli/src/runner/deno.rs b/components/clarinet-cli/src/runner/deno.rs
index c8b5c7d..9a5ae8a 100644
--- a/components/clarinet-cli/src/runner/deno.rs
+++ b/components/clarinet-cli/src/runner/deno.rs
@@ -65,6 +65,7 @@ pub async fn do_run_scripts(
let mut flags = Flags::default();
flags.unstable = true;
flags.reload = true;
+ flags.import_map_path = manifest.project.deno_import_file.clone();
if allow_disk_write {
let mut write_path_location = manifest.location.get_project_root_location().unwrap();
write_path_location.append_path("artifacts");
diff --git a/components/clarinet-files/src/project_manifest.rs b/components/clarinet-files/src/project_manifest.rs
index 4353df6..8338e2e 100644
--- a/components/clarinet-files/src/project_manifest.rs
+++ b/components/clarinet-files/src/project_manifest.rs
@@ -18,7 +18,7 @@ pub struct ProjectConfigFile {
telemetry: Option<bool>,
requirements: Option<Value>,
boot_contracts: Option<Vec<String>>,
-
+ deno_import_file: Option<String>,
// The fields below have been moved into repl above, but are kept here for
// backwards compatibility.
analysis: Option<Vec<clarity_repl::analysis::Pass>>,
@@ -47,6 +47,7 @@ pub struct ProjectConfig {
pub requirements: Option<Vec<RequirementConfig>>,
pub cache_location: FileLocation,
pub boot_contracts: Vec<String>,
+ pub deno_import_file: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
@@ -124,6 +125,7 @@ impl ProjectManifest {
format!("costs-v{}", repl_settings.costs_version),
"bns".to_string(),
]),
+ deno_import_file: project_manifest_file.project.deno_import_file,
};
let mut config = ProjectManifest {
It's a pretty simple patch, but I think I ran into some other issues related either to TSC versions or the deno vendoring:
In sample_test.ts:
import * as secp from "https://deno.land/x/secp256k1/mod.ts";
$ clarinet test
TS2339 [ERROR]: Property 'cause' does not exist on type 'PartialReadError'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:432:13
TS2339 [ERROR]: Property 'cause' does not exist on type 'Error'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:432:25
TS2339 [ERROR]: Property 'cause' does not exist on type 'PartialReadError'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:618:13
TS2339 [ERROR]: Property 'cause' does not exist on type 'Error'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:618:25
TS2339 [ERROR]: Property 'cause' does not exist on type 'PartialReadError'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:663:13
TS2339 [ERROR]: Property 'cause' does not exist on type 'Error'.
e.cause = err.cause;
~~~~~
at https://deno.land/[email protected]/io/buffer.ts:663:25
TS2345 [ERROR]: Argument of type 'Hex' is not assignable to parameter of type 'Uint8Array'.
Type 'string' is not assignable to type 'Uint8Array'.
const str = arr ? bytesToHex(hex) : hex;
~~~
at https://deno.land/x/[email protected]/index.ts:598:34
TS2345 [ERROR]: Argument of type 'string | Uint8Array' is not assignable to parameter of type 'string'.
Type 'Uint8Array' is not assignable to type 'string'.
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128)));
~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/index.ts:600:38
TS2345 [ERROR]: Argument of type 'string | Uint8Array' is not assignable to parameter of type 'string'.
Type 'Uint8Array' is not assignable to type 'string'.
return new Signature(hexToNumber(str.slice(0, 64)), hexToNumber(str.slice(64, 128)));
~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/index.ts:600:69
TS2345 [ERROR]: Argument of type 'Hex' is not assignable to parameter of type 'Uint8Array'.
Type 'string' is not assignable to type 'Uint8Array'.
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex));
~~~~~~~~~~~~~~~~~~~~~~~~~~~
at https://deno.land/x/[email protected]/index.ts:609:40
TS2345 [ERROR]: Argument of type 'Hex' is not assignable to parameter of type 'string'.
Type 'Uint8Array' is not assignable to type 'string'.
const { r, s } = parseDERSignature(arr ? hex : hexToBytes(hex));
~~~
at https://deno.land/x/[email protected]/index.ts:609:63
TS2322 [ERROR]: Type 'Hex | SchnorrSignature' is not assignable to type 'SchnorrSignature'.
Type 'string' is not assignable to type 'SchnorrSignature'.
const sig: SchnorrSignature = raw ? signature : SchnorrSignature.fromHex(signature);
~~~
at https://deno.land/x/[email protected]/index.ts:1377:9
Found 12 errors.
Whereas the deno cli (v1.23) is perfectly happy with that file:
deno run --import-map=import_map.json ./sample_test.ts
Thanks Aaron! This issue will be be addressed in https://github.com/hirosystems/clarinet/pull/511. It looks functional so this is blocker on your end, you can hop into this branch, install a copy and use
clarinet test --import-map=imports.json
I will try this out in my branch. I used a workaround to get unblocked (basically just hardcoding signature values), but I'll see if this works.
What are your thoughts on configuring the --import-map via the manifest rather than having to pass a command line option? Other settings in Clarinet don't need to be passed through the CLI.
Yep, it worked!
$ clarinet test
./tests/hyperchains/hyperchains_test.ts => Unit test the withdrawal leaf hash calculations using test vectors ... ok (21ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that block can be committed by subnet miner ... ok (29ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can register and setup assets ... ok (26ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can deposit NFT & miner can withdraw it ... ok (36ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can deposit FT & miner can withdraw it ... ok (43ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can withdraw FT minted on hyperchain & L1 miner can mint it ... ok (32ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that withdrawals work with a more complex Merkle tree ... ok (57ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that L1 contract can't mint an NFT first created on the hyperchain if it already exists on the L1 ... ok (20ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can mint an NFT on the hyperchain and L1 miner can withdraw it by minting ... ok (24ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can't withdraw an NFT if nobody owns it, in the `no-mint` case. ... ok (19ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can withdraw an NFT to the original owner, in the `no-mint` case. ... ok (24ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can withdraw an NFT to a different user, in the `no-mint` case. ... ok (24ms)
ok | 12 passed | 0 failed (381ms)
error:: Relative import path "crypto" not prefixed with / or ./ or ../
at https://deno.land/x/[email protected]/index.ts:6:29
$ clarinet test --import-map=./import_map.json --allow-net
./tests/hyperchains/hyperchains_test.ts => Unit test the withdrawal leaf hash calculations using test vectors ... ok (13ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that block can be committed by subnet miner ... ok (24ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can register and setup assets ... ok (26ms)
./tests/hyperchains/multiminer_test.ts => Test multi-party commit when one party submits transactions and other party is signatory ... ok (66ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can deposit NFT & miner can withdraw it ... ok (37ms)
./tests/hyperchains/multiminer_test.ts => Test multi-party commit when 2 parties are signatories ... ok (49ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can deposit FT & miner can withdraw it ... ok (46ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can withdraw FT minted on hyperchain & L1 miner can mint it ... ok (32ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that withdrawals work with a more complex Merkle tree ... ok (56ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that L1 contract can't mint an NFT first created on the hyperchain if it already exists on the L1 ... ok (21ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that user can mint an NFT on the hyperchain and L1 miner can withdraw it by minting ... ok (24ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can't withdraw an NFT if nobody owns it, in the `no-mint` case. ... ok (18ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can withdraw an NFT to the original owner, in the `no-mint` case. ... ok (25ms)
./tests/hyperchains/hyperchains_test.ts => Ensure that a miner can withdraw an NFT to a different user, in the `no-mint` case. ... ok (25ms)
ok | 14 passed | 0 failed (367ms)
Note that the --allow-net CLI option was necessary to avoid a prompt from deno:
$ clarinet test --import-map=./import_map.json
⚠️ ️Deno requests net access to "deno.land". Run again with --allow-net to bypass this prompt.
Allow? [y/n (y = yes allow, n = no deny)] n
It worked without the net access, but to avoid the prompt, I had to add --allow-net. I'm not sure if you want to capture those prompts in Clarinet so that it doesn't break, e.g., CI integrations.