@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.
- package/README.md +27 -0
- package/dist/lib/__tests__/network-loader.spec.js.map +1 -1
- package/dist/lib/core/__tests__/resolver.spec.js +22 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -1
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/sign-actions.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js +128 -0
- package/dist/lib/core/__tests__/sign-actions.spec.js.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/signer.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/signer.spec.js +40 -0
- package/dist/lib/core/__tests__/signer.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +3 -2
- package/dist/lib/core/context.d.ts.map +1 -1
- package/dist/lib/core/context.js +3 -2
- package/dist/lib/core/context.js.map +1 -1
- package/dist/lib/core/engine.d.ts +4 -0
- package/dist/lib/core/engine.d.ts.map +1 -1
- package/dist/lib/core/engine.js +173 -0
- package/dist/lib/core/engine.js.map +1 -1
- package/dist/lib/core/signer.d.ts +7 -0
- package/dist/lib/core/signer.d.ts.map +1 -0
- package/dist/lib/core/signer.js +60 -0
- package/dist/lib/core/signer.js.map +1 -0
- package/dist/lib/parsers/__tests__/source.spec.js +37 -0
- package/dist/lib/parsers/__tests__/source.spec.js.map +1 -1
- package/dist/lib/parsers/source.js +1 -1
- package/dist/lib/parsers/source.js.map +1 -1
- package/dist/lib/provenance.js +51 -2
- package/dist/lib/provenance.js.map +1 -1
- package/dist/lib/types/actions.d.ts +26 -2
- package/dist/lib/types/actions.d.ts.map +1 -1
- package/dist/lib/types/actions.js +3 -0
- package/dist/lib/types/actions.js.map +1 -1
- package/dist/lib/types/source.d.ts +2 -0
- package/dist/lib/types/source.d.ts.map +1 -1
- package/package.json +4 -1
- package/.eslintrc.json +0 -29
- package/.github/workflows/ci.yml +0 -181
- package/CONCEPT.md +0 -24
- package/contracts/checked-call.huff +0 -65
- package/eslint.config.js +0 -48
- package/examples/jobs/guards-v1.yaml +0 -17
- package/examples/jobs/sequence-seq-0001-patch.yaml +0 -59
- package/examples/jobs/sequence-v1.yaml +0 -59
- package/examples/templates/sequence-factory-v1.yaml +0 -56
- package/jest.config.js +0 -25
- package/src/cli.ts +0 -18
- package/src/commands/common.ts +0 -61
- package/src/commands/dry.ts +0 -209
- package/src/commands/etherscan.ts +0 -360
- package/src/commands/index.ts +0 -6
- package/src/commands/list.ts +0 -262
- package/src/commands/provenance.ts +0 -120
- package/src/commands/run.ts +0 -146
- package/src/commands/utils.ts +0 -215
- package/src/index.ts +0 -67
- package/src/lib/__tests__/deployer-events.spec.ts +0 -338
- package/src/lib/__tests__/deployer.spec.ts +0 -2269
- package/src/lib/__tests__/network-loader.spec.ts +0 -150
- package/src/lib/__tests__/network-selection.spec.ts +0 -41
- package/src/lib/__tests__/network-utils.spec.ts +0 -230
- package/src/lib/__tests__/provenance.spec.ts +0 -208
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +0 -19
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +0 -18
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +0 -8
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +0 -2
- package/src/lib/contracts/__tests__/repository.spec.ts +0 -612
- package/src/lib/contracts/repository.ts +0 -411
- package/src/lib/core/__tests__/assert-action.spec.ts +0 -474
- package/src/lib/core/__tests__/context.spec.ts +0 -37
- package/src/lib/core/__tests__/engine.spec.ts +0 -2005
- package/src/lib/core/__tests__/graph.spec.ts +0 -125
- package/src/lib/core/__tests__/json-integration.spec.ts +0 -425
- package/src/lib/core/__tests__/loader.spec.ts +0 -367
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +0 -406
- package/src/lib/core/__tests__/resolver.spec.ts +0 -2496
- package/src/lib/core/__tests__/static-action.spec.ts +0 -172
- package/src/lib/core/context.ts +0 -127
- package/src/lib/core/engine.ts +0 -1834
- package/src/lib/core/graph.ts +0 -252
- package/src/lib/core/loader.ts +0 -253
- package/src/lib/core/resolver.ts +0 -873
- package/src/lib/deployer.ts +0 -1005
- package/src/lib/events/__tests__/event-system.spec.ts +0 -392
- package/src/lib/events/cli-adapter.ts +0 -369
- package/src/lib/events/emitter.ts +0 -62
- package/src/lib/events/index.ts +0 -3
- package/src/lib/events/types.ts +0 -520
- package/src/lib/index.ts +0 -17
- package/src/lib/network-loader.ts +0 -90
- package/src/lib/network-selection.ts +0 -73
- package/src/lib/network-utils.ts +0 -64
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +0 -122
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +0 -62
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +0 -2
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +0 -89
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +0 -17
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +0 -63
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +0 -4
- package/src/lib/parsers/__tests__/job.spec.ts +0 -439
- package/src/lib/parsers/__tests__/source.spec.ts +0 -134
- package/src/lib/parsers/__tests__/template.spec.ts +0 -111
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +0 -117
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +0 -67
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +0 -5
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +0 -4
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +0 -11
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +0 -40
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +0 -7
- package/src/lib/parsers/artifact/foundry-1.2.ts +0 -72
- package/src/lib/parsers/artifact/index.ts +0 -27
- package/src/lib/parsers/artifact/types.ts +0 -9
- package/src/lib/parsers/buildinfo.ts +0 -127
- package/src/lib/parsers/constants.ts +0 -56
- package/src/lib/parsers/index.ts +0 -6
- package/src/lib/parsers/job.ts +0 -160
- package/src/lib/parsers/source.ts +0 -129
- package/src/lib/parsers/template.ts +0 -135
- package/src/lib/provenance.ts +0 -785
- package/src/lib/std/templates/arachnid-deterministic-deployment-proxy.yaml +0 -68
- package/src/lib/std/templates/assured-deployment.yaml +0 -46
- package/src/lib/std/templates/era-evm-predeploy.yaml +0 -35
- package/src/lib/std/templates/erc-2470.yaml +0 -70
- package/src/lib/std/templates/min-balance.yaml +0 -35
- package/src/lib/std/templates/nano-universal-deployer.yaml +0 -61
- package/src/lib/std/templates/raw-erc-2470.yaml +0 -62
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +0 -54
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +0 -52
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +0 -61
- package/src/lib/types/__tests__/json-request-action.spec.ts +0 -243
- package/src/lib/types/__tests__/read-json-value.spec.ts +0 -278
- package/src/lib/types/__tests__/resolve-json-value.spec.ts +0 -769
- package/src/lib/types/actions.ts +0 -148
- package/src/lib/types/artifacts.ts +0 -21
- package/src/lib/types/buildinfo.ts +0 -116
- package/src/lib/types/conditions.ts +0 -50
- package/src/lib/types/contracts.ts +0 -26
- package/src/lib/types/definitions.ts +0 -77
- package/src/lib/types/index.ts +0 -9
- package/src/lib/types/network.ts +0 -33
- package/src/lib/types/project.ts +0 -9
- package/src/lib/types/source.ts +0 -26
- package/src/lib/types/task.ts +0 -9
- package/src/lib/types/values.ts +0 -221
- package/src/lib/utils/assertion.ts +0 -24
- package/src/lib/utils/validation.ts +0 -116
- package/src/lib/validation/contract-references.ts +0 -210
- package/src/lib/validation/index.ts +0 -1
- package/src/lib/verification/__tests__/etherscan.spec.ts +0 -710
- package/src/lib/verification/__tests__/sourcify.spec.ts +0 -288
- package/src/lib/verification/etherscan.ts +0 -547
- package/src/lib/verification/sourcify.ts +0 -248
- package/test_validation/artifacts/TestContract.json +0 -9
- package/test_validation/jobs/test-missing.yaml +0 -16
- package/test_validation/networks.yaml +0 -3
- 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
|
-
})
|