updateAccountsVisitor.test.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import {
  2. accountNode,
  3. assertIsNode,
  4. CamelCaseString,
  5. constantPdaSeedNodeFromString,
  6. numberTypeNode,
  7. pdaLinkNode,
  8. pdaNode,
  9. programNode,
  10. resolveNestedTypeNode,
  11. rootNode,
  12. structFieldTypeNode,
  13. structTypeNode,
  14. } from '@codama/nodes';
  15. import { visit } from '@codama/visitors-core';
  16. import { expect, test } from 'vitest';
  17. import { updateAccountsVisitor } from '../src';
  18. test('it updates the name of an account', () => {
  19. // Given the following program node with one account.
  20. const node = programNode({
  21. accounts: [accountNode({ name: 'myAccount' })],
  22. name: 'myProgram',
  23. publicKey: '1111',
  24. });
  25. // When we update the name of that account.
  26. const result = visit(
  27. node,
  28. updateAccountsVisitor({
  29. myAccount: { name: 'myNewAccount' },
  30. }),
  31. );
  32. // Then we expect the following tree changes.
  33. assertIsNode(result, 'programNode');
  34. expect(result.accounts[0].name).toBe('myNewAccount' as CamelCaseString);
  35. });
  36. test('it updates the name of an account within a specific program', () => {
  37. // Given two programs each with an account of the same name.
  38. const node = rootNode(
  39. programNode({
  40. accounts: [accountNode({ name: 'candyMachine' })],
  41. name: 'myProgramA',
  42. publicKey: '1111',
  43. }),
  44. [
  45. programNode({
  46. accounts: [accountNode({ name: 'candyMachine' })],
  47. name: 'myProgramB',
  48. publicKey: '2222',
  49. }),
  50. ],
  51. );
  52. // When we update the name of that account in the first program.
  53. const result = visit(
  54. node,
  55. updateAccountsVisitor({
  56. 'myProgramA.candyMachine': { name: 'newCandyMachine' },
  57. }),
  58. );
  59. // Then we expect the first account to have been renamed.
  60. assertIsNode(result, 'rootNode');
  61. expect(result.program.accounts[0].name).toBe('newCandyMachine' as CamelCaseString);
  62. // But not the second account.
  63. expect(result.additionalPrograms[0].accounts[0].name).toBe('candyMachine' as CamelCaseString);
  64. });
  65. test("it renames the fields of an account's data", () => {
  66. // Given the following account.
  67. const node = accountNode({
  68. data: structTypeNode([structFieldTypeNode({ name: 'myData', type: numberTypeNode('u32') })]),
  69. name: 'myAccount',
  70. });
  71. // When we rename its data fields.
  72. const result = visit(
  73. node,
  74. updateAccountsVisitor({
  75. myAccount: {
  76. data: { myData: 'myNewData' },
  77. },
  78. }),
  79. );
  80. // Then we expect the following tree changes.
  81. assertIsNode(result, 'accountNode');
  82. const data = resolveNestedTypeNode(result.data);
  83. expect(data.fields[0].name).toBe('myNewData' as CamelCaseString);
  84. });
  85. test('it updates the name of associated PDA nodes', () => {
  86. // Given the following program node with one account
  87. // and PDA accounts such that one of them is named the same.
  88. const node = programNode({
  89. accounts: [accountNode({ name: 'myAccount' })],
  90. name: 'myProgram',
  91. pdas: [pdaNode({ name: 'myAccount', seeds: [] }), pdaNode({ name: 'myOtherAccount', seeds: [] })],
  92. publicKey: '1111',
  93. });
  94. // When we update the name of that account.
  95. const result = visit(
  96. node,
  97. updateAccountsVisitor({
  98. myAccount: { name: 'myNewAccount' },
  99. }),
  100. );
  101. // Then we expect the associated PDA node to have been renamed.
  102. assertIsNode(result, 'programNode');
  103. expect(result.pdas[0].name).toBe('myNewAccount' as CamelCaseString);
  104. // But not the other PDA node.
  105. expect(result.pdas[1].name).toBe('myOtherAccount' as CamelCaseString);
  106. });
  107. test('it creates a new PDA node when providing seeds to an account with no linked PDA', () => {
  108. // Given the following program node with one account.
  109. const node = rootNode(
  110. programNode({
  111. accounts: [accountNode({ name: 'myAccount' })],
  112. name: 'myProgramA',
  113. pdas: [],
  114. publicKey: '1111',
  115. }),
  116. [programNode({ name: 'myProgramB', publicKey: '2222' })],
  117. );
  118. // When we update the account with PDA seeds.
  119. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  120. const result = visit(
  121. node,
  122. updateAccountsVisitor({
  123. myAccount: { seeds },
  124. }),
  125. );
  126. assertIsNode(result, 'rootNode');
  127. // Then we expect a new PDA node to have been created on the program.
  128. expect(result.program.pdas.length).toBe(1);
  129. expect(result.additionalPrograms[0].pdas.length).toBe(0);
  130. expect(result.program.pdas[0]).toEqual(pdaNode({ name: 'myAccount', seeds }));
  131. // And the account now links to the new PDA node.
  132. expect(result.program.accounts[0].pda).toEqual(pdaLinkNode('myAccount'));
  133. });
  134. test('it updates the PDA node when the updated account name matches an existing PDA node', () => {
  135. // Given an account node and a PDA node with the same name
  136. // such that the account is not linked to the PDA.
  137. const node = programNode({
  138. accounts: [accountNode({ name: 'myAccount' })],
  139. name: 'myProgram',
  140. pdas: [pdaNode({ name: 'myAccount', seeds: [] })],
  141. publicKey: '1111',
  142. });
  143. // When we update the account with PDA seeds.
  144. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  145. const result = visit(
  146. node,
  147. updateAccountsVisitor({
  148. myAccount: { seeds },
  149. }),
  150. );
  151. assertIsNode(result, 'programNode');
  152. // Then we expect the PDA node with the same name to have been updated.
  153. expect(result.pdas.length).toBe(1);
  154. expect(result.pdas[0]).toEqual(pdaNode({ name: 'myAccount', seeds }));
  155. // And the account now links to this PDA node.
  156. expect(result.accounts[0].pda).toEqual(pdaLinkNode('myAccount'));
  157. });
  158. test('it updates the PDA node with the provided seeds when an account is linked to a PDA', () => {
  159. // Given an account node and a PDA node with a different name
  160. // such that the account is linked to the PDA.
  161. const node = programNode({
  162. accounts: [accountNode({ name: 'myAccount', pda: pdaLinkNode('myPda') })],
  163. name: 'myProgram',
  164. pdas: [pdaNode({ name: 'myPda', seeds: [] })],
  165. publicKey: '1111',
  166. });
  167. // When we update the account with PDA seeds.
  168. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  169. const result = visit(
  170. node,
  171. updateAccountsVisitor({
  172. myAccount: { seeds },
  173. }),
  174. );
  175. assertIsNode(result, 'programNode');
  176. // Then we expect the linked PDA node to have been updated.
  177. expect(result.pdas.length).toBe(1);
  178. expect(result.pdas[0]).toEqual(pdaNode({ name: 'myPda', seeds }));
  179. // And the account still links to the PDA node.
  180. expect(result.accounts[0].pda).toEqual(pdaLinkNode('myPda'));
  181. });
  182. test('it creates a new PDA node when updating an account with seeds and a new linked PDA that does not exist', () => {
  183. // Given an account node with no linked PDA.
  184. const node = programNode({
  185. accounts: [accountNode({ name: 'myAccount' })],
  186. name: 'myProgram',
  187. publicKey: '1111',
  188. });
  189. // When we update the account with PDA seeds and a new linked PDA node.
  190. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  191. const result = visit(
  192. node,
  193. updateAccountsVisitor({
  194. myAccount: {
  195. pda: pdaLinkNode('myPda'),
  196. seeds,
  197. },
  198. }),
  199. );
  200. assertIsNode(result, 'programNode');
  201. // Then we expect the linked PDA node to have been created.
  202. expect(result.pdas.length).toBe(1);
  203. expect(result.pdas[0]).toEqual(pdaNode({ name: 'myPda', seeds }));
  204. // And the account now links to the PDA node.
  205. expect(result.accounts[0].pda).toEqual(pdaLinkNode('myPda'));
  206. });
  207. test('it updates a PDA node when updating an account with seeds and a new linked PDA that exists', () => {
  208. // Given an account node with no linked PDA and an existing PDA node.
  209. const node = programNode({
  210. accounts: [accountNode({ name: 'myAccount' })],
  211. name: 'myProgram',
  212. pdas: [pdaNode({ name: 'myPda', seeds: [] })],
  213. publicKey: '1111',
  214. });
  215. // When we update the account with PDA seeds and a linked PDA node that points to the existing PDA.
  216. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  217. const result = visit(
  218. node,
  219. updateAccountsVisitor({
  220. myAccount: {
  221. pda: pdaLinkNode('myPda'),
  222. seeds,
  223. },
  224. }),
  225. );
  226. assertIsNode(result, 'programNode');
  227. // Then we expect the existing PDA node to have been updated.
  228. expect(result.pdas.length).toBe(1);
  229. expect(result.pdas[0]).toEqual(pdaNode({ name: 'myPda', seeds }));
  230. // And the account now links to this PDA node.
  231. expect(result.accounts[0].pda).toEqual(pdaLinkNode('myPda'));
  232. });
  233. test('it can update the seeds and name of an account at the same time', () => {
  234. // Given an account node with no linked PDA.
  235. const node = programNode({
  236. accounts: [accountNode({ name: 'myAccount' })],
  237. name: 'myProgram',
  238. publicKey: '1111',
  239. });
  240. // When we update the name and seeds of the account.
  241. const seeds = [constantPdaSeedNodeFromString('utf8', 'myAccount')];
  242. const result = visit(
  243. node,
  244. updateAccountsVisitor({
  245. myAccount: {
  246. name: 'myNewAccount',
  247. seeds,
  248. },
  249. }),
  250. );
  251. assertIsNode(result, 'programNode');
  252. // Then we expect the account name to have been updated.
  253. expect(result.accounts[0].name).toBe('myNewAccount' as CamelCaseString);
  254. // And a new PDA node to have been created with that new name and the provided seeds.
  255. expect(result.pdas.length).toBe(1);
  256. expect(result.pdas[0]).toEqual(pdaNode({ name: 'myNewAccount', seeds }));
  257. // And the account to now link to the PDA node.
  258. expect(result.accounts[0].pda).toEqual(pdaLinkNode('myNewAccount'));
  259. });