diff --git a/.github/workflows/reusable-tests.yaml b/.github/workflows/reusable-tests.yaml index 1c7d815a32..10e875a9b6 100644 --- a/.github/workflows/reusable-tests.yaml +++ b/.github/workflows/reusable-tests.yaml @@ -338,6 +338,9 @@ jobs: name: Test Anchor Init runs-on: ubuntu-latest timeout-minutes: 30 + strategy: + matrix: + template: [mocha, jest, rust, mollusk] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/setup/ @@ -358,7 +361,7 @@ jobs: path: ~/.cargo/bin/ - run: chmod +x ~/.cargo/bin/anchor - - run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix + - run: cd "$(mktemp -d)" && anchor init --test-template ${{ matrix.template }} hello-anchor-${{ matrix.template }} && cd hello-anchor-${{ matrix.template }} && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix - uses: ./.github/actions/git-diff/ test-programs: diff --git a/CHANGELOG.md b/CHANGELOG.md index 420ef71e7f..754731e34e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ The minor version will be incremented upon a breaking change and the patch versi - avm: Add short alias for `install` and `list` commands ([#3326](https://github.com/coral-xyz/anchor/pull/3326)). - avm: Add Windows support for renaming anchor binary ([#3325](https://github.com/coral-xyz/anchor/pull/3325)). - cli: Add optional `package-manager` flag in `init` command to set package manager field in Anchor.toml ([#3328](https://github.com/coral-xyz/anchor/pull/3328)). +- cli: Add test template for [Mollusk](https://github.com/buffalojoec/mollusk) ([#3352](https://github.com/coral-xyz/anchor/pull/3352)). ### Fixes diff --git a/cli/src/lib.rs b/cli/src/lib.rs index de9d569654..52b22d724d 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -1036,7 +1036,11 @@ fn init( if solidity { solidity_template::create_program(&project_name)?; } else { - rust_template::create_program(&project_name, template)?; + rust_template::create_program( + &project_name, + template, + TestTemplate::Mollusk == test_template, + )?; } // Build the migrations directory. @@ -1155,7 +1159,7 @@ fn new( if solidity { solidity_template::create_program(&name)?; } else { - rust_template::create_program(&name, template)?; + rust_template::create_program(&name, template, false)?; } programs.insert( diff --git a/cli/src/rust_template.rs b/cli/src/rust_template.rs index a8c954bf64..be11e7c28d 100644 --- a/cli/src/rust_template.rs +++ b/cli/src/rust_template.rs @@ -29,11 +29,14 @@ pub enum ProgramTemplate { } /// Create a program from the given name and template. -pub fn create_program(name: &str, template: ProgramTemplate) -> Result<()> { +pub fn create_program(name: &str, template: ProgramTemplate, with_mollusk: bool) -> Result<()> { let program_path = Path::new("programs").join(name); let common_files = vec![ ("Cargo.toml".into(), workspace_manifest().into()), - (program_path.join("Cargo.toml"), cargo_toml(name)), + ( + program_path.join("Cargo.toml"), + cargo_toml(name, with_mollusk), + ), (program_path.join("Xargo.toml"), xargo_toml().into()), ]; @@ -171,7 +174,17 @@ codegen-units = 1 "# } -fn cargo_toml(name: &str) -> String { +fn cargo_toml(name: &str, with_mollusk: bool) -> String { + let test_sbf_feature = if with_mollusk { r#"test-sbf = []"# } else { "" }; + let dev_dependencies = if with_mollusk { + r#" +[dev-dependencies] +mollusk-svm = "=0.0.6-solana-1.18" +"# + } else { + "" + }; + format!( r#"[package] name = "{0}" @@ -190,13 +203,17 @@ no-entrypoint = [] no-idl = [] no-log-ix-name = [] idl-build = ["anchor-lang/idl-build"] +{2} [dependencies] -anchor-lang = "{2}" +anchor-lang = "{3}" +{4} "#, name, name.to_snake_case(), + test_sbf_feature, VERSION, + dev_dependencies, ) } @@ -610,6 +627,8 @@ pub enum TestTemplate { Jest, /// Generate template for Rust unit-test Rust, + /// Generate template for Mollusk Rust unit-test + Mollusk, } impl TestTemplate { @@ -636,6 +655,7 @@ impl TestTemplate { } } Self::Rust => "cargo test".to_owned(), + Self::Mollusk => "cargo test-sbf".to_owned(), } } @@ -707,6 +727,19 @@ impl TestTemplate { )); override_or_create_files(&files)?; } + Self::Mollusk => { + // Build the test suite. + let tests_path_str = format!("programs/{}/tests", &project_name); + let tests_path = Path::new(&tests_path_str); + fs::create_dir_all(tests_path)?; + + let mut files = Vec::new(); + files.extend(create_program_template_mollusk_test( + project_name, + tests_path, + )); + override_or_create_files(&files)?; + } } Ok(()) @@ -778,3 +811,35 @@ fn test_initialize() {{ ), ] } + +/// Generate template for Mollusk Rust unit-test +fn create_program_template_mollusk_test(name: &str, tests_path: &Path) -> Files { + vec![( + tests_path.join("test_initialize.rs"), + format!( + r#"#![cfg(feature = "test-sbf")] + +use {{ + anchor_lang::{{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}}, + mollusk_svm::{{result::Check, Mollusk}}, +}}; + +#[test] +fn test_initialize() {{ + let program_id = {0}::id(); + + let mollusk = Mollusk::new(&program_id, "{0}"); + + let instruction = Instruction::new_with_bytes( + program_id, + &{0}::instruction::Initialize {{}}.data(), + {0}::accounts::Initialize {{}}.to_account_metas(None), + ); + + mollusk.process_and_validate_instruction(&instruction, &[], &[Check::success()]); +}} +"#, + name.to_snake_case(), + ), + )] +}