test.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import {
  2. Blockhash,
  3. Keypair,
  4. PublicKey,
  5. SystemProgram,
  6. Transaction,
  7. TransactionInstruction,
  8. } from "@solana/web3.js";
  9. import { BN } from "bn.js";
  10. import * as borsh from "borsh";
  11. import { assert, expect } from "chai";
  12. import { describe, test } from "mocha";
  13. import { BanksClient, ProgramTestContext, start } from "solana-bankrun";
  14. // This is a helper class to assign properties to the class
  15. class Assignable {
  16. constructor(properties) {
  17. for (const [key, value] of Object.entries(properties)) {
  18. this[key] = value;
  19. }
  20. }
  21. }
  22. const MyInstruction = {
  23. CreateFav: 0,
  24. GetFav: 1,
  25. } as const;
  26. class CreateFav extends Assignable {
  27. number: number;
  28. instruction: MyInstruction;
  29. color: string;
  30. hobbies: string[];
  31. toBuffer() {
  32. return Buffer.from(borsh.serialize(CreateNewAccountSchema, this));
  33. }
  34. static fromBuffer(buffer: Buffer): CreateFav {
  35. return borsh.deserialize(
  36. {
  37. struct: {
  38. number: "u64",
  39. color: "string",
  40. hobbies: {
  41. array: {
  42. type: "string",
  43. },
  44. },
  45. },
  46. },
  47. buffer,
  48. ) as CreateFav;
  49. }
  50. }
  51. const CreateNewAccountSchema = {
  52. struct: {
  53. instruction: "u8",
  54. number: "u64",
  55. color: "string",
  56. hobbies: {
  57. array: {
  58. type: "string",
  59. },
  60. },
  61. },
  62. };
  63. class GetFav extends Assignable {
  64. toBuffer() {
  65. return Buffer.from(borsh.serialize(GetFavSchema, this));
  66. }
  67. }
  68. const GetFavSchema = {
  69. struct: {
  70. instruction: "u8",
  71. },
  72. };
  73. describe("Favorites Solana Native", () => {
  74. // Randomly generate the program keypair and load the program to solana-bankrun
  75. const programId = PublicKey.unique();
  76. let context: ProgramTestContext;
  77. let client: BanksClient;
  78. let payer: Keypair;
  79. let blockhash: Blockhash;
  80. beforeEach(async () => {
  81. context = await start([{ name: "favorites_native", programId }], []);
  82. client = context.banksClient;
  83. // Get the payer keypair from the context, this will be used to sign transactions with enough lamports
  84. payer = context.payer;
  85. blockhash = context.lastBlockhash;
  86. });
  87. test("Set the favorite pda and cross-check the updated data", async () => {
  88. const favoritesPda = PublicKey.findProgramAddressSync(
  89. [Buffer.from("favorite"), payer.publicKey.toBuffer()],
  90. programId,
  91. )[0];
  92. const favData = {
  93. instruction: MyInstruction.CreateFav,
  94. number: 42,
  95. color: "blue",
  96. hobbies: ["coding", "reading", "traveling"],
  97. };
  98. const favorites = new CreateFav(favData);
  99. const ix = new TransactionInstruction({
  100. keys: [
  101. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  102. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  103. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  104. ],
  105. programId,
  106. data: favorites.toBuffer(),
  107. });
  108. const tx = new Transaction().add(ix);
  109. tx.feePayer = payer.publicKey;
  110. tx.recentBlockhash = blockhash;
  111. tx.sign(payer);
  112. tx.recentBlockhash = blockhash;
  113. await client.processTransaction(tx);
  114. const account = await client.getAccount(favoritesPda);
  115. const data = Buffer.from(account.data);
  116. const favoritesData = CreateFav.fromBuffer(data);
  117. console.log("Deserialized data:", favoritesData);
  118. expect(new BN(favoritesData.number as any, "le").toNumber()).to.equal(
  119. favData.number,
  120. );
  121. expect(favoritesData.color).to.equal(favData.color);
  122. expect(favoritesData.hobbies).to.deep.equal(favData.hobbies);
  123. });
  124. test("Check if the test fails if the pda seeds aren't same", async () => {
  125. // We put the wrong seeds knowingly to see if the test fails because of checks
  126. const favoritesPda = PublicKey.findProgramAddressSync(
  127. [Buffer.from("favorite"), payer.publicKey.toBuffer()],
  128. programId,
  129. )[0];
  130. const favData = {
  131. instruction: MyInstruction.CreateFav,
  132. number: 42,
  133. color: "blue",
  134. hobbies: ["coding", "reading", "traveling"],
  135. };
  136. const favorites = new CreateFav(favData);
  137. const ix = new TransactionInstruction({
  138. keys: [
  139. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  140. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  141. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  142. ],
  143. programId,
  144. data: favorites.toBuffer(),
  145. });
  146. const tx = new Transaction().add(ix);
  147. tx.feePayer = payer.publicKey;
  148. tx.recentBlockhash = blockhash;
  149. tx.sign(payer);
  150. tx.recentBlockhash = blockhash;
  151. try {
  152. await client.processTransaction(tx);
  153. console.error("Expected the test to fail");
  154. } catch (_err) {
  155. assert(true);
  156. }
  157. });
  158. test("Get the favorite pda and cross-check the data", async () => {
  159. // Creating a new account with payer's pubkey
  160. const favoritesPda = PublicKey.findProgramAddressSync(
  161. [Buffer.from("favorite"), payer.publicKey.toBuffer()],
  162. programId,
  163. )[0];
  164. const favData = {
  165. instruction: MyInstruction.CreateFav,
  166. number: 42,
  167. color: "hazel",
  168. hobbies: ["singing", "dancing", "skydiving"],
  169. };
  170. const favorites = new CreateFav(favData);
  171. const ix = new TransactionInstruction({
  172. keys: [
  173. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  174. { pubkey: favoritesPda, isSigner: false, isWritable: true },
  175. { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
  176. ],
  177. programId,
  178. data: favorites.toBuffer(),
  179. });
  180. const tx1 = new Transaction().add(ix);
  181. tx1.feePayer = payer.publicKey;
  182. tx1.recentBlockhash = blockhash;
  183. tx1.sign(payer);
  184. tx1.recentBlockhash = blockhash;
  185. await client.processTransaction(tx1);
  186. // Getting the user's data through the get_pda instruction
  187. const getfavData = { instruction: MyInstruction.GetFav };
  188. const getfavorites = new GetFav(getfavData);
  189. const ix2 = new TransactionInstruction({
  190. keys: [
  191. { pubkey: payer.publicKey, isSigner: true, isWritable: true },
  192. { pubkey: favoritesPda, isSigner: false, isWritable: false },
  193. ],
  194. programId,
  195. data: getfavorites.toBuffer(),
  196. });
  197. const tx = new Transaction().add(ix2);
  198. tx.feePayer = payer.publicKey;
  199. tx.recentBlockhash = blockhash;
  200. tx.sign(payer);
  201. tx.recentBlockhash = blockhash;
  202. await client.processTransaction(tx);
  203. });
  204. });