tests: Move IDL related tests in `misc` to `idl` (#2606)

This commit is contained in:
acheron 2023-08-19 13:09:09 +02:00 committed by GitHub
parent 6eacad4b11
commit 6f9f7d9369
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 591 additions and 645 deletions

View File

@ -2,6 +2,8 @@
seeds = true
[programs.localnet]
client_interactions = "C1ient1nteractions1111111111111111111111111"
docs = "Docs111111111111111111111111111111111111111"
external = "Externa1111111111111111111111111111111111111"
generics = "Generics111111111111111111111111111111111111"
idl = "id11111111111111111111111111111111111111111"

View File

@ -10,6 +10,11 @@
"type": "u8",
"value": "6"
},
{
"name": "BYTE_STR",
"type": "u8",
"value": "116"
},
{
"name": "FOO_CONST",
"type": "u128",

View File

@ -14,6 +14,16 @@
"name": "BAR_CONST",
"type": "u8",
"value": "6"
},
{
"name": "BYTES_STR",
"type": "bytes",
"value": "[116, 101, 115, 116]"
},
{
"name": "BYTE_STR",
"type": "u8",
"value": "116"
}
],
"instructions": [

View File

@ -1,5 +1,5 @@
[package]
name = "idl_doc"
name = "client-interactions"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.60"
@ -7,7 +7,7 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "idl_doc"
name = "client_interactions"
[features]
no-entrypoint = []
@ -16,4 +16,4 @@ cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] }
anchor-lang = { path = "../../../../lang" }

View File

@ -0,0 +1,95 @@
use anchor_lang::prelude::*;
declare_id!("C1ient1nteractions1111111111111111111111111");
#[program]
pub mod client_interactions {
use super::*;
pub fn int(ctx: Context<Int>, i8: i8, i16: i16, i32: i32, i64: i64, i128: i128) -> Result<()> {
ctx.accounts.account.i8 = i8;
ctx.accounts.account.i16 = i16;
ctx.accounts.account.i32 = i32;
ctx.accounts.account.i64 = i64;
ctx.accounts.account.i128 = i128;
Ok(())
}
pub fn uint(
ctx: Context<UnsignedInt>,
u8: u8,
u16: u16,
u32: u32,
u64: u64,
u128: u128,
) -> Result<()> {
ctx.accounts.account.u8 = u8;
ctx.accounts.account.u16 = u16;
ctx.accounts.account.u32 = u32;
ctx.accounts.account.u64 = u64;
ctx.accounts.account.u128 = u128;
Ok(())
}
pub fn enm(ctx: Context<Enum>, enum_arg: MyEnum) -> Result<()> {
ctx.accounts.account.enum_field = enum_arg;
Ok(())
}
}
#[derive(Accounts)]
pub struct Int<'info> {
#[account(zero)]
pub account: Account<'info, IntAccount>,
}
#[account]
pub struct IntAccount {
pub i8: i8,
pub i16: i16,
pub i32: i32,
pub i64: i64,
pub i128: i128,
}
#[derive(Accounts)]
pub struct UnsignedInt<'info> {
#[account(zero)]
pub account: Account<'info, UnsignedIntAccount>,
}
#[account]
pub struct UnsignedIntAccount {
pub u8: u8,
pub u16: u16,
pub u32: u32,
pub u64: u64,
pub u128: u128,
}
#[derive(Accounts)]
pub struct Enum<'info> {
#[account(zero)]
pub account: Account<'info, EnumAccount>,
}
#[account]
pub struct EnumAccount {
pub enum_field: MyEnum,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Debug, Eq, PartialEq)]
pub enum MyEnum {
Unit,
Named { x: u64, y: u64 },
Unnamed(u8, u8, u16, u16),
UnnamedStruct(MyStruct),
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Debug, Eq, PartialEq)]
pub struct MyStruct {
pub u8: u8,
pub u16: u16,
pub u32: u32,
pub u64: u64,
}

View File

@ -0,0 +1,19 @@
[package]
name = "docs"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.60"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
name = "docs"
[features]
no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = { path = "../../../../lang" }

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -2,18 +2,15 @@
use anchor_lang::prelude::*;
declare_id!("BqmKjZGVa8fqyWuojJzG16zaKSV1GjAisZToNuvEaz6m");
declare_id!("Docs111111111111111111111111111111111111111");
/// This is a doc comment for the program
#[program]
pub mod idl_doc {
pub mod docs {
use super::*;
/// This instruction doc should appear in the IDL
pub fn test_idl_doc_parse(
_ctx: Context<TestIdlDocParse>,
) -> Result<()> {
pub fn test_idl_doc_parse(_ctx: Context<TestIdlDocParse>) -> Result<()> {
Ok(())
}
}
@ -25,7 +22,6 @@ pub struct DataWithDoc {
pub data: u16,
}
#[derive(Accounts)]
pub struct TestIdlDocParse<'info> {
/// This account doc comment should appear in the IDL

View File

@ -5,9 +5,18 @@ declare_id!("id11111111111111111111111111111111111111111");
#[constant]
pub const FOO_CONST: u128 = 1_000_000;
#[constant]
pub const BAR_CONST: u8 = 6;
#[constant]
pub const BYTES_STR: &[u8] = b"test";
#[constant]
pub const BYTE_STR: u8 = b't';
pub const NO_IDL: u16 = 55;
/// IDL test program documentation.
#[program]
pub mod idl {

View File

@ -0,0 +1,121 @@
import * as anchor from "@coral-xyz/anchor";
import { assert } from "chai";
import { ClientInteractions } from "../target/types/client_interactions";
describe("Client interactions", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace
.clientInteractions as anchor.Program<ClientInteractions>;
it("Can use integers", async () => {
const kp = anchor.web3.Keypair.generate();
const i8 = -3;
const i16 = 1;
const i32 = -5555551;
const i64 = new anchor.BN("384535471");
const i128 = new anchor.BN(-8342491);
await program.methods
.int(i8, i16, i32, i64, i128)
.accounts({ account: kp.publicKey })
.signers([kp])
.preInstructions([await program.account.intAccount.createInstruction(kp)])
.rpc();
const account = await program.account.intAccount.fetch(kp.publicKey);
assert.strictEqual(account.i8, i8);
assert.strictEqual(account.i16, i16);
assert.strictEqual(account.i32, i32);
assert(account.i64.eq(i64));
assert(account.i128.eq(i128));
});
it("Can use unsigned integers", async () => {
const kp = anchor.web3.Keypair.generate();
const u8 = 123;
const u16 = 7888;
const u32 = 5555551;
const u64 = new anchor.BN("384535471");
const u128 = new anchor.BN(8888888);
await program.methods
.uint(u8, u16, u32, u64, u128)
.accounts({ account: kp.publicKey })
.signers([kp])
.preInstructions([
await program.account.unsignedIntAccount.createInstruction(kp),
])
.rpc();
const account = await program.account.unsignedIntAccount.fetch(
kp.publicKey
);
assert.strictEqual(account.u8, u8);
assert.strictEqual(account.u16, u16);
assert.strictEqual(account.u32, u32);
assert(account.u64.eq(u64));
assert(account.u128.eq(u128));
});
it("Can use enum", async () => {
const testAccountEnum = async (
...args: Parameters<typeof program["methods"]["enm"]>
) => {
const kp = anchor.web3.Keypair.generate();
await program.methods
.enm(...(args as any))
.accounts({ account: kp.publicKey })
.signers([kp])
.preInstructions([
await program.account.enumAccount.createInstruction(kp),
])
.rpc();
return await program.account.enumAccount.fetch(kp.publicKey);
};
// Unit
const unit = await testAccountEnum({ unit: {} });
assert.deepEqual(unit.enumField.unit, {});
// Named
const x = new anchor.BN(1);
const y = new anchor.BN(2);
const named = await testAccountEnum({ named: { x, y } });
assert(named.enumField.named.x.eq(x));
assert(named.enumField.named.y.eq(y));
// Unnamed
const tupleArg = [1, 2, 3, 4] as const;
const unnamed = await testAccountEnum({ unnamed: tupleArg });
assert.strictEqual(unnamed.enumField.unnamed[0], tupleArg[0]);
assert.strictEqual(unnamed.enumField.unnamed[1], tupleArg[1]);
assert.strictEqual(unnamed.enumField.unnamed[2], tupleArg[2]);
assert.strictEqual(unnamed.enumField.unnamed[3], tupleArg[3]);
// Unnamed struct
const tupleStructArg = [
{ u8: 1, u16: 11, u32: 111, u64: new anchor.BN(1111) },
] as const;
const unnamedStruct = await testAccountEnum({
unnamedStruct: tupleStructArg,
});
assert.strictEqual(
unnamedStruct.enumField.unnamedStruct[0].u8,
tupleStructArg[0].u8
);
assert.strictEqual(
unnamedStruct.enumField.unnamedStruct[0].u16,
tupleStructArg[0].u16
);
assert.strictEqual(
unnamedStruct.enumField.unnamedStruct[0].u32,
tupleStructArg[0].u32
);
assert(
unnamedStruct.enumField.unnamedStruct[0].u64.eq(tupleStructArg[0].u64)
);
});
});

46
tests/idl/tests/docs.ts Normal file
View File

@ -0,0 +1,46 @@
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { assert } from "chai";
import { Docs } from "../target/types/docs";
describe("Docs", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.docs as Program<Docs>;
const instruction = program.idl.instructions.find(
(i) => i.name === "testIdlDocParse"
);
it("includes instruction doc comment", () => {
assert.deepEqual(instruction.docs, [
"This instruction doc should appear in the IDL",
]);
});
it("includes account doc comment", () => {
const act = instruction.accounts.find((i) => i.name === "act");
assert.deepEqual(act.docs, [
"This account doc comment should appear in the IDL",
"This is a multi-line comment",
]);
});
const dataWithDoc = program.idl.accounts.find(
// @ts-expect-error
(acc) => acc.name === "DataWithDoc"
);
it("includes accounts doc comment", () => {
assert.deepEqual(dataWithDoc.docs, [
"Custom account doc comment should appear in the IDL",
]);
});
it("includes account attribute doc comment", () => {
const dataField = dataWithDoc.type.fields.find((i) => i.name === "data");
assert.deepEqual(dataField.docs, [
"Account attribute doc comment should appear in the IDL",
]);
});
});

View File

@ -1,43 +1,32 @@
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { assert } from "chai";
import { Idl } from "../target/types/idl";
describe("IDL", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.idl as Program<Idl>;
it("Can lazy load workspace programs", () => {
assert.doesNotThrow(() => {
// Program exists, should not throw
anchor.workspace.relationsDerivation;
});
assert.throws(() => {
// IDL path in Anchor.toml doesn't exist but other tests still run
// successfully because workspace programs are getting loaded on-demand
anchor.workspace.nonExistent;
}, /non-existent\.json/);
it("Should include `FOO_CONST`", () => {
assert.isDefined(
program.idl.constants.find(
(c) =>
c.name === "FOO_CONST" && c.type === "u128" && c.value === "1000000"
)
);
});
it("Can get workspace programs by their name independent of casing", () => {
const camel = anchor.workspace.relationsDerivation;
const pascal = anchor.workspace.RelationsDerivation;
const kebab = anchor.workspace["relations-derivation"];
const snake = anchor.workspace["relations_derivation"];
const compareProgramNames = (...programs: anchor.Program[]) => {
return programs.every(
(program) => program.idl.name === "relations_derivation"
);
};
assert(compareProgramNames(camel, pascal, kebab, snake));
it("Should include `BAR_CONST`", () => {
assert.isDefined(
program.idl.constants.find(
(c) => c.name === "BAR_CONST" && c.type === "u8" && c.value === "6"
)
);
});
it("Can use numbers in program names", () => {
assert.doesNotThrow(() => {
anchor.workspace.numbers123;
anchor.workspace.Numbers123;
anchor.workspace["numbers-123"];
anchor.workspace["numbers_123"];
});
it("Should not include `NO_IDL` const", () => {
// @ts-expect-error
assert.isUndefined(program.idl.constants.find((c) => c.name === "NO_IDL"));
});
});

View File

@ -0,0 +1,43 @@
import * as anchor from "@coral-xyz/anchor";
import { assert } from "chai";
describe("Workspace", () => {
anchor.setProvider(anchor.AnchorProvider.env());
it("Can lazy load workspace programs", () => {
assert.doesNotThrow(() => {
// Program exists, should not throw
anchor.workspace.relationsDerivation;
});
assert.throws(() => {
// IDL path in Anchor.toml doesn't exist but other tests still run
// successfully because workspace programs are getting loaded on-demand
anchor.workspace.nonExistent;
}, /non-existent\.json/);
});
it("Can get workspace programs by their name independent of casing", () => {
const camel = anchor.workspace.relationsDerivation;
const pascal = anchor.workspace.RelationsDerivation;
const kebab = anchor.workspace["relations-derivation"];
const snake = anchor.workspace["relations_derivation"];
const compareProgramNames = (...programs: anchor.Program[]) => {
return programs.every(
(program) => program.idl.name === "relations_derivation"
);
};
assert(compareProgramNames(camel, pascal, kebab, snake));
});
it("Can use numbers in program names", () => {
assert.doesNotThrow(() => {
anchor.workspace.numbers123;
anchor.workspace.Numbers123;
anchor.workspace["numbers-123"];
anchor.workspace["numbers_123"];
});
});
});

View File

@ -3,7 +3,6 @@ cluster = "localnet"
wallet = "~/.config/solana/id.json"
[programs.localnet]
idl_doc = "BqmKjZGVa8fqyWuojJzG16zaKSV1GjAisZToNuvEaz6m"
init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2"
lamports = "Lamports11111111111111111111111111111111111"
misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"

View File

@ -30,18 +30,6 @@ pub struct DataI8 {
}
size!(DataI8, 1);
#[account]
pub struct DataI16 {
pub data: i16, // 2
}
size!(DataI16, 2);
#[account]
pub struct DataEnum {
pub data: TestEnum, // 1 + 16
}
size!(DataEnum, 17);
#[account(zero_copy)]
pub struct DataZeroCopy {
pub data: u16, // 2
@ -79,19 +67,3 @@ pub struct DataConstCastArraySize {
pub struct DataMultidimensionalArrayConstSizes {
pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE],
}
#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
pub enum TestEnum {
First,
Second { x: u64, y: u64 },
TupleTest(u8, u8, u16, u16),
TupleStructTest(TestStruct),
}
#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
pub struct TestStruct {
pub data1: u8,
pub data2: u16,
pub data3: u32,
pub data4: u64,
}

View File

@ -193,35 +193,9 @@ pub struct TestCloseMut<'info> {
pub sol_dest: Option<AccountInfo<'info>>,
}
#[derive(Accounts)]
pub struct TestU16<'info> {
#[account(zero)]
pub my_account: Option<Account<'info, DataU16>>,
}
#[derive(Accounts)]
pub struct TestI16<'info> {
#[account(zero)]
pub data: Option<Account<'info, DataI16>>,
}
#[derive(Accounts)]
pub struct TestSimulate {}
#[derive(Accounts)]
pub struct TestAccountEnum<'info> {
#[account(init, payer = payer.as_ref().unwrap(), space = 8+ DataEnum::LEN )]
pub data: Option<Account<'info, DataEnum>>,
pub payer: Option<Signer<'info>>,
pub system_program: Option<Program<'info, System>>,
}
#[derive(Accounts)]
pub struct TestI8<'info> {
#[account(zero)]
pub data: Option<Account<'info, DataI8>>,
}
#[derive(Accounts)]
pub struct TestCompositePayer<'info> {
pub composite: TestInit<'info>,

View File

@ -1,7 +1,5 @@
use anchor_lang::prelude::*;
use crate::account::*;
pub const MAX_EVENT_SIZE: usize = 10;
pub const MAX_EVENT_SIZE_U8: u8 = 11;
@ -34,8 +32,3 @@ pub struct E5 {
pub struct E6 {
pub data: [u8; MAX_EVENT_SIZE_U8 as usize],
}
#[event]
pub struct E7 {
pub data: TestEnum,
}

View File

@ -44,11 +44,6 @@ pub mod misc_optional {
Ok(())
}
pub fn test_u16(ctx: Context<TestU16>, data: u16) -> Result<()> {
ctx.accounts.my_account.as_mut().unwrap().data = data;
Ok(())
}
pub fn test_simulate(_ctx: Context<TestSimulate>, data: u32) -> Result<()> {
emit!(E1 { data });
emit!(E2 { data: 1234 });
@ -62,26 +57,6 @@ pub mod misc_optional {
Ok(())
}
pub fn test_input_enum(_ctx: Context<TestSimulate>, data: TestEnum) -> Result<()> {
emit!(E7 { data: data });
Ok(())
}
pub fn test_account_enum(ctx: Context<TestAccountEnum>, data: TestEnum) -> Result<()> {
ctx.accounts.data.as_mut().unwrap().data = data;
Ok(())
}
pub fn test_i8(ctx: Context<TestI8>, data: i8) -> Result<()> {
ctx.accounts.data.as_mut().unwrap().data = data;
Ok(())
}
pub fn test_i16(ctx: Context<TestI16>, data: i16) -> Result<()> {
ctx.accounts.data.as_mut().unwrap().data = data;
Ok(())
}
pub fn test_const_array_size(ctx: Context<TestConstArraySize>, data: u8) -> Result<()> {
ctx.accounts.data.as_mut().unwrap().data[0] = data;
Ok(())

View File

@ -30,18 +30,6 @@ pub struct DataI8 {
}
size!(DataI8, 1);
#[account]
pub struct DataI16 {
pub data: i16, // 2
}
size!(DataI16, 2);
#[account]
pub struct DataEnum {
pub data: TestEnum, // 1 + 16
}
size!(DataEnum, 17);
#[account(zero_copy)]
pub struct DataZeroCopy {
pub data: u16, // 2
@ -101,14 +89,6 @@ pub enum CoolEnum {
},
}
#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
pub enum TestEnum {
First,
Second { x: u64, y: u64 },
TupleTest(u8, u8, u16, u16),
TupleStructTest(TestStruct),
}
#[derive(Debug, Clone, Copy, AnchorSerialize, AnchorDeserialize, PartialEq, Eq)]
pub struct TestStruct {
pub data1: u8,

View File

@ -200,36 +200,9 @@ pub struct TestCloseMut<'info> {
pub sol_dest: AccountInfo<'info>,
}
#[derive(Accounts)]
pub struct TestU16<'info> {
#[account(zero)]
pub my_account: Account<'info, DataU16>,
}
#[derive(Accounts)]
pub struct TestI16<'info> {
#[account(zero)]
pub data: Account<'info, DataI16>,
}
#[derive(Accounts)]
pub struct TestSimulate {}
#[derive(Accounts)]
pub struct TestAccountEnum<'info> {
#[account(init, payer = payer, space = 8 + DataEnum::LEN)]
pub data: Account<'info, DataEnum>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct TestI8<'info> {
#[account(zero)]
pub data: Account<'info, DataI8>,
}
#[derive(Accounts)]
pub struct TestInit<'info> {
#[account(init, payer = payer, space = DataI8::LEN + 8)]

View File

@ -1,7 +1,5 @@
use anchor_lang::prelude::*;
use crate::account::*;
pub const MAX_EVENT_SIZE: usize = 10;
pub const MAX_EVENT_SIZE_U8: u8 = 11;
@ -34,8 +32,3 @@ pub struct E5 {
pub struct E6 {
pub data: [u8; MAX_EVENT_SIZE_U8 as usize],
}
#[event]
pub struct E7 {
pub data: TestEnum,
}

View File

@ -12,16 +12,6 @@ mod event;
declare_id!("3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh");
#[constant]
pub const BASE: u128 = 1_000_000;
#[constant]
pub const DECIMALS: u8 = 6;
#[constant]
pub const BYTES_STR: &[u8] = b"test";
#[constant]
pub const BYTE_STR: u8 = b't';
pub const NO_IDL: u16 = 55;
#[program]
pub mod misc {
use super::*;
@ -48,11 +38,6 @@ pub mod misc {
Ok(())
}
pub fn test_u16(ctx: Context<TestU16>, data: u16) -> Result<()> {
ctx.accounts.my_account.data = data;
Ok(())
}
pub fn test_simulate(_ctx: Context<TestSimulate>, data: u32) -> Result<()> {
emit!(E1 { data });
emit!(E2 { data: 1234 });
@ -66,26 +51,6 @@ pub mod misc {
Ok(())
}
pub fn test_input_enum(_ctx: Context<TestSimulate>, data: TestEnum) -> Result<()> {
emit!(E7 { data: data });
Ok(())
}
pub fn test_account_enum(ctx: Context<TestAccountEnum>, data: TestEnum) -> Result<()> {
ctx.accounts.data.data = data;
Ok(())
}
pub fn test_i8(ctx: Context<TestI8>, data: i8) -> Result<()> {
ctx.accounts.data.data = data;
Ok(())
}
pub fn test_i16(ctx: Context<TestI16>, data: i16) -> Result<()> {
ctx.accounts.data.data = data;
Ok(())
}
pub fn test_const_array_size(ctx: Context<TestConstArraySize>, data: u8) -> Result<()> {
ctx.accounts.data.data[0] = data;
Ok(())

View File

@ -1,2 +0,0 @@
[scripts]
test = "yarn run ts-mocha -t 1000000 ./tests/idl_doc/*.ts"

View File

@ -1,50 +0,0 @@
import * as anchor from "@coral-xyz/anchor";
import { Program, Wallet } from "@coral-xyz/anchor";
import { IdlDoc } from "../../target/types/idl_doc";
const { expect } = require("chai");
const idl_doc_idl = require("../../target/idl/idl_doc.json");
describe("idl_doc", () => {
// Configure the client to use the local cluster.
const provider = anchor.AnchorProvider.env();
const wallet = provider.wallet as Wallet;
anchor.setProvider(provider);
const program = anchor.workspace.IdlDoc as Program<IdlDoc>;
describe("IDL doc strings", () => {
const instruction = program.idl.instructions.find(
(i) => i.name === "testIdlDocParse"
);
it("includes instruction doc comment", async () => {
expect(instruction.docs).to.have.same.members([
"This instruction doc should appear in the IDL",
]);
});
it("includes account doc comment", async () => {
const act = instruction.accounts.find((i) => i.name === "act");
expect(act.docs).to.have.same.members([
"This account doc comment should appear in the IDL",
"This is a multi-line comment",
]);
});
const dataWithDoc = program.idl.accounts.find(
// @ts-expect-error
(i) => i.name === "DataWithDoc"
);
it("includes accounts doc comment", async () => {
expect(dataWithDoc.docs).to.have.same.members([
"Custom account doc comment should appear in the IDL",
]);
});
it("includes account attribute doc comment", async () => {
const dataField = dataWithDoc.type.fields.find((i) => i.name === "data");
expect(dataField.docs).to.have.same.members([
"Account attribute doc comment should appear in the IDL",
]);
});
});
});

View File

@ -1,5 +1,5 @@
import * as anchor from "@coral-xyz/anchor";
import { Program, BN, AnchorError, Wallet, IdlEvents } from "@coral-xyz/anchor";
import { Program, AnchorError, Wallet } from "@coral-xyz/anchor";
import {
PublicKey,
Keypair,
@ -14,11 +14,12 @@ import {
Token,
ASSOCIATED_TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { assert, expect } from "chai";
import { Misc } from "../../target/types/misc";
import { MiscOptional } from "../../target/types/misc_optional";
const utf8 = anchor.utils.bytes.utf8;
const { assert, expect } = require("chai");
const nativeAssert = require("assert");
const miscIdl = require("../../target/idl/misc.json");
@ -31,36 +32,217 @@ const miscTest = (
const wallet = provider.wallet as Wallet;
anchor.setProvider(provider);
const data = anchor.web3.Keypair.generate();
it("Can use u128 and i128", async () => {
const tx = await program.rpc.initialize(
new anchor.BN(1234),
new anchor.BN(22),
{
accounts: {
data: data.publicKey,
},
signers: [data],
instructions: [await program.account.data.createInstruction(data)],
}
);
const dataAccount = await program.account.data.fetch(data.publicKey);
assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234)));
assert.isTrue(dataAccount.idata.eq(new anchor.BN(22)));
});
it("Can use u16", async () => {
describe("Data Account", () => {
const data = anchor.web3.Keypair.generate();
const tx = await program.rpc.testU16(99, {
accounts: {
myAccount: data.publicKey,
},
signers: [data],
instructions: [await program.account.dataU16.createInstruction(data)],
const udata = new anchor.BN(1);
const idata = new anchor.BN(2);
it("Can initialize data account", async () => {
await program.methods
.initialize(udata, idata)
.accounts({ data: data.publicKey })
.signers([data])
.preInstructions([await program.account.data.createInstruction(data)])
.rpc();
const dataAccount = await program.account.data.fetch(data.publicKey);
assert(dataAccount.udata.eq(udata));
assert(dataAccount.idata.eq(idata));
});
it("Can use base58 strings to fetch an account", async () => {
const dataAccount = await program.account.data.fetch(
data.publicKey.toString()
);
assert(dataAccount.udata.eq(udata));
assert(dataAccount.idata.eq(idata));
});
it("Can use the owner constraint", async () => {
await program.methods
.testOwner()
.accounts({
data: data.publicKey,
misc: program.programId,
})
.rpc();
await nativeAssert.rejects(
async () => {
await program.methods
.testOwner()
.accounts({
data: provider.wallet.publicKey,
misc: program.programId,
})
.rpc();
},
(err) => {
return true;
}
);
});
it("Should fail to close an account when sending lamports to itself", async () => {
try {
await program.methods
.testClose()
.accounts({
data: data.publicKey,
solDest: data.publicKey,
})
.rpc();
expect(false).to.be.true;
} catch (err) {
assert.strictEqual(
err.error.errorMessage,
"A close constraint was violated"
);
assert.strictEqual(err.error.errorCode.number, 2011);
}
});
it("Can close an account", async () => {
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(
transferIx
);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testClose()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
it("Can close an account twice", async () => {
const data = anchor.web3.Keypair.generate();
await program.methods
.initialize(new anchor.BN(10), new anchor.BN(10))
.accounts({ data: data.publicKey })
.preInstructions([await program.account.data.createInstruction(data)])
.signers([data])
.rpc();
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(
transferIx
);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testCloseTwice()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
it("Can close a mut account manually", async () => {
const data = anchor.web3.Keypair.generate();
await program.methods
.initialize(new anchor.BN(10), new anchor.BN(10))
.accounts({ data: data.publicKey })
.preInstructions([await program.account.data.createInstruction(data)])
.signers([data])
.rpc();
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(
transferIx
);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testCloseMut()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
const dataAccount = await program.account.dataU16.fetch(data.publicKey);
assert.strictEqual(dataAccount.data, 99);
});
it("Can send VersionedTransaction", async () => {
@ -216,29 +398,6 @@ const miscTest = (
assert.isTrue(accInfo.executable);
});
it("Can use the owner constraint", async () => {
await program.rpc.testOwner({
accounts: {
data: data.publicKey,
misc: program.programId,
},
});
await nativeAssert.rejects(
async () => {
await program.rpc.testOwner({
accounts: {
data: provider.wallet.publicKey,
misc: program.programId,
},
});
},
(err) => {
return true;
}
);
});
it("Can use the executable attribute", async () => {
await program.rpc.testExecutable({
accounts: {
@ -291,292 +450,6 @@ const miscTest = (
);
});
it("Can use enum in idl", async () => {
const resp1 = await program.methods
.testInputEnum({ first: {} })
.simulate();
const event1 = resp1.events[0].data as IdlEvents<
typeof program.idl
>["E7"];
assert.deepEqual(event1.data.first, {});
const resp2 = await program.methods
.testInputEnum({ second: { x: new BN(1), y: new BN(2) } })
.simulate();
const event2 = resp2.events[0].data as IdlEvents<
typeof program.idl
>["E7"];
assert.isTrue(new BN(1).eq(event2.data.second.x));
assert.isTrue(new BN(2).eq(event2.data.second.y));
const resp3 = await program.methods
.testInputEnum({
tupleStructTest: [
{ data1: 1, data2: 11, data3: 111, data4: new BN(1111) },
],
})
.simulate();
const event3 = resp3.events[0].data as IdlEvents<
typeof program.idl
>["E7"];
assert.strictEqual(event3.data.tupleStructTest[0].data1, 1);
assert.strictEqual(event3.data.tupleStructTest[0].data2, 11);
assert.strictEqual(event3.data.tupleStructTest[0].data3, 111);
assert.isTrue(event3.data.tupleStructTest[0].data4.eq(new BN(1111)));
const resp4 = await program.methods
.testInputEnum({ tupleTest: [1, 2, 3, 4] })
.simulate();
const event4 = resp4.events[0].data as IdlEvents<
typeof program.idl
>["E7"];
assert.strictEqual(event4.data.tupleTest[0], 1);
assert.strictEqual(event4.data.tupleTest[1], 2);
assert.strictEqual(event4.data.tupleTest[2], 3);
assert.strictEqual(event4.data.tupleTest[3], 4);
});
it("Can use enum in accounts", async () => {
const testAccountEnum = async (
...args: Parameters<typeof program["methods"]["testAccountEnum"]>
) => {
const dataKp = anchor.web3.Keypair.generate();
const txHash = await program.methods
.testAccountEnum(...(args as any))
.accounts({
data: dataKp.publicKey,
payer: program.provider.publicKey,
})
.signers([dataKp])
.rpc();
await program.provider.connection.confirmTransaction(txHash);
return await program.account.dataEnum.fetch(dataKp.publicKey);
};
// Unit
const unit = await testAccountEnum({ first: {} });
assert.deepEqual(unit.data.first, {});
// Named
const named = await testAccountEnum({
second: { x: new BN(1), y: new BN(2) },
});
assert.isTrue(new BN(1).eq(named.data.second.x));
assert.isTrue(new BN(2).eq(named.data.second.y));
// Unnamed
const unnamed = await testAccountEnum({ tupleTest: [1, 2, 3, 4] });
assert.strictEqual(unnamed.data.tupleTest[0], 1);
assert.strictEqual(unnamed.data.tupleTest[1], 2);
assert.strictEqual(unnamed.data.tupleTest[2], 3);
assert.strictEqual(unnamed.data.tupleTest[3], 4);
// Unnamed struct
const unnamedStruct = await testAccountEnum({
tupleStructTest: [
{ data1: 1, data2: 11, data3: 111, data4: new BN(1111) },
],
});
assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data1, 1);
assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data2, 11);
assert.strictEqual(unnamedStruct.data.tupleStructTest[0].data3, 111);
assert.isTrue(
unnamedStruct.data.tupleStructTest[0].data4.eq(new BN(1111))
);
});
let dataI8;
it("Can use i8 in the idl", async () => {
dataI8 = anchor.web3.Keypair.generate();
await program.rpc.testI8(-3, {
accounts: {
data: dataI8.publicKey,
},
instructions: [await program.account.dataI8.createInstruction(dataI8)],
signers: [dataI8],
});
const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey);
assert.strictEqual(dataAccount.data, -3);
});
let dataPubkey;
it("Can use i16 in the idl", async () => {
const data = anchor.web3.Keypair.generate();
await program.rpc.testI16(-2048, {
accounts: {
data: data.publicKey,
},
instructions: [await program.account.dataI16.createInstruction(data)],
signers: [data],
});
const dataAccount = await program.account.dataI16.fetch(data.publicKey);
assert.strictEqual(dataAccount.data, -2048);
dataPubkey = data.publicKey;
});
it("Can use base58 strings to fetch an account", async () => {
const dataAccount = await program.account.dataI16.fetch(
dataPubkey.toString()
);
assert.strictEqual(dataAccount.data, -2048);
});
it("Should fail to close an account when sending lamports to itself", async () => {
try {
await program.rpc.testClose({
accounts: {
data: data.publicKey,
solDest: data.publicKey,
},
});
expect(false).to.be.true;
} catch (err) {
const errMsg = "A close constraint was violated";
assert.strictEqual(err.error.errorMessage, errMsg);
assert.strictEqual(err.error.errorCode.number, 2011);
}
});
it("Can close an account", async () => {
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(transferIx);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testClose()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
it("Can close an account twice", async () => {
const data = anchor.web3.Keypair.generate();
await program.methods
.initialize(new anchor.BN(10), new anchor.BN(10))
.accounts({ data: data.publicKey })
.preInstructions([await program.account.data.createInstruction(data)])
.signers([data])
.rpc();
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(transferIx);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testCloseTwice()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
it("Can close a mut account manually", async () => {
const data = anchor.web3.Keypair.generate();
await program.methods
.initialize(new anchor.BN(10), new anchor.BN(10))
.accounts({ data: data.publicKey })
.preInstructions([await program.account.data.createInstruction(data)])
.signers([data])
.rpc();
const connection = program.provider.connection;
const openAccount = await connection.getAccountInfo(data.publicKey);
assert.isNotNull(openAccount);
const openAccountBalance = openAccount.lamports;
// double balance to calculate closed balance correctly
const transferIx = anchor.web3.SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: data.publicKey,
lamports: openAccountBalance,
});
const transferTransaction = new anchor.web3.Transaction().add(transferIx);
await provider.sendAndConfirm(transferTransaction);
let beforeBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
await program.methods
.testCloseMut()
.accounts({
data: data.publicKey,
solDest: provider.wallet.publicKey,
})
.postInstructions([transferIx])
.rpc();
let afterBalance = (
await connection.getAccountInfo(provider.wallet.publicKey)
).lamports;
// Retrieved rent exemption sol.
expect(afterBalance > beforeBalance).to.be.true;
const closedAccount = await connection.getAccountInfo(data.publicKey);
assert.isTrue(closedAccount.data.length === 0);
assert.isTrue(closedAccount.owner.equals(SystemProgram.programId));
});
it("Can use instruction data in accounts constraints", async () => {
// b"my-seed"
const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]);
@ -1501,27 +1374,6 @@ const miscTest = (
assert.deepStrictEqual(dataAccount.data, dataArray);
});
it("Should include BASE const in IDL", async () => {
assert.isDefined(
miscIdl.constants.find(
(c) =>
c.name === "BASE" && c.type === "u128" && c.value === "1_000_000"
)
);
});
it("Should include DECIMALS const in IDL", async () => {
assert.isDefined(
miscIdl.constants.find(
(c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6"
)
);
});
it("Should not include NO_IDL const in IDL", async () => {
assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL"));
});
it("init_if_needed creates mint account if not exists", async () => {
const newMint = anchor.web3.Keypair.generate();
@ -2678,6 +2530,7 @@ const miscTest = (
});
});
});
describe("Token Constraint Test", () => {
it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => {
const mint = anchor.web3.Keypair.generate();
@ -3349,22 +3202,6 @@ const miscTest = (
);
assert.isDefined(thisTx);
});
it("Can access enum variant fields using camel case without throwing a type error", async () => {
const anotherProgram = new anchor.Program<Misc>(
miscIdl,
program.programId,
provider
);
const enumWrappers =
await anotherProgram.account.coolEnumWrapperAccount.all();
for (let enumWrapper of enumWrappers) {
// this code never gets run so just putting whatever
console.log(enumWrapper.account.myEnum.variant2?.someSlot);
console.log(enumWrapper.account.myEnum.variant2?.user1);
console.log(enumWrapper.account.myEnum.variant3?.someSlot);
console.log(enumWrapper.account.myEnum.variant3?.user2);
}
});
});
};
};