Skip to main content

anchor_cli/
rust_template.rs

1use {
2    crate::{
3        config::ProgramWorkspace, create_files, override_or_create_files, Files, PackageManager,
4        VERSION,
5    },
6    anyhow::Result,
7    clap::{Parser, ValueEnum},
8    heck::{ToLowerCamelCase, ToPascalCase, ToSnakeCase},
9    solana_keypair::{read_keypair_file, write_keypair_file, Keypair},
10    solana_pubkey::Pubkey,
11    solana_signer::Signer,
12    std::{
13        fmt::Write as _,
14        fs::{self, File},
15        io::Write as _,
16        path::Path,
17        process::Stdio,
18    },
19};
20
21const ANCHOR_MSRV: &str = "1.89.0";
22
23/// Program initialization template
24#[derive(Clone, Debug, Default, Eq, PartialEq, Parser, ValueEnum)]
25pub enum ProgramTemplate {
26    /// Program with a single `lib.rs` file (not recommended for production)
27    Single,
28    /// Program with multiple files for instructions, state... (recommended)
29    #[default]
30    Multiple,
31}
32
33/// Create a program from the given name and template.
34pub fn create_program(
35    name: &str,
36    template: ProgramTemplate,
37    test_template: Option<&TestTemplate>,
38) -> Result<()> {
39    let program_path = Path::new("programs").join(name);
40    let common_files = vec![
41        ("Cargo.toml".into(), workspace_manifest().into()),
42        ("rust-toolchain.toml".into(), rust_toolchain_toml()),
43        (
44            program_path.join("Cargo.toml"),
45            cargo_toml(name, test_template),
46        ),
47        // Note: Xargo.toml is no longer needed for modern Solana builds using SBF
48    ];
49
50    let template_files = match template {
51        ProgramTemplate::Single => {
52            println!(
53                "Note: Using single-file template. For better code organization and \
54                 maintainability, consider using --template multiple (default)."
55            );
56            create_program_template_single(name, &program_path)
57        }
58        ProgramTemplate::Multiple => create_program_template_multiple(name, &program_path),
59    };
60
61    create_files(&[common_files, template_files].concat())
62}
63
64/// Helper to create a rust-toolchain.toml at the workspace root
65fn rust_toolchain_toml() -> String {
66    format!(
67        r#"[toolchain]
68channel = "{ANCHOR_MSRV}"
69components = ["rustfmt","clippy"]
70profile = "minimal"
71"#
72    )
73}
74
75/// Create a program with a single `lib.rs` file.
76fn create_program_template_single(name: &str, program_path: &Path) -> Files {
77    vec![(
78        program_path.join("src").join("lib.rs"),
79        format!(
80            r#"use anchor_lang::prelude::*;
81
82declare_id!("{}");
83
84#[program]
85pub mod {} {{
86    use super::*;
87
88    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
89        msg!("Greetings from: {{:?}}", ctx.program_id);
90        Ok(())
91    }}
92}}
93
94#[derive(Accounts)]
95pub struct Initialize {{}}
96"#,
97            get_or_create_program_id(name),
98            name.to_snake_case(),
99        ),
100    )]
101}
102
103/// Create a program with multiple files for instructions, state...
104fn create_program_template_multiple(name: &str, program_path: &Path) -> Files {
105    let src_path = program_path.join("src");
106    vec![
107        (
108            src_path.join("lib.rs"),
109            format!(
110                r#"pub mod constants;
111pub mod error;
112pub mod instructions;
113pub mod state;
114
115use anchor_lang::prelude::*;
116
117pub use constants::*;
118pub use instructions::*;
119pub use state::*;
120
121declare_id!("{}");
122
123#[program]
124pub mod {} {{
125    use super::*;
126
127    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {{
128        initialize::handler(ctx)
129    }}
130}}
131"#,
132                get_or_create_program_id(name),
133                name.to_snake_case(),
134            ),
135        ),
136        (
137            src_path.join("constants.rs"),
138            r#"use anchor_lang::prelude::*;
139
140#[constant]
141pub const SEED: &str = "anchor";
142"#
143            .into(),
144        ),
145        (
146            src_path.join("error.rs"),
147            r#"use anchor_lang::prelude::*;
148
149#[error_code]
150pub enum ErrorCode {
151    #[msg("Custom error message")]
152    CustomError,
153}
154"#
155            .into(),
156        ),
157        (
158            src_path.join("instructions.rs"),
159            r#"pub mod initialize;
160
161pub use initialize::*;
162"#
163            .into(),
164        ),
165        (
166            src_path.join("instructions").join("initialize.rs"),
167            r#"use anchor_lang::prelude::*;
168
169#[derive(Accounts)]
170pub struct Initialize {}
171
172pub fn handler(ctx: Context<Initialize>) -> Result<()> {
173    msg!("Greetings from: {:?}", ctx.program_id);
174    Ok(())
175}
176"#
177            .into(),
178        ),
179        (src_path.join("state.rs"), r#""#.into()),
180    ]
181}
182
183const fn workspace_manifest() -> &'static str {
184    r#"[workspace]
185members = [
186    "programs/*"
187]
188resolver = "2"
189
190[profile.release]
191overflow-checks = true
192lto = "fat"
193codegen-units = 1
194[profile.release.build-override]
195opt-level = 3
196incremental = false
197codegen-units = 1
198"#
199}
200
201fn cargo_toml(name: &str, test_template: Option<&TestTemplate>) -> String {
202    let test_sbf_feature = match test_template {
203        Some(TestTemplate::Mollusk) => r#"test-sbf = []"#,
204        _ => "",
205    };
206    let dev_dependencies = match test_template {
207        Some(TestTemplate::Mollusk) => {
208            r#"
209[dev-dependencies]
210mollusk-svm = "~0.10"
211"#
212        }
213        Some(TestTemplate::Litesvm) => {
214            r#"
215[dev-dependencies]
216litesvm = "0.10.0"
217solana-message = "3.0.1"
218solana-transaction = "3.0.2"
219solana-signer = "3.0.0"
220solana-keypair = "3.0.1"
221"#
222        }
223        _ => "",
224    };
225
226    format!(
227        r#"[package]
228name = "{0}"
229version = "0.1.0"
230description = "Created with Anchor"
231edition = "2021"
232
233[lib]
234crate-type = ["cdylib", "lib"]
235name = "{1}"
236
237[features]
238default = []
239cpi = ["no-entrypoint"]
240no-entrypoint = []
241no-idl = []
242no-log-ix-name = []
243idl-build = ["anchor-lang/idl-build"]
244anchor-debug = []
245custom-heap = []
246custom-panic = []
247{2}
248
249[dependencies]
250anchor-lang = "{3}"
251{4}
252
253[lints.rust]
254unexpected_cfgs = {{ level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] }}
255"#,
256        name,
257        name.to_snake_case(),
258        test_sbf_feature,
259        VERSION,
260        dev_dependencies,
261    )
262}
263
264/// Read the program keypair file or create a new one if it doesn't exist.
265pub fn get_or_create_program_id(name: &str) -> Pubkey {
266    let keypair_path = Path::new("target")
267        .join("deploy")
268        .join(format!("{}-keypair.json", name.to_snake_case()));
269
270    read_keypair_file(&keypair_path)
271        .unwrap_or_else(|_| {
272            let keypair = Keypair::new();
273            write_keypair_file(&keypair, keypair_path).expect("Unable to create program keypair");
274            keypair
275        })
276        .pubkey()
277}
278
279pub fn deploy_js_script_host(cluster_url: &str, script_path: &str) -> String {
280    format!(
281        r#"
282const anchor = require('@anchor-lang/core');
283
284// Deploy script defined by the user.
285const userScript = require("{script_path}");
286
287async function main() {{
288    const connection = new anchor.web3.Connection(
289      "{cluster_url}",
290      anchor.AnchorProvider.defaultOptions().commitment
291    );
292    const wallet = anchor.Wallet.local();
293    const provider = new anchor.AnchorProvider(connection, wallet);
294
295    // Run the user's deploy script.
296    userScript(provider);
297}}
298main();
299"#,
300    )
301}
302
303pub fn deploy_ts_script_host(cluster_url: &str, script_path: &str) -> String {
304    format!(
305        r#"import * as anchor from '@anchor-lang/core';
306
307// Deploy script defined by the user.
308const userScript = require("{script_path}");
309
310async function main() {{
311    const connection = new anchor.web3.Connection(
312      "{cluster_url}",
313      anchor.AnchorProvider.defaultOptions().commitment
314    );
315    const wallet = anchor.Wallet.local();
316    const provider = new anchor.AnchorProvider(connection, wallet);
317
318    // Run the user's deploy script.
319    userScript(provider);
320}}
321main();
322"#,
323    )
324}
325
326pub fn deploy_script() -> &'static str {
327    r#"// Migrations are an early feature. Currently, they're nothing more than this
328// single deploy script that's invoked from the CLI, injecting a provider
329// configured from the workspace's Anchor.toml.
330
331const anchor = require("@anchor-lang/core");
332
333module.exports = async function (provider) {
334  // Configure client to use the provider.
335  anchor.setProvider(provider);
336
337  // Add your deploy script here.
338};
339"#
340}
341
342pub fn ts_deploy_script() -> &'static str {
343    r#"// Migrations are an early feature. Currently, they're nothing more than this
344// single deploy script that's invoked from the CLI, injecting a provider
345// configured from the workspace's Anchor.toml.
346
347import * as anchor from "@anchor-lang/core";
348
349module.exports = async function (provider: anchor.AnchorProvider) {
350  // Configure client to use the provider.
351  anchor.setProvider(provider);
352
353  // Add your deploy script here.
354};
355"#
356}
357
358pub fn mocha(name: &str) -> String {
359    format!(
360        r#"const anchor = require("@anchor-lang/core");
361
362describe("{}", () => {{
363  // Configure the client to use the local cluster.
364  anchor.setProvider(anchor.AnchorProvider.env());
365
366  it("Is initialized!", async () => {{
367    // Add your test here.
368    const program = anchor.workspace.{};
369    const tx = await program.methods.initialize().rpc();
370    console.log("Your transaction signature", tx);
371  }});
372}});
373"#,
374        name,
375        name.to_lower_camel_case(),
376    )
377}
378
379pub fn jest(name: &str) -> String {
380    format!(
381        r#"const anchor = require("@anchor-lang/core");
382
383describe("{}", () => {{
384  // Configure the client to use the local cluster.
385  anchor.setProvider(anchor.AnchorProvider.env());
386
387  it("Is initialized!", async () => {{
388    // Add your test here.
389    const program = anchor.workspace.{};
390    const tx = await program.methods.initialize().rpc();
391    console.log("Your transaction signature", tx);
392  }});
393}});
394"#,
395        name,
396        name.to_lower_camel_case(),
397    )
398}
399
400pub fn package_json(jest: bool, license: String) -> String {
401    if jest {
402        format!(
403            r#"{{
404  "license": "{license}",
405  "scripts": {{
406    "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
407    "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
408  }},
409  "dependencies": {{
410    "@anchor-lang/core": "^{VERSION}"
411  }},
412  "devDependencies": {{
413    "jest": "^29.0.3",
414    "prettier": "^2.6.2"
415  }}
416}}
417    "#
418        )
419    } else {
420        format!(
421            r#"{{
422  "license": "{license}",
423  "scripts": {{
424    "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
425    "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
426  }},
427  "dependencies": {{
428    "@anchor-lang/core": "^{VERSION}"
429  }},
430  "devDependencies": {{
431    "chai": "^4.3.4",
432    "mocha": "^9.0.3",
433    "prettier": "^2.6.2"
434  }}
435}}
436"#
437        )
438    }
439}
440
441pub fn ts_package_json(jest: bool, license: String) -> String {
442    if jest {
443        format!(
444            r#"{{
445  "license": "{license}",
446  "scripts": {{
447    "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
448    "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
449  }},
450  "dependencies": {{
451    "@anchor-lang/core": "^{VERSION}"
452  }},
453  "devDependencies": {{
454    "@types/bn.js": "^5.1.0",
455    "@types/jest": "^29.0.3",
456    "jest": "^29.0.3",
457    "prettier": "^2.6.2",
458    "ts-jest": "^29.0.2",
459    "typescript": "^5.7.3"
460  }}
461}}
462"#
463        )
464    } else {
465        format!(
466            r#"{{
467  "license": "{license}",
468  "scripts": {{
469    "lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
470    "lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
471  }},
472  "dependencies": {{
473    "@anchor-lang/core": "^{VERSION}"
474  }},
475  "devDependencies": {{
476    "chai": "^4.3.4",
477    "mocha": "^9.0.3",
478    "ts-mocha": "^10.0.0",
479    "@types/bn.js": "^5.1.0",
480    "@types/chai": "^4.3.0",
481    "@types/mocha": "^9.0.0",
482    "typescript": "^5.7.3",
483    "prettier": "^2.6.2"
484  }}
485}}
486"#
487        )
488    }
489}
490
491pub fn ts_mocha(name: &str) -> String {
492    format!(
493        r#"import * as anchor from "@anchor-lang/core";
494import {{ Program }} from "@anchor-lang/core";
495import {{ {} }} from "../target/types/{}";
496
497describe("{}", () => {{
498  // Configure the client to use the local cluster.
499  anchor.setProvider(anchor.AnchorProvider.env());
500
501  const program = anchor.workspace.{} as Program<{}>;
502
503  it("Is initialized!", async () => {{
504    // Add your test here.
505    const tx = await program.methods.initialize().rpc();
506    console.log("Your transaction signature", tx);
507  }});
508}});
509"#,
510        name.to_pascal_case(),
511        name.to_snake_case(),
512        name,
513        name.to_lower_camel_case(),
514        name.to_pascal_case(),
515    )
516}
517
518pub fn ts_jest(name: &str) -> String {
519    format!(
520        r#"import * as anchor from "@anchor-lang/core";
521import {{ Program }} from "@anchor-lang/core";
522import {{ {} }} from "../target/types/{}";
523
524describe("{}", () => {{
525  // Configure the client to use the local cluster.
526  anchor.setProvider(anchor.AnchorProvider.env());
527
528  const program = anchor.workspace.{} as Program<{}>;
529
530  it("Is initialized!", async () => {{
531    // Add your test here.
532    const tx = await program.methods.initialize().rpc();
533    console.log("Your transaction signature", tx);
534  }});
535}});
536"#,
537        name.to_pascal_case(),
538        name.to_snake_case(),
539        name,
540        name.to_lower_camel_case(),
541        name.to_pascal_case(),
542    )
543}
544
545pub fn ts_config(jest: bool) -> &'static str {
546    if jest {
547        r#"{
548  "compilerOptions": {
549    "types": ["jest"],
550    "typeRoots": ["./node_modules/@types"],
551    "lib": ["es2015"],
552    "module": "commonjs",
553    "target": "es6",
554    "esModuleInterop": true
555  }
556}
557"#
558    } else {
559        r#"{
560  "compilerOptions": {
561    "types": ["mocha", "chai"],
562    "typeRoots": ["./node_modules/@types"],
563    "lib": ["es2015"],
564    "module": "commonjs",
565    "target": "es6",
566    "esModuleInterop": true
567  }
568}
569"#
570    }
571}
572
573pub fn git_ignore() -> &'static str {
574    r#".anchor
575.DS_Store
576target
577**/*.rs.bk
578node_modules
579test-ledger
580.yarn
581.surfpool
582"#
583}
584
585pub fn prettier_ignore() -> &'static str {
586    r#".anchor
587.DS_Store
588target
589node_modules
590dist
591build
592test-ledger
593"#
594}
595
596pub fn node_shell(
597    cluster_url: &str,
598    wallet_path: &str,
599    programs: Vec<ProgramWorkspace>,
600) -> Result<String> {
601    let mut eval_string = format!(
602        r#"
603const anchor = require('@anchor-lang/core');
604const web3 = anchor.web3;
605const PublicKey = anchor.web3.PublicKey;
606const Keypair = anchor.web3.Keypair;
607
608const __wallet = new anchor.Wallet(
609  Keypair.fromSecretKey(
610    Buffer.from(
611      JSON.parse(
612        require('fs').readFileSync(
613          "{wallet_path}",
614          {{
615            encoding: "utf-8",
616          }},
617        ),
618      ),
619    ),
620  ),
621);
622const __connection = new web3.Connection("{cluster_url}", "processed");
623const provider = new anchor.AnchorProvider(__connection, __wallet, {{
624  commitment: "processed",
625  preflightcommitment: "processed",
626}});
627anchor.setProvider(provider);
628"#,
629    );
630
631    for program in programs {
632        write!(
633            &mut eval_string,
634            r#"
635anchor.workspace.{} = new anchor.Program({}, provider);
636"#,
637            program.name.to_lower_camel_case(),
638            serde_json::to_string(&program.idl)?,
639        )?;
640    }
641
642    Ok(eval_string)
643}
644
645/// Test initialization template
646#[derive(Clone, Debug, Default, Eq, PartialEq, Parser, ValueEnum)]
647pub enum TestTemplate {
648    /// Generate template for Mocha unit-test
649    Mocha,
650    /// Generate template for Jest unit-test
651    Jest,
652    /// Generate template for Rust unit-test
653    Rust,
654    /// Generate template for Mollusk Rust unit-test
655    Mollusk,
656    /// Generate template for LiteSVM rust unit-test
657    #[default]
658    Litesvm,
659}
660
661impl TestTemplate {
662    pub fn get_test_script(&self, js: bool, pkg_manager: &PackageManager) -> String {
663        let pkg_manager_exec_cmd = match pkg_manager {
664            PackageManager::Yarn => "yarn run",
665            PackageManager::NPM => "npx",
666            PackageManager::PNPM => "pnpm exec",
667            PackageManager::Bun => "bunx",
668        };
669
670        match &self {
671            Self::Mocha => {
672                if js {
673                    format!("{pkg_manager_exec_cmd} mocha -t 1000000 tests/")
674                } else {
675                    format!(
676                        r#"{pkg_manager_exec_cmd} ts-mocha -p ./tsconfig.json -t 1000000 "tests/**/*.ts""#
677                    )
678                }
679            }
680            Self::Jest => {
681                if js {
682                    format!("{pkg_manager_exec_cmd} jest")
683                } else {
684                    format!("{pkg_manager_exec_cmd} jest --preset ts-jest")
685                }
686            }
687            Self::Rust | Self::Litesvm => "cargo test".to_owned(),
688            Self::Mollusk => "cargo test-sbf".to_owned(),
689        }
690    }
691
692    pub fn create_test_files(&self, project_name: &str, js: bool, program_id: &str) -> Result<()> {
693        match self {
694            Self::Mocha => {
695                // Build the test suite.
696                fs::create_dir_all("tests")?;
697
698                if js {
699                    let mut test = File::create(format!("tests/{}.js", &project_name))?;
700                    test.write_all(mocha(project_name).as_bytes())?;
701                } else {
702                    let mut mocha = File::create(format!("tests/{}.ts", &project_name))?;
703                    mocha.write_all(ts_mocha(project_name).as_bytes())?;
704                }
705            }
706            Self::Jest => {
707                // Build the test suite.
708                fs::create_dir_all("tests")?;
709
710                let mut test = File::create(format!("tests/{}.test.js", &project_name))?;
711                test.write_all(jest(project_name).as_bytes())?;
712            }
713            Self::Rust => {
714                // Do not initialize git repo
715                let exit = std::process::Command::new("cargo")
716                    .arg("new")
717                    .arg("--vcs")
718                    .arg("none")
719                    .arg("--lib")
720                    .arg("tests")
721                    .stderr(Stdio::inherit())
722                    .output()
723                    .map_err(|e| anyhow::format_err!("{}", e))?;
724                if !exit.status.success() {
725                    eprintln!("'cargo new --lib tests' failed");
726                    std::process::exit(exit.status.code().unwrap_or(1));
727                }
728
729                let mut files = Vec::new();
730                let tests_path = Path::new("tests");
731                files.extend(vec![(
732                    tests_path.join("Cargo.toml"),
733                    tests_cargo_toml(project_name),
734                )]);
735                files.extend(create_program_template_rust_test(
736                    project_name,
737                    tests_path,
738                    program_id,
739                ));
740                override_or_create_files(&files)?;
741            }
742            Self::Mollusk => {
743                // Build the test suite.
744                let tests_path_str = format!("programs/{}/tests", &project_name);
745                let tests_path = Path::new(&tests_path_str);
746                fs::create_dir_all(tests_path)?;
747
748                let mut files = Vec::new();
749                files.extend(create_program_template_mollusk_test(
750                    project_name,
751                    tests_path,
752                ));
753                override_or_create_files(&files)?;
754            }
755
756            Self::Litesvm => {
757                let tests_path_str = format!("programs/{}/tests", &project_name);
758                let tests_path = Path::new(&tests_path_str);
759                fs::create_dir_all(tests_path)?;
760                let mut files = Vec::new();
761                files.extend(create_program_template_litesvm_test(
762                    project_name,
763                    tests_path,
764                ));
765                override_or_create_files(&files)?;
766            }
767        }
768
769        Ok(())
770    }
771}
772
773pub fn tests_cargo_toml(name: &str) -> String {
774    format!(
775        r#"[package]
776name = "tests"
777version = "0.1.0"
778description = "Created with Anchor"
779edition = "2021"
780rust-version = "{ANCHOR_MSRV}"
781
782[dependencies]
783anchor-client = "{VERSION}"
784{name} = {{ version = "0.1.0", path = "../programs/{name}" }}
785solana-keypair = "3.0.0"
786solana-pubkey = "3.0.0"
787"#
788    )
789}
790
791/// Generate template for Rust unit-test
792fn create_program_template_rust_test(name: &str, tests_path: &Path, program_id: &str) -> Files {
793    let src_path = tests_path.join("src");
794    vec![
795        (
796            src_path.join("lib.rs"),
797            r#"#[cfg(test)]
798mod test_initialize;
799"#
800            .into(),
801        ),
802        (
803            src_path.join("test_initialize.rs"),
804            format!(
805                r#"use anchor_client::{{
806    CommitmentConfig,
807    Client, Cluster,
808}};
809use solana_keypair::{{read_keypair_file}};
810use solana_pubkey::Pubkey;
811
812#[test]
813fn test_initialize() {{
814    let program_id = "{0}";
815    let anchor_wallet = std::env::var("ANCHOR_WALLET").unwrap();
816    let payer = read_keypair_file(&anchor_wallet).unwrap();
817
818    let client = Client::new_with_options(Cluster::Localnet, &payer, CommitmentConfig::confirmed());
819    let program_id = Pubkey::try_from(program_id).unwrap();
820    let program = client.program(program_id).unwrap();
821
822    let tx = program
823        .request()
824        .accounts({1}::accounts::Initialize {{}})
825        .args({1}::instruction::Initialize {{}})
826        .send()
827        .expect("");
828
829    println!("Your transaction signature {{}}", tx);
830}}
831"#,
832                program_id,
833                name.to_snake_case(),
834            ),
835        ),
836    ]
837}
838
839/// Generate template for Mollusk Rust unit-test
840fn create_program_template_mollusk_test(name: &str, tests_path: &Path) -> Files {
841    vec![(
842        tests_path.join("test_initialize.rs"),
843        format!(
844            r#"#![cfg(feature = "test-sbf")]
845
846use {{
847    anchor_lang::{{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}},
848    mollusk_svm::{{result::Check, Mollusk}},
849}};
850
851#[test]
852fn test_initialize() {{
853    let program_id = {0}::id();
854
855    let mollusk = Mollusk::new(&program_id, "{0}");
856
857    let instruction = Instruction::new_with_bytes(
858        program_id,
859        &{0}::instruction::Initialize {{}}.data(),
860        {0}::accounts::Initialize {{}}.to_account_metas(None),
861    );
862
863    mollusk.process_and_validate_instruction(&instruction, &[], &[Check::success()]);
864}}
865"#,
866            name.to_snake_case(),
867        ),
868    )]
869}
870
871/// Generate template for LiteSVM Rust unit-test
872fn create_program_template_litesvm_test(name: &str, tests_path: &Path) -> Files {
873    vec![(
874        tests_path.join("test_initialize.rs"),
875        format!(
876            r#"
877use {{
878    anchor_lang::{{solana_program::instruction::Instruction, InstructionData, ToAccountMetas}},
879    litesvm::LiteSVM,
880    solana_message::{{Message, VersionedMessage}},
881    solana_signer::Signer,
882    solana_keypair::Keypair,
883    solana_transaction::versioned::VersionedTransaction,
884}};
885
886#[test]
887fn test_initialize() {{
888    let program_id = {0}::id();
889    let payer = Keypair::new();
890    let mut svm = LiteSVM::new();
891    let bytes = include_bytes!("../../../target/deploy/{0}.so");
892    svm.add_program(program_id, bytes).unwrap();
893    svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();
894    
895    let instruction = Instruction::new_with_bytes(
896        program_id,
897        &{0}::instruction::Initialize {{}}.data(),
898        {0}::accounts::Initialize {{}}.to_account_metas(None),
899    );
900
901    let blockhash = svm.latest_blockhash();
902    let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);
903    let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[payer]).unwrap();
904
905    let res = svm.send_transaction(tx);
906    assert!(res.is_ok());
907}}
908"#,
909            name.to_snake_case(),
910        ),
911    )]
912}