@0xsequence/catapult 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/README.md +27 -0
  2. package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
  3. package/dist/lib/core/__tests__/resolver.spec.js +22 -0
  4. package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
  5. package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
  6. package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
  7. package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
  8. package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
  9. package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
  10. package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
  11. package/dist/lib/core/__tests__/signer.spec.js +40 -0
  12. package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
  13. package/dist/lib/core/context.d.ts +3 -2
  14. package/dist/lib/core/context.d.ts.map +1 -1
  15. package/dist/lib/core/context.js +3 -2
  16. package/dist/lib/core/context.js.map +1 -1
  17. package/dist/lib/core/engine.d.ts +4 -0
  18. package/dist/lib/core/engine.d.ts.map +1 -1
  19. package/dist/lib/core/engine.js +173 -0
  20. package/dist/lib/core/engine.js.map +1 -1
  21. package/dist/lib/core/signer.d.ts +7 -0
  22. package/dist/lib/core/signer.d.ts.map +1 -0
  23. package/dist/lib/core/signer.js +60 -0
  24. package/dist/lib/core/signer.js.map +1 -0
  25. package/dist/lib/parsers/__tests__/source.spec.js +37 -0
  26. package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
  27. package/dist/lib/parsers/source.js +1 -1
  28. package/dist/lib/parsers/source.js.map +1 -1
  29. package/dist/lib/provenance.js +51 -2
  30. package/dist/lib/provenance.js.map +1 -1
  31. package/dist/lib/types/actions.d.ts +26 -2
  32. package/dist/lib/types/actions.d.ts.map +1 -1
  33. package/dist/lib/types/actions.js +3 -0
  34. package/dist/lib/types/actions.js.map +1 -1
  35. package/dist/lib/types/source.d.ts +2 -0
  36. package/dist/lib/types/source.d.ts.map +1 -1
  37. package/package.json +4 -1
  38. package/.eslintrc.json +0 -29
  39. package/.github/workflows/ci.yml +0 -181
  40. package/CONCEPT.md +0 -24
  41. package/contracts/checked-call.huff +0 -65
  42. package/eslint.config.js +0 -48
  43. package/examples/jobs/guards-v1.yaml +0 -17
  44. package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
  45. package/examples/jobs/sequence-v1.yaml +0 -59
  46. package/examples/templates/sequence-factory-v1.yaml +0 -56
  47. package/jest.config.js +0 -25
  48. package/src/cli.ts +0 -18
  49. package/src/commands/common.ts +0 -61
  50. package/src/commands/dry.ts +0 -209
  51. package/src/commands/etherscan.ts +0 -360
  52. package/src/commands/index.ts +0 -6
  53. package/src/commands/list.ts +0 -262
  54. package/src/commands/provenance.ts +0 -120
  55. package/src/commands/run.ts +0 -146
  56. package/src/commands/utils.ts +0 -215
  57. package/src/index.ts +0 -67
  58. package/src/lib/__tests__/deployer-events.spec.ts +0 -338
  59. package/src/lib/__tests__/deployer.spec.ts +0 -2269
  60. package/src/lib/__tests__/network-loader.spec.ts +0 -150
  61. package/src/lib/__tests__/network-selection.spec.ts +0 -41
  62. package/src/lib/__tests__/network-utils.spec.ts +0 -230
  63. package/src/lib/__tests__/provenance.spec.ts +0 -208
  64. package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
  65. package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
  66. package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
  67. package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
  68. package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
  69. package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
  70. package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
  71. package/src/lib/contracts/repository.ts +0 -411
  72. package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
  73. package/src/lib/core/__tests__/context.spec.ts +0 -37
  74. package/src/lib/core/__tests__/engine.spec.ts +0 -2005
  75. package/src/lib/core/__tests__/graph.spec.ts +0 -125
  76. package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
  77. package/src/lib/core/__tests__/loader.spec.ts +0 -367
  78. package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
  79. package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
  80. package/src/lib/core/__tests__/static-action.spec.ts +0 -172
  81. package/src/lib/core/context.ts +0 -127
  82. package/src/lib/core/engine.ts +0 -1834
  83. package/src/lib/core/graph.ts +0 -252
  84. package/src/lib/core/loader.ts +0 -253
  85. package/src/lib/core/resolver.ts +0 -873
  86. package/src/lib/deployer.ts +0 -1005
  87. package/src/lib/events/__tests__/event-system.spec.ts +0 -392
  88. package/src/lib/events/cli-adapter.ts +0 -369
  89. package/src/lib/events/emitter.ts +0 -62
  90. package/src/lib/events/index.ts +0 -3
  91. package/src/lib/events/types.ts +0 -520
  92. package/src/lib/index.ts +0 -17
  93. package/src/lib/network-loader.ts +0 -90
  94. package/src/lib/network-selection.ts +0 -73
  95. package/src/lib/network-utils.ts +0 -64
  96. package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
  97. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
  98. package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
  99. package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
  100. package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
  101. package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
  102. package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
  103. package/src/lib/parsers/__tests__/job.spec.ts +0 -439
  104. package/src/lib/parsers/__tests__/source.spec.ts +0 -134
  105. package/src/lib/parsers/__tests__/template.spec.ts +0 -111
  106. package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
  107. package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
  108. package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
  109. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
  110. package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
  111. package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
  112. package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
  113. package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
  114. package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
  115. package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
  116. package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
  117. package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
  118. package/src/lib/parsers/artifact/index.ts +0 -27
  119. package/src/lib/parsers/artifact/types.ts +0 -9
  120. package/src/lib/parsers/buildinfo.ts +0 -127
  121. package/src/lib/parsers/constants.ts +0 -56
  122. package/src/lib/parsers/index.ts +0 -6
  123. package/src/lib/parsers/job.ts +0 -160
  124. package/src/lib/parsers/source.ts +0 -129
  125. package/src/lib/parsers/template.ts +0 -135
  126. package/src/lib/provenance.ts +0 -785
  127. package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
  128. package/src/lib/std/templates/assured-deployment.yaml +0 -46
  129. package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
  130. package/src/lib/std/templates/erc-2470.yaml +0 -70
  131. package/src/lib/std/templates/min-balance.yaml +0 -35
  132. package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
  133. package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
  134. package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
  135. package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
  136. package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
  137. package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
  138. package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
  139. package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
  140. package/src/lib/types/actions.ts +0 -148
  141. package/src/lib/types/artifacts.ts +0 -21
  142. package/src/lib/types/buildinfo.ts +0 -116
  143. package/src/lib/types/conditions.ts +0 -50
  144. package/src/lib/types/contracts.ts +0 -26
  145. package/src/lib/types/definitions.ts +0 -77
  146. package/src/lib/types/index.ts +0 -9
  147. package/src/lib/types/network.ts +0 -33
  148. package/src/lib/types/project.ts +0 -9
  149. package/src/lib/types/source.ts +0 -26
  150. package/src/lib/types/task.ts +0 -9
  151. package/src/lib/types/values.ts +0 -221
  152. package/src/lib/utils/assertion.ts +0 -24
  153. package/src/lib/utils/validation.ts +0 -116
  154. package/src/lib/validation/contract-references.ts +0 -210
  155. package/src/lib/validation/index.ts +0 -1
  156. package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
  157. package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
  158. package/src/lib/verification/etherscan.ts +0 -547
  159. package/src/lib/verification/sourcify.ts +0 -248
  160. package/test_validation/artifacts/TestContract.json +0 -9
  161. package/test_validation/jobs/test-missing.yaml +0 -16
  162. package/test_validation/networks.yaml +0 -3
  163. package/tsconfig.json +0 -36
@@ -1,439 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import { parseJob } from '../job'
4
- import { Job } from '../../types'
5
-
6
- describe('parseJob', () => {
7
- // --- Happy Path Tests ---
8
-
9
- it('should correctly parse a valid, complex job with multiple actions and dependencies (sequence-v1)', () => {
10
- const yamlPath = path.resolve(__dirname, '../../../../examples/jobs/sequence-v1.yaml')
11
- const yamlContent = fs.readFileSync(yamlPath, 'utf-8')
12
-
13
- const job: Job = parseJob(yamlContent)
14
-
15
- expect(job.name).toBe('sequence-v1')
16
- expect(job.version).toBe('1.0.0')
17
- expect(job.description).toBe('The Sequence v1 contracts')
18
- expect(job.depends_on).toBeUndefined()
19
- expect(job.actions).toHaveLength(6)
20
-
21
- // Spot-check a simple action
22
- const factoryAction = job.actions.find((a) => a.name === 'factory')
23
- expect(factoryAction).toBeDefined()
24
- expect(factoryAction?.template).toBe('sequence-universal-deployer-2')
25
- expect(factoryAction?.arguments).toEqual({
26
- creationCode: '{{Contract(sequence/v1/factory).creationCode}}',
27
- salt: '0',
28
- })
29
- expect(factoryAction?.depends_on).toBeUndefined()
30
-
31
- // Spot-check an action with dependencies and complex arguments
32
- const mainModuleAction = job.actions.find((a) => a.name === 'main-module')
33
- expect(mainModuleAction).toBeDefined()
34
- expect(mainModuleAction?.template).toBe('sequence-universal-deployer-2')
35
- expect(mainModuleAction?.depends_on).toEqual(['factory'])
36
- expect(mainModuleAction?.arguments).toEqual({
37
- salt: '0',
38
- creationCode: {
39
- type: 'constructor-encode',
40
- arguments: {
41
- creationCode: '{{Contract(sequence/v1/main-module).creationCode}}',
42
- types: ['address'],
43
- values: ['{{factory.address}}'],
44
- },
45
- },
46
- })
47
- })
48
-
49
- it('should correctly parse a job with job-level depends_on (guards-v1)', () => {
50
- const yamlPath = path.resolve(__dirname, '../../../../examples/jobs/guards-v1.yaml')
51
- const yamlContent = fs.readFileSync(yamlPath, 'utf-8')
52
-
53
- const job: Job = parseJob(yamlContent)
54
-
55
- expect(job.name).toBe('guards-v1')
56
- expect(job.version).toBe('1')
57
- expect(job.description).toBe('Deploy both prod and dev guards for Sequence v1')
58
- expect(job.depends_on).toEqual(['sequence-v1'])
59
- expect(job.actions).toHaveLength(2)
60
- })
61
-
62
- it('should handle numeric versions and convert them to strings', () => {
63
- const yamlContent = `
64
- name: test-job
65
- version: 1
66
- description: A test job
67
- actions:
68
- - name: action1
69
- template: tpl1
70
- arguments: {}
71
- `
72
- const job = parseJob(yamlContent)
73
- expect(job.version).toBe('1')
74
- expect(typeof job.version).toBe('string')
75
- })
76
-
77
- // --- Error Handling and Validation Tests ---
78
-
79
- it('should throw an error for malformed YAML', () => {
80
- const invalidYaml = 'name: bad-yaml\n actions: - item1'
81
- expect(() => parseJob(invalidYaml)).toThrow(/Failed to parse job YAML:.*at line 1/)
82
- })
83
-
84
- it('should throw an error if YAML content does not resolve to an object', () => {
85
- const invalidYaml = 'just-a-string'
86
- expect(() => parseJob(invalidYaml)).toThrow('Invalid job: YAML content must resolve to an object.')
87
- })
88
-
89
- it('should throw an error if the "name" field is missing', () => {
90
- const yamlContent = `
91
- version: "1.0"
92
- actions: []
93
- `
94
- expect(() => parseJob(yamlContent)).toThrow('Invalid job: "name" field is required and must be a string.')
95
- })
96
-
97
- it('should throw an error if the "name" field is not a string', () => {
98
- const yamlContent = `
99
- name: 12345
100
- version: "1.0"
101
- actions: []
102
- `
103
- expect(() => parseJob(yamlContent)).toThrow('Invalid job: "name" field is required and must be a string.')
104
- })
105
-
106
- it('should throw an error if the "version" field is missing', () => {
107
- const yamlContent = `
108
- name: "my-job"
109
- actions: []
110
- `
111
- expect(() => parseJob(yamlContent)).toThrow('Invalid job "my-job": "version" field is required.')
112
- })
113
-
114
- it('should throw an error if the "actions" field is missing', () => {
115
- const yamlContent = `
116
- name: "my-job"
117
- version: "1"
118
- `
119
- expect(() => parseJob(yamlContent)).toThrow('Invalid job "my-job": "actions" field is required and must be an array.')
120
- })
121
-
122
- it('should throw an error if the "actions" field is not an array', () => {
123
- const yamlContent = `
124
- name: "my-job"
125
- version: "1"
126
- actions: "not-an-array"
127
- `
128
- expect(() => parseJob(yamlContent)).toThrow('Invalid job "my-job": "actions" field is required and must be an array.')
129
- })
130
-
131
- it('should throw an error if an item in the "actions" array is not an object', () => {
132
- const yamlContent = `
133
- name: "my-job"
134
- version: "1"
135
- actions:
136
- - "i-am-a-string-not-an-object"
137
- `
138
- expect(() => parseJob(yamlContent)).toThrow(
139
- 'Invalid job "my-job": contains a non-object item in "actions" array.',
140
- )
141
- })
142
-
143
- it('should throw an error if an action is missing its "name" field', () => {
144
- const yamlContent = `
145
- name: "my-job"
146
- version: "1"
147
- actions:
148
- - template: "my-template"
149
- arguments: {}
150
- `
151
- expect(() => parseJob(yamlContent)).toThrow('Invalid job "my-job": an action is missing the required "name" field.')
152
- })
153
-
154
- it('should throw an error if an action is missing its "template" field', () => {
155
- const yamlContent = `
156
- name: "my-job"
157
- version: "1"
158
- actions:
159
- - name: "my-action"
160
- arguments: {}
161
- `
162
- expect(() => parseJob(yamlContent)).toThrow(
163
- 'Invalid job "my-job": action "my-action" must have either a "template" field (for template actions) or a "type" field (for primitive actions).',
164
- )
165
- })
166
-
167
- it('should throw an error if an action is missing its "arguments" field', () => {
168
- const yamlContent = `
169
- name: "my-job"
170
- version: "1"
171
- actions:
172
- - name: "my-action"
173
- template: "my-template"
174
- `
175
- expect(() => parseJob(yamlContent)).toThrow(
176
- 'Invalid job "my-job": action "my-action" is missing the required "arguments" field or it is not an object.',
177
- )
178
- })
179
-
180
- it('should throw an error if an action has "arguments" that are not an object', () => {
181
- const yamlContent = `
182
- name: "my-job"
183
- version: "1"
184
- actions:
185
- - name: "my-action"
186
- template: "my-template"
187
- arguments: ["not", "an", "object"]
188
- `
189
- expect(() => parseJob(yamlContent)).toThrow(
190
- 'Invalid job "my-job": action "my-action" is missing the required "arguments" field or it is not an object.',
191
- )
192
- })
193
-
194
- // --- Output Field Tests ---
195
-
196
- it('should correctly parse actions with output: true', () => {
197
- const yamlContent = `
198
- name: "my-job"
199
- version: "1"
200
- actions:
201
- - name: "action-with-output"
202
- template: "my-template"
203
- arguments: {}
204
- output: true
205
- `
206
- const job = parseJob(yamlContent)
207
- expect(job.actions[0].output).toBe(true)
208
- })
209
-
210
- it('should correctly parse actions with output: false', () => {
211
- const yamlContent = `
212
- name: "my-job"
213
- version: "1"
214
- actions:
215
- - name: "action-without-output"
216
- template: "my-template"
217
- arguments: {}
218
- output: false
219
- `
220
- const job = parseJob(yamlContent)
221
- expect(job.actions[0].output).toBe(false)
222
- })
223
-
224
- it('should correctly parse actions with no output field (undefined)', () => {
225
- const yamlContent = `
226
- name: "my-job"
227
- version: "1"
228
- actions:
229
- - name: "action-no-output"
230
- template: "my-template"
231
- arguments: {}
232
- `
233
- const job = parseJob(yamlContent)
234
- expect(job.actions[0].output).toBeUndefined()
235
- })
236
-
237
- it('should correctly parse actions with output: object (custom map)', () => {
238
- const yamlContent = `
239
- name: "my-job"
240
- version: "1"
241
- actions:
242
- - name: "action-custom"
243
- template: "my-template"
244
- arguments: {}
245
- output:
246
- address: "{{someAddress}}"
247
- txHash: "{{action-custom.hash}}"
248
- `
249
- const job = parseJob(yamlContent)
250
- expect(typeof job.actions[0].output).toBe('object')
251
- expect(job.actions[0].output).toEqual({
252
- address: '{{someAddress}}',
253
- txHash: '{{action-custom.hash}}'
254
- } as any)
255
- })
256
-
257
- it('should throw an error if output field is not a boolean or object', () => {
258
- const yamlContent = `
259
- name: "my-job"
260
- version: "1"
261
- actions:
262
- - name: "my-action"
263
- template: "my-template"
264
- arguments: {}
265
- output: "not-a-boolean"
266
- `
267
- expect(() => parseJob(yamlContent)).toThrow(
268
- 'Invalid job "my-job": action "my-action" has an invalid "output" field. It must be either a boolean (true/false) or an object mapping custom outputs.',
269
- )
270
- })
271
-
272
- it('should throw an error if output field is a number', () => {
273
- const yamlContent = `
274
- name: "my-job"
275
- version: "1"
276
- actions:
277
- - name: "my-action"
278
- template: "my-template"
279
- arguments: {}
280
- output: 1
281
- `
282
- expect(() => parseJob(yamlContent)).toThrow(
283
- 'Invalid job "my-job": action "my-action" has an invalid "output" field. It must be either a boolean (true/false) or an object mapping custom outputs.',
284
- )
285
- })
286
-
287
- it('should correctly parse mixed actions with and without output fields', () => {
288
- const yamlContent = `
289
- name: "my-job"
290
- version: "1"
291
- actions:
292
- - name: "action1"
293
- template: "template1"
294
- arguments: {}
295
- output: true
296
- - name: "action2"
297
- template: "template2"
298
- arguments: {}
299
- - name: "action3"
300
- template: "template3"
301
- arguments: {}
302
- output: false
303
- - name: "action4"
304
- template: "template4"
305
- arguments: {}
306
- output:
307
- important: "{{action4.hash}}"
308
- `
309
- const job = parseJob(yamlContent)
310
- expect(job.actions[0].output).toBe(true)
311
- expect(job.actions[1].output).toBeUndefined()
312
- expect(job.actions[2].output).toBe(false)
313
- expect(job.actions[3].output).toEqual({ important: '{{action4.hash}}' } as any)
314
- })
315
-
316
- // --- New: Job-level constants field parsing ---
317
-
318
- it('should attach an optional job-level constants block as an object', () => {
319
- const yamlContent = `
320
- name: "job-with-constants"
321
- version: "1"
322
- constants:
323
- FEE: "1000"
324
- ADMIN: "0x0000000000000000000000000000000000000001"
325
- actions:
326
- - name: "a1"
327
- template: "t1"
328
- arguments:
329
- x: "{{FEE}}"
330
- `
331
- const job = parseJob(yamlContent)
332
- expect(job.constants).toEqual({
333
- FEE: '1000',
334
- ADMIN: '0x0000000000000000000000000000000000000001'
335
- })
336
- })
337
-
338
- it('should parse job-level skip conditions', () => {
339
- const yamlContent = `
340
- name: "job-with-skip"
341
- version: "1"
342
- skip_condition:
343
- - type: "contract-exists"
344
- arguments:
345
- address: "0xabc"
346
- actions:
347
- - name: "a1"
348
- template: "t1"
349
- arguments: {}
350
- `
351
- const job = parseJob(yamlContent)
352
- expect(job.skip_condition).toHaveLength(1)
353
- expect(job.skip_condition?.[0]).toEqual({
354
- type: 'contract-exists',
355
- arguments: { address: '0xabc' }
356
- })
357
- })
358
-
359
- it('should parse job-level skip_if conditions', () => {
360
- const yamlContent = `
361
- name: "job-with-skip-if"
362
- version: "1"
363
- skip_if:
364
- - type: "contract-exists"
365
- arguments:
366
- address: "0xdef"
367
- actions:
368
- - name: "a1"
369
- template: "t1"
370
- arguments: {}
371
- `
372
- const job = parseJob(yamlContent)
373
- expect(job.skip_if).toHaveLength(1)
374
- expect(job.skip_if?.[0]).toEqual({
375
- type: 'contract-exists',
376
- arguments: { address: '0xdef' }
377
- })
378
- })
379
-
380
- it('should parse both skip_condition and skip_if on a job', () => {
381
- const yamlContent = `
382
- name: "job-with-both"
383
- version: "1"
384
- skip_condition:
385
- - type: "contract-exists"
386
- arguments:
387
- address: "0xabc"
388
- skip_if:
389
- - type: "job-completed"
390
- arguments:
391
- job: "other-job"
392
- actions:
393
- - name: "a1"
394
- template: "t1"
395
- arguments: {}
396
- `
397
- const job = parseJob(yamlContent)
398
- expect(job.skip_condition).toHaveLength(1)
399
- expect(job.skip_condition?.[0]).toEqual({
400
- type: 'contract-exists',
401
- arguments: { address: '0xabc' }
402
- })
403
- expect(job.skip_if).toHaveLength(1)
404
- expect(job.skip_if?.[0]).toEqual({
405
- type: 'job-completed',
406
- arguments: { job: 'other-job' }
407
- })
408
- })
409
-
410
- it('should throw when skip_if is not an array', () => {
411
- const yamlContent = `
412
- name: "job-with-invalid-skip-if"
413
- version: "1"
414
- skip_if:
415
- type: "contract-exists"
416
- arguments:
417
- address: "0xabc"
418
- actions:
419
- - name: "a1"
420
- template: "t1"
421
- arguments: {}
422
- `
423
- expect(() => parseJob(yamlContent)).toThrow('"skip_if" must be an array if provided')
424
- })
425
-
426
- it('should throw when skip_if contains invalid condition', () => {
427
- const yamlContent = `
428
- name: "job-with-invalid-skip-if"
429
- version: "1"
430
- skip_if:
431
- - type: "invalid-type"
432
- actions:
433
- - name: "a1"
434
- template: "t1"
435
- arguments: {}
436
- `
437
- expect(() => parseJob(yamlContent)).toThrow('"skip_if" contains an invalid condition entry')
438
- })
439
- })
@@ -1,134 +0,0 @@
1
- import { mergeSourceProvenance, parseSourceDocument } from '../source'
2
-
3
- describe('Source Provenance Parsing', () => {
4
- it('should return null for non-source YAML documents', () => {
5
- const result = parseSourceDocument(`
6
- type: constants
7
- constants:
8
- value: 1
9
- `)
10
-
11
- expect(result).toBeNull()
12
- })
13
-
14
- it('should parse source provenance documents', () => {
15
- const result = parseSourceDocument(`
16
- type: source
17
- build_info:
18
- "./stage1.json":
19
- repo: "https://github.com/0xsequence/wallet-contracts-v3"
20
- ref: "v3.0.0-rc.5"
21
- commit: "0d9061f229da73edae890e6fdd1fbf753028df6d"
22
- build: "forge build --build-info"
23
- contracts:
24
- "src/Stage1Module.sol:Stage1Module":
25
- ref: "stage1-special"
26
- `)
27
-
28
- expect(result).not.toBeNull()
29
- expect(result!.build_info['./stage1.json'].repo).toBe('https://github.com/0xsequence/wallet-contracts-v3')
30
- expect(result!.build_info['./stage1.json'].contracts?.['src/Stage1Module.sol:Stage1Module'].ref).toBe('stage1-special')
31
- })
32
-
33
- it('should require repo on each build-info provenance entry', () => {
34
- const result = parseSourceDocument(`
35
- type: source
36
- build_info:
37
- "./stage1.json":
38
- ref: "v3.0.0-rc.5"
39
- `)
40
-
41
- expect(result).not.toBeNull()
42
- expect(result!.build_info).toEqual({})
43
- expect(result!.warnings).toEqual([
44
- expect.stringContaining('build_info["./stage1.json"].repo')
45
- ])
46
- })
47
-
48
- it('should validate optional string fields', () => {
49
- const result = parseSourceDocument(`
50
- type: source
51
- build_info:
52
- "./stage1.json":
53
- repo: "https://github.com/0xsequence/wallet-contracts-v3"
54
- commit: 123
55
- `)
56
-
57
- expect(result).not.toBeNull()
58
- expect(result!.build_info).toEqual({})
59
- expect(result!.warnings).toEqual([
60
- expect.stringContaining('commit')
61
- ])
62
- })
63
-
64
- it('should reject unsupported fields', () => {
65
- const result = parseSourceDocument(`
66
- type: source
67
- build_info:
68
- "./stage1.json":
69
- repo: "https://github.com/0xsequence/wallet-contracts-v3"
70
- path: "contracts"
71
- `)
72
-
73
- expect(result).not.toBeNull()
74
- expect(result!.build_info).toEqual({})
75
- expect(result!.warnings).toEqual([
76
- expect.stringContaining('path is not supported')
77
- ])
78
- })
79
-
80
- it('should quote build-info paths in validation errors', () => {
81
- const result = parseSourceDocument(`
82
- type: source
83
- build_info:
84
- "./stage1.json":
85
- ref: "v3.0.0-rc.5"
86
- `)
87
-
88
- expect(result).not.toBeNull()
89
- expect(result!.warnings).toEqual([
90
- expect.stringContaining('build_info["./stage1.json"].repo')
91
- ])
92
- })
93
-
94
- it('should keep valid entries when sibling entries are invalid', () => {
95
- const result = parseSourceDocument(`
96
- type: source
97
- build_info:
98
- "./stage1.json":
99
- repo: "https://github.com/0xsequence/wallet-contracts-v3"
100
- "./bad.json":
101
- typo_field: true
102
- `)
103
-
104
- expect(result).not.toBeNull()
105
- expect(Object.keys(result!.build_info)).toEqual(['./stage1.json'])
106
- expect(result!.warnings).toEqual([
107
- expect.stringContaining('typo_field is not supported')
108
- ])
109
- })
110
-
111
- describe('mergeSourceProvenance', () => {
112
- it('should merge contract overrides into build-info provenance', () => {
113
- const result = mergeSourceProvenance({
114
- repo: 'https://github.com/0xsequence/wallet-contracts-v3',
115
- ref: 'v3.0.0-rc.5',
116
- commit: '0d9061f229da73edae890e6fdd1fbf753028df6d',
117
- contracts: {
118
- 'src/Stage1Module.sol:Stage1Module': {
119
- ref: 'stage1-special'
120
- }
121
- }
122
- }, {
123
- ref: 'stage1-special'
124
- })
125
-
126
- expect(result).toEqual({
127
- repo: 'https://github.com/0xsequence/wallet-contracts-v3',
128
- ref: 'stage1-special',
129
- commit: '0d9061f229da73edae890e6fdd1fbf753028df6d'
130
- })
131
- expect(result).not.toHaveProperty('contracts')
132
- })
133
- })
134
- })
@@ -1,111 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as path from 'path'
3
- import { parseTemplate } from '../template'
4
- import { isContractExistsCondition, isJobCompletedCondition, Template } from '../../types'
5
-
6
- describe('parseTemplate', () => {
7
- // --- Happy Path Tests ---
8
-
9
- it('should correctly parse a valid template with a complex structure (sequence-factory-v1)', () => {
10
- const yamlPath = path.resolve(__dirname, '../../../../examples/templates/sequence-factory-v1.yaml')
11
- const yamlContent = fs.readFileSync(yamlPath, 'utf-8')
12
-
13
- const template = parseTemplate(yamlContent)
14
-
15
- expect(template.name).toBe('sequence-factory-v1')
16
- expect(template.arguments).toBeDefined()
17
- expect(template.returns).toBeDefined()
18
- expect(template.actions).toHaveLength(1)
19
- expect(template.outputs).toBeDefined()
20
- expect(template.outputs?.address).toBeDefined()
21
-
22
- // Test the specific 'setup' block parsing where it's an array of conditions
23
- expect(template.setup).toBeDefined()
24
- expect(template.setup?.actions).toBeUndefined() // No actions in this setup
25
- expect(template.setup?.skip_condition).toHaveLength(1)
26
- expect(isJobCompletedCondition(template.setup?.skip_condition?.[0])).toBe(true)
27
- })
28
-
29
- it('should correctly parse a template with an object-based setup block (nano-universal-deployer)', () => {
30
- const yamlPath = path.resolve(__dirname, '../../std/templates/nano-universal-deployer.yaml')
31
- const yamlContent = fs.readFileSync(yamlPath, 'utf-8')
32
-
33
- const template = parseTemplate(yamlContent)
34
-
35
- expect(template.name).toBe('nano-universal-deployer')
36
-
37
- // Test the specific 'setup' block parsing where it's a structured object
38
- expect(template.setup).toBeDefined()
39
- expect(template.setup?.actions).toHaveLength(3)
40
- expect(template.setup?.skip_condition).toHaveLength(1)
41
- expect(template.setup?.actions?.[0].type).toBe('test-nicks-method')
42
- expect(isContractExistsCondition(template.setup?.skip_condition?.[0])).toBe(true)
43
- })
44
-
45
- it('should parse a template with no optional fields like setup, description, arguments, or outputs', () => {
46
- const minimalYaml = `
47
- name: minimal-template
48
- actions:
49
- - type: send-transaction
50
- arguments:
51
- to: '0x123'
52
- data: '0xabc'
53
- `
54
- const template = parseTemplate(minimalYaml)
55
- expect(template.name).toBe('minimal-template')
56
- expect(template.description).toBeUndefined()
57
- expect(template.arguments).toBeUndefined()
58
- expect(template.setup).toBeUndefined()
59
- expect(template.actions).toHaveLength(1)
60
- expect(template.outputs).toBeUndefined()
61
- })
62
-
63
- // --- Error Handling and Validation Tests ---
64
-
65
- it('should throw an error for malformed YAML', () => {
66
- const invalidYaml = `
67
- name: bad-yaml
68
- actions: - item1
69
- `
70
- expect(() => parseTemplate(invalidYaml)).toThrow(/Failed to parse template YAML:.* at line 2/)
71
- })
72
-
73
- it('should throw an error if the "name" field is missing', () => {
74
- const yamlContent = `
75
- version: "1.0"
76
- actions: []
77
- `
78
- expect(() => parseTemplate(yamlContent)).toThrow('Invalid template: "name" field is required and must be a string.')
79
- })
80
-
81
- it('should throw an error if the "actions" field is missing or not an array', () => {
82
- const missingActions = `
83
- name: "my-template"
84
- `
85
- expect(() => parseTemplate(missingActions)).toThrow('Invalid template "my-template": "actions" field is required and must be an array.')
86
-
87
- const wrongTypeActions = `
88
- name: "my-template"
89
- actions: "not-an-array"
90
- `
91
- expect(() => parseTemplate(wrongTypeActions)).toThrow('Invalid template "my-template": "actions" field is required and must be an array.')
92
- })
93
-
94
- it('should throw an error if the "outputs" field is not an object when provided', () => {
95
- const wrongTypeOutputs = `
96
- name: "my-template"
97
- actions: []
98
- outputs: ["not-an-object"]
99
- `
100
- expect(() => parseTemplate(wrongTypeOutputs)).toThrow('Invalid template "my-template": "outputs" field must be an object if provided.')
101
- })
102
-
103
- it('should throw an error if the "setup" field is not an array or object', () => {
104
- const invalidSetup = `
105
- name: "my-template"
106
- actions: []
107
- setup: "i-am-a-string"
108
- `
109
- expect(() => parseTemplate(invalidSetup)).toThrow('Invalid template "my-template": "setup" field must be an array or an object if provided.')
110
- })
111
- })