@0xsequence/catapult 1.1.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/.eslintrc.json +29 -0
- package/.github/workflows/ci.yml +181 -0
- package/CONCEPT.md +24 -0
- package/README.md +772 -0
- package/contracts/checked-call.huff +65 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/common.d.ts +11 -0
- package/dist/commands/common.d.ts.map +1 -0
- package/dist/commands/common.js +73 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/dry.d.ts +3 -0
- package/dist/commands/dry.d.ts.map +1 -0
- package/dist/commands/dry.js +171 -0
- package/dist/commands/dry.js.map +1 -0
- package/dist/commands/etherscan.d.ts +3 -0
- package/dist/commands/etherscan.d.ts.map +1 -0
- package/dist/commands/etherscan.js +323 -0
- package/dist/commands/etherscan.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +22 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +259 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +96 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/utils.d.ts +3 -0
- package/dist/commands/utils.d.ts.map +1 -0
- package/dist/commands/utils.js +46 -0
- package/dist/commands/utils.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/__tests__/deployer-events.spec.d.ts +2 -0
- package/dist/lib/__tests__/deployer-events.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/deployer-events.spec.js +260 -0
- package/dist/lib/__tests__/deployer-events.spec.js.map +1 -0
- package/dist/lib/__tests__/deployer.spec.d.ts +2 -0
- package/dist/lib/__tests__/deployer.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/deployer.spec.js +884 -0
- package/dist/lib/__tests__/deployer.spec.js.map +1 -0
- package/dist/lib/__tests__/network-utils.spec.d.ts +2 -0
- package/dist/lib/__tests__/network-utils.spec.d.ts.map +1 -0
- package/dist/lib/__tests__/network-utils.spec.js +140 -0
- package/dist/lib/__tests__/network-utils.spec.js.map +1 -0
- package/dist/lib/contracts/__tests__/repository.spec.d.ts +2 -0
- package/dist/lib/contracts/__tests__/repository.spec.d.ts.map +1 -0
- package/dist/lib/contracts/__tests__/repository.spec.js +321 -0
- package/dist/lib/contracts/__tests__/repository.spec.js.map +1 -0
- package/dist/lib/contracts/repository.d.ts +27 -0
- package/dist/lib/contracts/repository.d.ts.map +1 -0
- package/dist/lib/contracts/repository.js +241 -0
- package/dist/lib/contracts/repository.js.map +1 -0
- package/dist/lib/core/__tests__/engine.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/engine.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/engine.spec.js +1212 -0
- package/dist/lib/core/__tests__/engine.spec.js.map +1 -0
- package/dist/lib/core/__tests__/graph.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/graph.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/graph.spec.js +116 -0
- package/dist/lib/core/__tests__/graph.spec.js.map +1 -0
- package/dist/lib/core/__tests__/json-integration.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/json-integration.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/json-integration.spec.js +300 -0
- package/dist/lib/core/__tests__/json-integration.spec.js.map +1 -0
- package/dist/lib/core/__tests__/loader.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/loader.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/loader.spec.js +288 -0
- package/dist/lib/core/__tests__/loader.spec.js.map +1 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.js +342 -0
- package/dist/lib/core/__tests__/multi-platform-verification.spec.js.map +1 -0
- package/dist/lib/core/__tests__/resolver.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/resolver.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/resolver.spec.js +1367 -0
- package/dist/lib/core/__tests__/resolver.spec.js.map +1 -0
- package/dist/lib/core/__tests__/static-action.spec.d.ts +2 -0
- package/dist/lib/core/__tests__/static-action.spec.d.ts.map +1 -0
- package/dist/lib/core/__tests__/static-action.spec.js +136 -0
- package/dist/lib/core/__tests__/static-action.spec.js.map +1 -0
- package/dist/lib/core/context.d.ts +29 -0
- package/dist/lib/core/context.d.ts.map +1 -0
- package/dist/lib/core/context.js +88 -0
- package/dist/lib/core/context.js.map +1 -0
- package/dist/lib/core/engine.d.ts +25 -0
- package/dist/lib/core/engine.d.ts.map +1 -0
- package/dist/lib/core/engine.js +1191 -0
- package/dist/lib/core/engine.js.map +1 -0
- package/dist/lib/core/graph.d.ts +18 -0
- package/dist/lib/core/graph.d.ts.map +1 -0
- package/dist/lib/core/graph.js +158 -0
- package/dist/lib/core/graph.js.map +1 -0
- package/dist/lib/core/loader.d.ts +25 -0
- package/dist/lib/core/loader.d.ts.map +1 -0
- package/dist/lib/core/loader.js +248 -0
- package/dist/lib/core/loader.js.map +1 -0
- package/dist/lib/core/resolver.d.ts +20 -0
- package/dist/lib/core/resolver.d.ts.map +1 -0
- package/dist/lib/core/resolver.js +307 -0
- package/dist/lib/core/resolver.js.map +1 -0
- package/dist/lib/deployer.d.ts +39 -0
- package/dist/lib/deployer.d.ts.map +1 -0
- package/dist/lib/deployer.js +533 -0
- package/dist/lib/deployer.js.map +1 -0
- package/dist/lib/events/__tests__/event-system.spec.d.ts +2 -0
- package/dist/lib/events/__tests__/event-system.spec.d.ts.map +1 -0
- package/dist/lib/events/__tests__/event-system.spec.js +256 -0
- package/dist/lib/events/__tests__/event-system.spec.js.map +1 -0
- package/dist/lib/events/cli-adapter.d.ts +13 -0
- package/dist/lib/events/cli-adapter.d.ts.map +1 -0
- package/dist/lib/events/cli-adapter.js +244 -0
- package/dist/lib/events/cli-adapter.js.map +1 -0
- package/dist/lib/events/emitter.d.ts +11 -0
- package/dist/lib/events/emitter.d.ts.map +1 -0
- package/dist/lib/events/emitter.js +29 -0
- package/dist/lib/events/emitter.js.map +1 -0
- package/dist/lib/events/index.d.ts +4 -0
- package/dist/lib/events/index.d.ts.map +1 -0
- package/dist/lib/events/index.js +20 -0
- package/dist/lib/events/index.js.map +1 -0
- package/dist/lib/events/types.d.ts +368 -0
- package/dist/lib/events/types.d.ts.map +1 -0
- package/dist/lib/events/types.js +3 -0
- package/dist/lib/events/types.js.map +1 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +44 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/network-loader.d.ts +3 -0
- package/dist/lib/network-loader.d.ts.map +1 -0
- package/dist/lib/network-loader.js +80 -0
- package/dist/lib/network-loader.js.map +1 -0
- package/dist/lib/network-match.d.ts +3 -0
- package/dist/lib/network-match.d.ts.map +1 -0
- package/dist/lib/network-match.js +62 -0
- package/dist/lib/network-match.js.map +1 -0
- package/dist/lib/network-utils.d.ts +4 -0
- package/dist/lib/network-utils.d.ts.map +1 -0
- package/dist/lib/network-utils.js +39 -0
- package/dist/lib/network-utils.js.map +1 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.js +132 -0
- package/dist/lib/parsers/__tests__/buildinfo.spec.js.map +1 -0
- package/dist/lib/parsers/__tests__/job.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/job.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/job.spec.js +318 -0
- package/dist/lib/parsers/__tests__/job.spec.js.map +1 -0
- package/dist/lib/parsers/__tests__/template.spec.d.ts +2 -0
- package/dist/lib/parsers/__tests__/template.spec.d.ts.map +1 -0
- package/dist/lib/parsers/__tests__/template.spec.js +126 -0
- package/dist/lib/parsers/__tests__/template.spec.js.map +1 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts +2 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.js +128 -0
- package/dist/lib/parsers/artifact/__tests__/artifact.spec.js.map +1 -0
- package/dist/lib/parsers/artifact/foundry-1.2.d.ts +3 -0
- package/dist/lib/parsers/artifact/foundry-1.2.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/foundry-1.2.js +82 -0
- package/dist/lib/parsers/artifact/foundry-1.2.js.map +1 -0
- package/dist/lib/parsers/artifact/index.d.ts +3 -0
- package/dist/lib/parsers/artifact/index.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/index.js +17 -0
- package/dist/lib/parsers/artifact/index.js.map +1 -0
- package/dist/lib/parsers/artifact/types.d.ts +3 -0
- package/dist/lib/parsers/artifact/types.d.ts.map +1 -0
- package/dist/lib/parsers/artifact/types.js +3 -0
- package/dist/lib/parsers/artifact/types.js.map +1 -0
- package/dist/lib/parsers/buildinfo.d.ts +5 -0
- package/dist/lib/parsers/buildinfo.d.ts.map +1 -0
- package/dist/lib/parsers/buildinfo.js +85 -0
- package/dist/lib/parsers/buildinfo.js.map +1 -0
- package/dist/lib/parsers/constants.d.ts +4 -0
- package/dist/lib/parsers/constants.d.ts.map +1 -0
- package/dist/lib/parsers/constants.js +45 -0
- package/dist/lib/parsers/constants.js.map +1 -0
- package/dist/lib/parsers/index.d.ts +5 -0
- package/dist/lib/parsers/index.d.ts.map +1 -0
- package/dist/lib/parsers/index.js +21 -0
- package/dist/lib/parsers/index.js.map +1 -0
- package/dist/lib/parsers/job.d.ts +3 -0
- package/dist/lib/parsers/job.d.ts.map +1 -0
- package/dist/lib/parsers/job.js +74 -0
- package/dist/lib/parsers/job.js.map +1 -0
- package/dist/lib/parsers/template.d.ts +3 -0
- package/dist/lib/parsers/template.d.ts.map +1 -0
- package/dist/lib/parsers/template.js +91 -0
- package/dist/lib/parsers/template.js.map +1 -0
- package/dist/lib/std/templates/assured-deployment.yaml +45 -0
- package/dist/lib/std/templates/erc-2470.yaml +67 -0
- package/dist/lib/std/templates/min-balance.yaml +32 -0
- package/dist/lib/std/templates/nano-universal-deployer.yaml +59 -0
- package/dist/lib/std/templates/raw-erc-2470.yaml +59 -0
- package/dist/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
- package/dist/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
- package/dist/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
- package/dist/lib/types/__tests__/json-request-action.spec.d.ts +2 -0
- package/dist/lib/types/__tests__/json-request-action.spec.d.ts.map +1 -0
- package/dist/lib/types/__tests__/json-request-action.spec.js +219 -0
- package/dist/lib/types/__tests__/json-request-action.spec.js.map +1 -0
- package/dist/lib/types/__tests__/read-json-value.spec.d.ts +2 -0
- package/dist/lib/types/__tests__/read-json-value.spec.d.ts.map +1 -0
- package/dist/lib/types/__tests__/read-json-value.spec.js +233 -0
- package/dist/lib/types/__tests__/read-json-value.spec.js.map +1 -0
- package/dist/lib/types/actions.d.ts +74 -0
- package/dist/lib/types/actions.d.ts.map +1 -0
- package/dist/lib/types/actions.js +18 -0
- package/dist/lib/types/actions.js.map +1 -0
- package/dist/lib/types/artifacts.d.ts +15 -0
- package/dist/lib/types/artifacts.d.ts.map +1 -0
- package/dist/lib/types/artifacts.js +3 -0
- package/dist/lib/types/artifacts.js.map +1 -0
- package/dist/lib/types/buildinfo.d.ts +112 -0
- package/dist/lib/types/buildinfo.d.ts.map +1 -0
- package/dist/lib/types/buildinfo.js +3 -0
- package/dist/lib/types/buildinfo.js.map +1 -0
- package/dist/lib/types/conditions.d.ts +17 -0
- package/dist/lib/types/conditions.d.ts.map +1 -0
- package/dist/lib/types/conditions.js +21 -0
- package/dist/lib/types/conditions.js.map +1 -0
- package/dist/lib/types/contracts.d.ts +14 -0
- package/dist/lib/types/contracts.d.ts.map +1 -0
- package/dist/lib/types/contracts.js +3 -0
- package/dist/lib/types/contracts.js.map +1 -0
- package/dist/lib/types/definitions.d.ts +51 -0
- package/dist/lib/types/definitions.d.ts.map +1 -0
- package/dist/lib/types/definitions.js +3 -0
- package/dist/lib/types/definitions.js.map +1 -0
- package/dist/lib/types/index.d.ts +9 -0
- package/dist/lib/types/index.d.ts.map +1 -0
- package/dist/lib/types/index.js +25 -0
- package/dist/lib/types/index.js.map +1 -0
- package/dist/lib/types/network.d.ts +9 -0
- package/dist/lib/types/network.d.ts.map +1 -0
- package/dist/lib/types/network.js +3 -0
- package/dist/lib/types/network.js.map +1 -0
- package/dist/lib/types/project.d.ts +5 -0
- package/dist/lib/types/project.d.ts.map +1 -0
- package/dist/lib/types/project.js +3 -0
- package/dist/lib/types/project.js.map +1 -0
- package/dist/lib/types/task.d.ts +9 -0
- package/dist/lib/types/task.d.ts.map +1 -0
- package/dist/lib/types/task.js +3 -0
- package/dist/lib/types/task.js.map +1 -0
- package/dist/lib/types/values.d.ts +78 -0
- package/dist/lib/types/values.d.ts.map +1 -0
- package/dist/lib/types/values.js +3 -0
- package/dist/lib/types/values.js.map +1 -0
- package/dist/lib/utils/validation.d.ts +5 -0
- package/dist/lib/utils/validation.d.ts.map +1 -0
- package/dist/lib/utils/validation.js +77 -0
- package/dist/lib/utils/validation.js.map +1 -0
- package/dist/lib/validation/contract-references.d.ts +12 -0
- package/dist/lib/validation/contract-references.d.ts.map +1 -0
- package/dist/lib/validation/contract-references.js +112 -0
- package/dist/lib/validation/contract-references.js.map +1 -0
- package/dist/lib/validation/index.d.ts +1 -0
- package/dist/lib/validation/index.d.ts.map +1 -0
- package/dist/lib/validation/index.js +2 -0
- package/dist/lib/validation/index.js.map +1 -0
- package/dist/lib/verification/__tests__/etherscan.spec.d.ts +2 -0
- package/dist/lib/verification/__tests__/etherscan.spec.d.ts.map +1 -0
- package/dist/lib/verification/__tests__/etherscan.spec.js +565 -0
- package/dist/lib/verification/__tests__/etherscan.spec.js.map +1 -0
- package/dist/lib/verification/__tests__/sourcify.spec.d.ts +2 -0
- package/dist/lib/verification/__tests__/sourcify.spec.d.ts.map +1 -0
- package/dist/lib/verification/__tests__/sourcify.spec.js +212 -0
- package/dist/lib/verification/__tests__/sourcify.spec.js.map +1 -0
- package/dist/lib/verification/etherscan.d.ts +56 -0
- package/dist/lib/verification/etherscan.d.ts.map +1 -0
- package/dist/lib/verification/etherscan.js +340 -0
- package/dist/lib/verification/etherscan.js.map +1 -0
- package/dist/lib/verification/sourcify.d.ts +12 -0
- package/dist/lib/verification/sourcify.d.ts.map +1 -0
- package/dist/lib/verification/sourcify.js +227 -0
- package/dist/lib/verification/sourcify.js.map +1 -0
- package/eslint.config.js +48 -0
- package/examples/jobs/guards-v1.yaml +17 -0
- package/examples/jobs/sequence-seq-0001-patch.yaml +59 -0
- package/examples/jobs/sequence-v1.yaml +59 -0
- package/examples/templates/sequence-factory-v1.yaml +56 -0
- package/jest.config.js +25 -0
- package/package.json +68 -0
- package/src/cli.ts +17 -0
- package/src/commands/common.ts +61 -0
- package/src/commands/dry.ts +208 -0
- package/src/commands/etherscan.ts +360 -0
- package/src/commands/index.ts +5 -0
- package/src/commands/list.ts +249 -0
- package/src/commands/run.ts +136 -0
- package/src/commands/utils.ts +52 -0
- package/src/index.ts +67 -0
- package/src/lib/__tests__/deployer-events.spec.ts +338 -0
- package/src/lib/__tests__/deployer.spec.ts +1204 -0
- package/src/lib/__tests__/network-utils.spec.ts +181 -0
- package/src/lib/artifacts/__tests__/fixtures/contract1.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/contract2.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/duplicate-name.json +19 -0
- package/src/lib/artifacts/__tests__/fixtures/nested/nested-contract.json +18 -0
- package/src/lib/artifacts/__tests__/fixtures/not-an-artifact.json +8 -0
- package/src/lib/artifacts/__tests__/fixtures/readme.txt +2 -0
- package/src/lib/contracts/__tests__/repository.spec.ts +344 -0
- package/src/lib/contracts/repository.ts +313 -0
- package/src/lib/core/__tests__/engine.spec.ts +1514 -0
- package/src/lib/core/__tests__/graph.spec.ts +125 -0
- package/src/lib/core/__tests__/json-integration.spec.ts +360 -0
- package/src/lib/core/__tests__/loader.spec.ts +334 -0
- package/src/lib/core/__tests__/multi-platform-verification.spec.ts +406 -0
- package/src/lib/core/__tests__/resolver.spec.ts +1693 -0
- package/src/lib/core/__tests__/static-action.spec.ts +172 -0
- package/src/lib/core/context.ts +127 -0
- package/src/lib/core/engine.ts +1531 -0
- package/src/lib/core/graph.ts +252 -0
- package/src/lib/core/loader.ts +263 -0
- package/src/lib/core/resolver.ts +498 -0
- package/src/lib/deployer.ts +768 -0
- package/src/lib/events/__tests__/event-system.spec.ts +343 -0
- package/src/lib/events/cli-adapter.ts +325 -0
- package/src/lib/events/emitter.ts +62 -0
- package/src/lib/events/index.ts +3 -0
- package/src/lib/events/types.ts +469 -0
- package/src/lib/index.ts +14 -0
- package/src/lib/network-loader.ts +59 -0
- package/src/lib/network-utils.ts +64 -0
- package/src/lib/parsers/__tests__/buildinfo.spec.ts +122 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-bytecode-buildinfo.json +62 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/invalid-json.txt +2 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/multi-contract-buildinfo.json +89 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/no-contracts-buildinfo.json +17 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/simple-buildinfo.json +63 -0
- package/src/lib/parsers/__tests__/fixtures/buildinfo/wrong-format.json +4 -0
- package/src/lib/parsers/__tests__/job.spec.ts +335 -0
- package/src/lib/parsers/__tests__/template.spec.ts +111 -0
- package/src/lib/parsers/artifact/__tests__/artifact.spec.ts +117 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/empty-bytecode.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/hardhat-artifact.json +67 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-bytecode.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/invalid-json.txt +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/minimal-artifact.json +5 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-abi.json +4 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-bytecode.json +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/missing-contract-name.json +11 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/simple-artifact.json +40 -0
- package/src/lib/parsers/artifact/__tests__/fixtures/wrong-types.json +7 -0
- package/src/lib/parsers/artifact/foundry-1.2.ts +72 -0
- package/src/lib/parsers/artifact/index.ts +27 -0
- package/src/lib/parsers/artifact/types.ts +9 -0
- package/src/lib/parsers/buildinfo.ts +127 -0
- package/src/lib/parsers/constants.ts +56 -0
- package/src/lib/parsers/index.ts +5 -0
- package/src/lib/parsers/job.ts +101 -0
- package/src/lib/parsers/template.ts +131 -0
- package/src/lib/std/templates/assured-deployment.yaml +45 -0
- package/src/lib/std/templates/erc-2470.yaml +67 -0
- package/src/lib/std/templates/min-balance.yaml +32 -0
- package/src/lib/std/templates/nano-universal-deployer.yaml +59 -0
- package/src/lib/std/templates/raw-erc-2470.yaml +59 -0
- package/src/lib/std/templates/raw-nano-universal-deployer.yaml +51 -0
- package/src/lib/std/templates/raw-sequence-universal-deployer-2.yaml +48 -0
- package/src/lib/std/templates/sequence-universal-deployer-2.yaml +57 -0
- package/src/lib/types/__tests__/json-request-action.spec.ts +243 -0
- package/src/lib/types/__tests__/read-json-value.spec.ts +264 -0
- package/src/lib/types/actions.ts +127 -0
- package/src/lib/types/artifacts.ts +21 -0
- package/src/lib/types/buildinfo.ts +116 -0
- package/src/lib/types/conditions.ts +50 -0
- package/src/lib/types/contracts.ts +23 -0
- package/src/lib/types/definitions.ts +68 -0
- package/src/lib/types/index.ts +8 -0
- package/src/lib/types/network.ts +22 -0
- package/src/lib/types/project.ts +9 -0
- package/src/lib/types/task.ts +9 -0
- package/src/lib/types/values.ts +116 -0
- package/src/lib/utils/validation.ts +116 -0
- package/src/lib/validation/contract-references.ts +210 -0
- package/src/lib/validation/index.ts +1 -0
- package/src/lib/verification/__tests__/etherscan.spec.ts +710 -0
- package/src/lib/verification/__tests__/sourcify.spec.ts +288 -0
- package/src/lib/verification/etherscan.ts +546 -0
- package/src/lib/verification/sourcify.ts +248 -0
- package/test_validation/artifacts/TestContract.json +9 -0
- package/test_validation/jobs/test-missing.yaml +16 -0
- package/test_validation/networks.yaml +3 -0
- package/tsconfig.json +36 -0
|
@@ -0,0 +1,884 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs/promises"));
|
|
37
|
+
const deployer_1 = require("../deployer");
|
|
38
|
+
const loader_1 = require("../core/loader");
|
|
39
|
+
const graph_1 = require("../core/graph");
|
|
40
|
+
const engine_1 = require("../core/engine");
|
|
41
|
+
const context_1 = require("../core/context");
|
|
42
|
+
jest.mock('fs/promises');
|
|
43
|
+
jest.mock('../core/loader');
|
|
44
|
+
jest.mock('../core/graph');
|
|
45
|
+
jest.mock('../core/engine');
|
|
46
|
+
jest.mock('../core/context');
|
|
47
|
+
const mockFs = fs;
|
|
48
|
+
const MockProjectLoader = loader_1.ProjectLoader;
|
|
49
|
+
const MockDependencyGraph = graph_1.DependencyGraph;
|
|
50
|
+
const MockExecutionEngine = engine_1.ExecutionEngine;
|
|
51
|
+
const MockExecutionContext = context_1.ExecutionContext;
|
|
52
|
+
describe('Deployer', () => {
|
|
53
|
+
let deployerOptions;
|
|
54
|
+
let mockNetwork1;
|
|
55
|
+
let mockNetwork2;
|
|
56
|
+
let mockJob1;
|
|
57
|
+
let mockJob2;
|
|
58
|
+
let mockJob3;
|
|
59
|
+
let deprecatedJob;
|
|
60
|
+
let mockTemplate1;
|
|
61
|
+
let mockLoader;
|
|
62
|
+
let mockGraph;
|
|
63
|
+
let mockEngine;
|
|
64
|
+
let mockContext;
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
jest.clearAllMocks();
|
|
67
|
+
mockNetwork1 = { name: 'mainnet', chainId: 1, rpcUrl: 'https://eth.rpc' };
|
|
68
|
+
mockNetwork2 = { name: 'polygon', chainId: 137, rpcUrl: 'https://polygon.rpc' };
|
|
69
|
+
mockJob1 = {
|
|
70
|
+
name: 'job1',
|
|
71
|
+
version: '1.0.0',
|
|
72
|
+
description: 'First job',
|
|
73
|
+
actions: [
|
|
74
|
+
{ name: 'action1', template: 'template1', arguments: {} }
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
mockJob2 = {
|
|
78
|
+
name: 'job2',
|
|
79
|
+
version: '1.0.0',
|
|
80
|
+
description: 'Second job',
|
|
81
|
+
depends_on: ['job1'],
|
|
82
|
+
actions: [
|
|
83
|
+
{ name: 'action2', template: 'template1', arguments: {} }
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
mockJob3 = {
|
|
87
|
+
name: 'job3',
|
|
88
|
+
version: '1.0.0',
|
|
89
|
+
description: 'Third job with network filters',
|
|
90
|
+
only_networks: [1],
|
|
91
|
+
actions: [
|
|
92
|
+
{ name: 'action3', template: 'template1', arguments: {} }
|
|
93
|
+
]
|
|
94
|
+
};
|
|
95
|
+
deprecatedJob = {
|
|
96
|
+
name: 'legacy-job',
|
|
97
|
+
version: '0.1.0',
|
|
98
|
+
description: 'Deprecated job',
|
|
99
|
+
deprecated: true,
|
|
100
|
+
actions: [
|
|
101
|
+
{ name: 'legacy-action', template: 'template1', arguments: {} }
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
mockTemplate1 = {
|
|
105
|
+
name: 'template1',
|
|
106
|
+
actions: [
|
|
107
|
+
{ type: 'send-transaction', arguments: {} }
|
|
108
|
+
]
|
|
109
|
+
};
|
|
110
|
+
deployerOptions = {
|
|
111
|
+
projectRoot: '/test/project',
|
|
112
|
+
privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
113
|
+
networks: [mockNetwork1, mockNetwork2],
|
|
114
|
+
flatOutput: true
|
|
115
|
+
};
|
|
116
|
+
mockLoader = {
|
|
117
|
+
load: jest.fn(),
|
|
118
|
+
jobs: new Map([
|
|
119
|
+
['job1', mockJob1],
|
|
120
|
+
['job2', mockJob2],
|
|
121
|
+
['job3', mockJob3]
|
|
122
|
+
]),
|
|
123
|
+
templates: new Map([
|
|
124
|
+
['template1', mockTemplate1]
|
|
125
|
+
]),
|
|
126
|
+
contractRepository: {}
|
|
127
|
+
};
|
|
128
|
+
mockGraph = {
|
|
129
|
+
getExecutionOrder: jest.fn().mockReturnValue(['job1', 'job2', 'job3']),
|
|
130
|
+
getDependencies: jest.fn().mockReturnValue(new Set())
|
|
131
|
+
};
|
|
132
|
+
mockEngine = {
|
|
133
|
+
executeJob: jest.fn().mockResolvedValue(undefined)
|
|
134
|
+
};
|
|
135
|
+
mockContext = {
|
|
136
|
+
getOutputs: jest.fn().mockReturnValue(new Map([
|
|
137
|
+
['action1.hash', '0xhash1'],
|
|
138
|
+
['action1.receipt', { status: 1 }]
|
|
139
|
+
])),
|
|
140
|
+
dispose: jest.fn().mockResolvedValue(undefined),
|
|
141
|
+
setOutput: jest.fn(),
|
|
142
|
+
getOutput: jest.fn()
|
|
143
|
+
};
|
|
144
|
+
MockProjectLoader.mockImplementation(() => mockLoader);
|
|
145
|
+
MockDependencyGraph.mockImplementation(() => mockGraph);
|
|
146
|
+
MockExecutionEngine.mockImplementation(() => mockEngine);
|
|
147
|
+
MockExecutionContext.mockImplementation(() => mockContext);
|
|
148
|
+
mockFs.mkdir.mockResolvedValue(undefined);
|
|
149
|
+
mockFs.writeFile.mockResolvedValue(undefined);
|
|
150
|
+
jest.spyOn(console, 'log').mockImplementation();
|
|
151
|
+
jest.spyOn(console, 'error').mockImplementation();
|
|
152
|
+
});
|
|
153
|
+
afterEach(() => {
|
|
154
|
+
jest.clearAllMocks();
|
|
155
|
+
jest.restoreAllMocks();
|
|
156
|
+
});
|
|
157
|
+
describe('constructor', () => {
|
|
158
|
+
it('should create a deployer with valid options', () => {
|
|
159
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
160
|
+
expect(deployer).toBeInstanceOf(deployer_1.Deployer);
|
|
161
|
+
});
|
|
162
|
+
it('should initialize ProjectLoader with correct project root', () => {
|
|
163
|
+
new deployer_1.Deployer(deployerOptions);
|
|
164
|
+
expect(MockProjectLoader).toHaveBeenCalledWith('/test/project', undefined);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe('run', () => {
|
|
168
|
+
describe('happy paths', () => {
|
|
169
|
+
it('should successfully run a simple deployment', async () => {
|
|
170
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
171
|
+
await deployer.run();
|
|
172
|
+
expect(mockLoader.load).toHaveBeenCalledTimes(1);
|
|
173
|
+
expect(MockDependencyGraph).toHaveBeenCalledWith(mockLoader.jobs, mockLoader.templates);
|
|
174
|
+
expect(mockGraph.getExecutionOrder).toHaveBeenCalledTimes(1);
|
|
175
|
+
expect(MockExecutionEngine).toHaveBeenCalledWith(mockLoader.templates, expect.any(Object), expect.any(Object), false);
|
|
176
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(5);
|
|
177
|
+
expect(MockExecutionContext).toHaveBeenCalledTimes(5);
|
|
178
|
+
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/project/output', { recursive: true });
|
|
179
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(3);
|
|
180
|
+
});
|
|
181
|
+
it('should run only specified jobs and their dependencies', async () => {
|
|
182
|
+
mockGraph.getDependencies.mockImplementation((jobName) => {
|
|
183
|
+
if (jobName === 'job2')
|
|
184
|
+
return new Set(['job1']);
|
|
185
|
+
return new Set();
|
|
186
|
+
});
|
|
187
|
+
const options = {
|
|
188
|
+
...deployerOptions,
|
|
189
|
+
runJobs: ['job2']
|
|
190
|
+
};
|
|
191
|
+
const deployer = new deployer_1.Deployer(options);
|
|
192
|
+
await deployer.run();
|
|
193
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(4);
|
|
194
|
+
const executedJobs = mockEngine.executeJob.mock.calls.map(call => call[0].name);
|
|
195
|
+
expect(executedJobs).toContain('job1');
|
|
196
|
+
expect(executedJobs).toContain('job2');
|
|
197
|
+
expect(executedJobs).not.toContain('job3');
|
|
198
|
+
});
|
|
199
|
+
it('should run only on specified networks', async () => {
|
|
200
|
+
const options = {
|
|
201
|
+
...deployerOptions,
|
|
202
|
+
runOnNetworks: [1]
|
|
203
|
+
};
|
|
204
|
+
const deployer = new deployer_1.Deployer(options);
|
|
205
|
+
await deployer.run();
|
|
206
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(3);
|
|
207
|
+
const usedNetworks = MockExecutionContext.mock.calls.map(call => call[0]);
|
|
208
|
+
expect(usedNetworks).toHaveLength(3);
|
|
209
|
+
usedNetworks.forEach(network => {
|
|
210
|
+
expect(network.chainId).toBe(1);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
it('should skip jobs based on network filters', async () => {
|
|
214
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
215
|
+
await deployer.run();
|
|
216
|
+
const job3Calls = mockEngine.executeJob.mock.calls.filter(call => call[0].name === 'job3');
|
|
217
|
+
expect(job3Calls).toHaveLength(1);
|
|
218
|
+
const contextCallsForJob3 = MockExecutionContext.mock.calls.filter((_, index) => {
|
|
219
|
+
const engineCall = mockEngine.executeJob.mock.calls[index];
|
|
220
|
+
return engineCall && engineCall[0].name === 'job3';
|
|
221
|
+
});
|
|
222
|
+
expect(contextCallsForJob3[0][0].chainId).toBe(1);
|
|
223
|
+
});
|
|
224
|
+
it('should handle jobs with skip_networks filter', async () => {
|
|
225
|
+
const jobWithSkipNetworks = {
|
|
226
|
+
...mockJob1,
|
|
227
|
+
name: 'job-skip-polygon',
|
|
228
|
+
skip_networks: [137]
|
|
229
|
+
};
|
|
230
|
+
mockLoader.jobs.set('job-skip-polygon', jobWithSkipNetworks);
|
|
231
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job-skip-polygon']);
|
|
232
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
233
|
+
await deployer.run();
|
|
234
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(1);
|
|
235
|
+
const usedNetwork = MockExecutionContext.mock.calls[0][0];
|
|
236
|
+
expect(usedNetwork.chainId).toBe(1);
|
|
237
|
+
});
|
|
238
|
+
it('should create correct output files in flat mode', async () => {
|
|
239
|
+
const deployer = new deployer_1.Deployer({ ...deployerOptions, flatOutput: true });
|
|
240
|
+
await deployer.run();
|
|
241
|
+
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/project/output', { recursive: true });
|
|
242
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(3);
|
|
243
|
+
const job1OutputCall = mockFs.writeFile.mock.calls.find(call => call[0] === '/test/project/output/job1.json');
|
|
244
|
+
expect(job1OutputCall).toBeDefined();
|
|
245
|
+
const job1Content = JSON.parse(job1OutputCall[1]);
|
|
246
|
+
expect(job1Content).toMatchObject({
|
|
247
|
+
jobName: 'job1',
|
|
248
|
+
jobVersion: '1.0.0',
|
|
249
|
+
lastRun: expect.any(String),
|
|
250
|
+
networks: [
|
|
251
|
+
{
|
|
252
|
+
status: 'success',
|
|
253
|
+
chainIds: expect.arrayContaining(['1', '137']),
|
|
254
|
+
outputs: expect.any(Object)
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
it('should mirror jobs directory structure by default', async () => {
|
|
260
|
+
const job1 = mockLoader.jobs.get('job1');
|
|
261
|
+
const job2 = mockLoader.jobs.get('job2');
|
|
262
|
+
const job3 = mockLoader.jobs.get('job3');
|
|
263
|
+
job1._path = '/test/project/jobs/core/job1.yaml';
|
|
264
|
+
job2._path = '/test/project/jobs/patches/job2.yml';
|
|
265
|
+
job3._path = '/test/project/jobs/job3.yaml';
|
|
266
|
+
const deployer = new deployer_1.Deployer({ ...deployerOptions, flatOutput: undefined });
|
|
267
|
+
await deployer.run();
|
|
268
|
+
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/project/output/core', { recursive: true });
|
|
269
|
+
expect(mockFs.mkdir).toHaveBeenCalledWith('/test/project/output/patches', { recursive: true });
|
|
270
|
+
const job1OutputCall = mockFs.writeFile.mock.calls.find(call => call[0] === '/test/project/output/core/job1.json');
|
|
271
|
+
expect(job1OutputCall).toBeDefined();
|
|
272
|
+
const job2OutputCall = mockFs.writeFile.mock.calls.find(call => call[0] === '/test/project/output/patches/job2.json');
|
|
273
|
+
expect(job2OutputCall).toBeDefined();
|
|
274
|
+
const job3OutputCall = mockFs.writeFile.mock.calls.find(call => call[0] === '/test/project/output/job3.json');
|
|
275
|
+
expect(job3OutputCall).toBeDefined();
|
|
276
|
+
});
|
|
277
|
+
it('should handle empty project gracefully', async () => {
|
|
278
|
+
mockLoader.jobs.clear();
|
|
279
|
+
mockLoader.templates.clear();
|
|
280
|
+
mockGraph.getExecutionOrder.mockReturnValue([]);
|
|
281
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
282
|
+
await deployer.run();
|
|
283
|
+
expect(mockEngine.executeJob).not.toHaveBeenCalled();
|
|
284
|
+
expect(mockFs.writeFile).not.toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
it('should filter outputs based on action output flags', async () => {
|
|
287
|
+
const jobWithOutputFlags = {
|
|
288
|
+
name: 'job-with-output-flags',
|
|
289
|
+
version: '1.0.0',
|
|
290
|
+
description: 'Job with output filtering',
|
|
291
|
+
actions: [
|
|
292
|
+
{ name: 'deploy-action', template: 'template1', arguments: {}, output: true },
|
|
293
|
+
{ name: 'verify-action', template: 'template1', arguments: {}, output: false },
|
|
294
|
+
{ name: 'other-action', template: 'template1', arguments: {} }
|
|
295
|
+
]
|
|
296
|
+
};
|
|
297
|
+
mockLoader.jobs.clear();
|
|
298
|
+
mockLoader.jobs.set('job-with-output-flags', jobWithOutputFlags);
|
|
299
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job-with-output-flags']);
|
|
300
|
+
mockContext.getOutputs.mockReturnValue(new Map([
|
|
301
|
+
['deploy-action.address', '0xdeployaddress'],
|
|
302
|
+
['deploy-action.hash', '0xdeployhash'],
|
|
303
|
+
['verify-action.guid', 'verification-guid'],
|
|
304
|
+
['other-action.result', 'some-result']
|
|
305
|
+
]));
|
|
306
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
307
|
+
await deployer.run();
|
|
308
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
|
|
309
|
+
const outputCall = mockFs.writeFile.mock.calls[0];
|
|
310
|
+
expect(outputCall[0]).toBe('/test/project/output/job-with-output-flags.json');
|
|
311
|
+
const outputContent = JSON.parse(outputCall[1]);
|
|
312
|
+
expect(outputContent.networks).toHaveLength(1);
|
|
313
|
+
expect(outputContent.networks[0].status).toBe('success');
|
|
314
|
+
expect(outputContent.networks[0].outputs).toEqual({
|
|
315
|
+
'deploy-action.address': '0xdeployaddress',
|
|
316
|
+
'deploy-action.hash': '0xdeployhash'
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
it('should include all outputs when no actions have output: true (backward compatibility)', async () => {
|
|
320
|
+
const jobWithoutOutputFlags = {
|
|
321
|
+
name: 'job-without-output-flags',
|
|
322
|
+
version: '1.0.0',
|
|
323
|
+
description: 'Job without output flags',
|
|
324
|
+
actions: [
|
|
325
|
+
{ name: 'action1', template: 'template1', arguments: {} },
|
|
326
|
+
{ name: 'action2', template: 'template1', arguments: {}, output: false }
|
|
327
|
+
]
|
|
328
|
+
};
|
|
329
|
+
mockLoader.jobs.clear();
|
|
330
|
+
mockLoader.jobs.set('job-without-output-flags', jobWithoutOutputFlags);
|
|
331
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job-without-output-flags']);
|
|
332
|
+
mockContext.getOutputs.mockReturnValue(new Map([
|
|
333
|
+
['action1.result', 'result1'],
|
|
334
|
+
['action2.result', 'result2']
|
|
335
|
+
]));
|
|
336
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
337
|
+
await deployer.run();
|
|
338
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(1);
|
|
339
|
+
const outputCall = mockFs.writeFile.mock.calls[0];
|
|
340
|
+
const outputContent = JSON.parse(outputCall[1]);
|
|
341
|
+
expect(outputContent.networks[0].outputs).toEqual({
|
|
342
|
+
'action1.result': 'result1',
|
|
343
|
+
'action2.result': 'result2'
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
it('should filter outputs correctly when multiple actions have output: true', async () => {
|
|
347
|
+
const jobWithMultipleOutputs = {
|
|
348
|
+
name: 'job-multiple-outputs',
|
|
349
|
+
version: '1.0.0',
|
|
350
|
+
description: 'Job with multiple output actions',
|
|
351
|
+
actions: [
|
|
352
|
+
{ name: 'deploy1', template: 'template1', arguments: {}, output: true },
|
|
353
|
+
{ name: 'deploy2', template: 'template1', arguments: {}, output: true },
|
|
354
|
+
{ name: 'verify1', template: 'template1', arguments: {}, output: false },
|
|
355
|
+
{ name: 'verify2', template: 'template1', arguments: {}, output: false }
|
|
356
|
+
]
|
|
357
|
+
};
|
|
358
|
+
mockLoader.jobs.clear();
|
|
359
|
+
mockLoader.jobs.set('job-multiple-outputs', jobWithMultipleOutputs);
|
|
360
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job-multiple-outputs']);
|
|
361
|
+
mockContext.getOutputs.mockReturnValue(new Map([
|
|
362
|
+
['deploy1.address', '0xdeploy1'],
|
|
363
|
+
['deploy2.address', '0xdeploy2'],
|
|
364
|
+
['verify1.guid', 'verify1-guid'],
|
|
365
|
+
['verify2.guid', 'verify2-guid']
|
|
366
|
+
]));
|
|
367
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
368
|
+
await deployer.run();
|
|
369
|
+
const outputCall = mockFs.writeFile.mock.calls[0];
|
|
370
|
+
const outputContent = JSON.parse(outputCall[1]);
|
|
371
|
+
expect(outputContent.networks[0].outputs).toEqual({
|
|
372
|
+
'deploy1.address': '0xdeploy1',
|
|
373
|
+
'deploy2.address': '0xdeploy2'
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
describe('error handling', () => {
|
|
378
|
+
it('should throw when project loading fails', async () => {
|
|
379
|
+
mockLoader.load.mockRejectedValue(new Error('Failed to load project'));
|
|
380
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
381
|
+
await expect(deployer.run()).rejects.toThrow('Failed to load project');
|
|
382
|
+
});
|
|
383
|
+
it('should throw when dependency graph creation fails', async () => {
|
|
384
|
+
MockDependencyGraph.mockImplementation(() => {
|
|
385
|
+
throw new Error('Circular dependency detected');
|
|
386
|
+
});
|
|
387
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
388
|
+
await expect(deployer.run()).rejects.toThrow('Circular dependency detected');
|
|
389
|
+
});
|
|
390
|
+
it('should capture job execution failures and then throw', async () => {
|
|
391
|
+
mockEngine.executeJob.mockRejectedValue(new Error('Transaction failed'));
|
|
392
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
393
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
394
|
+
expect(mockFs.writeFile).toHaveBeenCalled();
|
|
395
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
396
|
+
const outputFile = writeFileCalls[0];
|
|
397
|
+
const outputContent = JSON.parse(outputFile[1]);
|
|
398
|
+
const errorEntries = outputContent.networks.filter((entry) => entry.status === 'error');
|
|
399
|
+
expect(errorEntries.length).toBeGreaterThan(0);
|
|
400
|
+
expect(errorEntries[0].error).toBe('Transaction failed');
|
|
401
|
+
});
|
|
402
|
+
it('should throw when output directory creation fails', async () => {
|
|
403
|
+
mockFs.mkdir.mockRejectedValue(new Error('Permission denied'));
|
|
404
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
405
|
+
await expect(deployer.run()).rejects.toThrow('Permission denied');
|
|
406
|
+
});
|
|
407
|
+
it('should throw when output file writing fails', async () => {
|
|
408
|
+
mockFs.writeFile.mockRejectedValue(new Error('Disk full'));
|
|
409
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
410
|
+
await expect(deployer.run()).rejects.toThrow('Disk full');
|
|
411
|
+
});
|
|
412
|
+
it('should handle execution context creation failure and then throw', async () => {
|
|
413
|
+
MockExecutionContext.mockImplementation(() => {
|
|
414
|
+
throw new Error('Invalid private key');
|
|
415
|
+
});
|
|
416
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
417
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
418
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
419
|
+
const outputFile = writeFileCalls[0];
|
|
420
|
+
const outputContent = JSON.parse(outputFile[1]);
|
|
421
|
+
const errorEntries = outputContent.networks.filter((entry) => entry.status === 'error');
|
|
422
|
+
expect(errorEntries.length).toBeGreaterThan(0);
|
|
423
|
+
expect(errorEntries[0].error).toBe('Invalid private key');
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
describe('edge cases and weird scenarios', () => {
|
|
427
|
+
it('should handle job with only_networks that includes non-existent network', async () => {
|
|
428
|
+
const weirdJob = {
|
|
429
|
+
...mockJob1,
|
|
430
|
+
name: 'weird-job',
|
|
431
|
+
only_networks: [999]
|
|
432
|
+
};
|
|
433
|
+
mockLoader.jobs.clear();
|
|
434
|
+
mockLoader.jobs.set('weird-job', weirdJob);
|
|
435
|
+
mockGraph.getExecutionOrder.mockReturnValue(['weird-job']);
|
|
436
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
437
|
+
await deployer.run();
|
|
438
|
+
expect(mockEngine.executeJob).not.toHaveBeenCalled();
|
|
439
|
+
});
|
|
440
|
+
it('should handle job with skip_networks that includes all networks', async () => {
|
|
441
|
+
const weirdJob = {
|
|
442
|
+
...mockJob1,
|
|
443
|
+
name: 'weird-job',
|
|
444
|
+
skip_networks: [1, 137]
|
|
445
|
+
};
|
|
446
|
+
mockLoader.jobs.clear();
|
|
447
|
+
mockLoader.jobs.set('weird-job', weirdJob);
|
|
448
|
+
mockGraph.getExecutionOrder.mockReturnValue(['weird-job']);
|
|
449
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
450
|
+
await deployer.run();
|
|
451
|
+
expect(mockEngine.executeJob).not.toHaveBeenCalled();
|
|
452
|
+
});
|
|
453
|
+
it('should handle runOnNetworks with non-existent chain IDs', async () => {
|
|
454
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
455
|
+
const options = {
|
|
456
|
+
...deployerOptions,
|
|
457
|
+
runOnNetworks: [1, 999, 888]
|
|
458
|
+
};
|
|
459
|
+
const deployer = new deployer_1.Deployer(options);
|
|
460
|
+
await deployer.run();
|
|
461
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(3);
|
|
462
|
+
});
|
|
463
|
+
it('should handle runJobs with non-existent job names', async () => {
|
|
464
|
+
const options = {
|
|
465
|
+
...deployerOptions,
|
|
466
|
+
runJobs: ['non-existent-job']
|
|
467
|
+
};
|
|
468
|
+
const deployer = new deployer_1.Deployer(options);
|
|
469
|
+
await expect(deployer.run()).rejects.toThrow('Specified job "non-existent-job" not found in project.');
|
|
470
|
+
});
|
|
471
|
+
it('should handle execution context without getOutputs method and then throw', async () => {
|
|
472
|
+
const brokenContext = {};
|
|
473
|
+
MockExecutionContext.mockImplementation(() => brokenContext);
|
|
474
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
475
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
476
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
477
|
+
const outputFile = writeFileCalls[0];
|
|
478
|
+
const outputContent = JSON.parse(outputFile[1]);
|
|
479
|
+
const errorEntries = outputContent.networks.filter((entry) => entry.status === 'error');
|
|
480
|
+
expect(errorEntries.length).toBeGreaterThan(0);
|
|
481
|
+
});
|
|
482
|
+
it('should handle empty networks array', async () => {
|
|
483
|
+
const options = {
|
|
484
|
+
...deployerOptions,
|
|
485
|
+
networks: []
|
|
486
|
+
};
|
|
487
|
+
const deployer = new deployer_1.Deployer(options);
|
|
488
|
+
await deployer.run();
|
|
489
|
+
expect(mockEngine.executeJob).not.toHaveBeenCalled();
|
|
490
|
+
expect(mockFs.writeFile).not.toHaveBeenCalled();
|
|
491
|
+
});
|
|
492
|
+
it('should handle empty runJobs array', async () => {
|
|
493
|
+
const options = {
|
|
494
|
+
...deployerOptions,
|
|
495
|
+
runJobs: []
|
|
496
|
+
};
|
|
497
|
+
const deployer = new deployer_1.Deployer(options);
|
|
498
|
+
await deployer.run();
|
|
499
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(5);
|
|
500
|
+
});
|
|
501
|
+
it('should handle empty runOnNetworks array', async () => {
|
|
502
|
+
const options = {
|
|
503
|
+
...deployerOptions,
|
|
504
|
+
runOnNetworks: []
|
|
505
|
+
};
|
|
506
|
+
const deployer = new deployer_1.Deployer(options);
|
|
507
|
+
await deployer.run();
|
|
508
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(5);
|
|
509
|
+
});
|
|
510
|
+
it('should handle job with both only_networks and skip_networks', async () => {
|
|
511
|
+
const conflictedJob = {
|
|
512
|
+
...mockJob1,
|
|
513
|
+
name: 'conflicted-job',
|
|
514
|
+
only_networks: [1, 137],
|
|
515
|
+
skip_networks: [137]
|
|
516
|
+
};
|
|
517
|
+
mockLoader.jobs.clear();
|
|
518
|
+
mockLoader.jobs.set('conflicted-job', conflictedJob);
|
|
519
|
+
mockGraph.getExecutionOrder.mockReturnValue(['conflicted-job']);
|
|
520
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
521
|
+
await deployer.run();
|
|
522
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(2);
|
|
523
|
+
const usedNetworks = MockExecutionContext.mock.calls.map(call => call[0].chainId);
|
|
524
|
+
expect(usedNetworks).toEqual(expect.arrayContaining([1, 137]));
|
|
525
|
+
});
|
|
526
|
+
it('should write output files even when all executions fail and then throw', async () => {
|
|
527
|
+
mockEngine.executeJob.mockImplementation(() => {
|
|
528
|
+
throw new Error('Execution failed');
|
|
529
|
+
});
|
|
530
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
531
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
532
|
+
expect(mockFs.writeFile).toHaveBeenCalled();
|
|
533
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
534
|
+
const outputFile = writeFileCalls[0];
|
|
535
|
+
const outputContent = JSON.parse(outputFile[1]);
|
|
536
|
+
const errorEntries = outputContent.networks.filter((entry) => entry.status === 'error');
|
|
537
|
+
expect(errorEntries.length).toBeGreaterThan(0);
|
|
538
|
+
const successEntries = outputContent.networks.filter((entry) => entry.status === 'success');
|
|
539
|
+
expect(successEntries.length).toBe(0);
|
|
540
|
+
});
|
|
541
|
+
it('should handle very long execution order', async () => {
|
|
542
|
+
const manyJobs = Array.from({ length: 100 }, (_, i) => `job${i}`);
|
|
543
|
+
mockGraph.getExecutionOrder.mockReturnValue(manyJobs);
|
|
544
|
+
for (let i = 0; i < 100; i++) {
|
|
545
|
+
mockLoader.jobs.set(`job${i}`, {
|
|
546
|
+
...mockJob1,
|
|
547
|
+
name: `job${i}`
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
551
|
+
await deployer.run();
|
|
552
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(200);
|
|
553
|
+
expect(mockFs.writeFile).toHaveBeenCalledTimes(100);
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
describe('private method testing', () => {
|
|
557
|
+
let deployer;
|
|
558
|
+
beforeEach(() => {
|
|
559
|
+
deployer = new deployer_1.Deployer(deployerOptions);
|
|
560
|
+
});
|
|
561
|
+
describe('getJobExecutionPlan', () => {
|
|
562
|
+
it('should return full order when no runJobs specified', () => {
|
|
563
|
+
const fullOrder = ['job1', 'job2', 'job3'];
|
|
564
|
+
const plan = deployer.getJobExecutionPlan(fullOrder);
|
|
565
|
+
expect(plan).toEqual(fullOrder);
|
|
566
|
+
});
|
|
567
|
+
it('should filter and include dependencies', async () => {
|
|
568
|
+
const options = {
|
|
569
|
+
...deployerOptions,
|
|
570
|
+
runJobs: ['job2']
|
|
571
|
+
};
|
|
572
|
+
const deployer = new deployer_1.Deployer(options);
|
|
573
|
+
await mockLoader.load();
|
|
574
|
+
deployer.graph = mockGraph;
|
|
575
|
+
mockGraph.getDependencies.mockReturnValueOnce(new Set(['job1']));
|
|
576
|
+
const fullOrder = ['job1', 'job2', 'job3'];
|
|
577
|
+
const plan = deployer.getJobExecutionPlan(fullOrder);
|
|
578
|
+
expect(plan).toEqual(['job1', 'job2']);
|
|
579
|
+
});
|
|
580
|
+
it('should include deprecated dependencies when no runJobs specified', () => {
|
|
581
|
+
;
|
|
582
|
+
mockLoader.jobs.set('legacy-job', deprecatedJob);
|
|
583
|
+
const fullOrder = ['legacy-job', 'job1', 'job2', 'job3'];
|
|
584
|
+
mockGraph.getDependencies.mockImplementation((jobName) => {
|
|
585
|
+
if (jobName === 'job2')
|
|
586
|
+
return new Set(['job1', 'legacy-job']);
|
|
587
|
+
return new Set();
|
|
588
|
+
});
|
|
589
|
+
deployer.graph = mockGraph;
|
|
590
|
+
const plan = deployer.getJobExecutionPlan(fullOrder);
|
|
591
|
+
expect(plan).toEqual(['legacy-job', 'job1', 'job2', 'job3']);
|
|
592
|
+
});
|
|
593
|
+
it('should keep deprecated dependencies when specific jobs are requested', async () => {
|
|
594
|
+
;
|
|
595
|
+
mockLoader.jobs.set('legacy-job', deprecatedJob);
|
|
596
|
+
const options = {
|
|
597
|
+
...deployerOptions,
|
|
598
|
+
runJobs: ['job2']
|
|
599
|
+
};
|
|
600
|
+
const depDeployer = new deployer_1.Deployer(options);
|
|
601
|
+
depDeployer.graph = mockGraph;
|
|
602
|
+
mockGraph.getDependencies.mockImplementation((jobName) => {
|
|
603
|
+
if (jobName === 'job2')
|
|
604
|
+
return new Set(['job1', 'legacy-job']);
|
|
605
|
+
return new Set();
|
|
606
|
+
});
|
|
607
|
+
const fullOrder = ['legacy-job', 'job1', 'job2', 'job3'];
|
|
608
|
+
const plan = depDeployer.getJobExecutionPlan(fullOrder);
|
|
609
|
+
expect(plan).toEqual(['legacy-job', 'job1', 'job2']);
|
|
610
|
+
});
|
|
611
|
+
it('should expand wildcard patterns in runJobs and preserve execution order', async () => {
|
|
612
|
+
;
|
|
613
|
+
mockLoader.jobs.set('job10', { ...mockJob1, name: 'job10' });
|
|
614
|
+
mockLoader.jobs.set('another', { ...mockJob1, name: 'another' });
|
|
615
|
+
const fullOrder = ['another', 'job1', 'job2', 'job3', 'job10'];
|
|
616
|
+
mockGraph.getExecutionOrder.mockReturnValue(fullOrder);
|
|
617
|
+
const options = {
|
|
618
|
+
...deployerOptions,
|
|
619
|
+
runJobs: ['job*']
|
|
620
|
+
};
|
|
621
|
+
const dep = new deployer_1.Deployer(options);
|
|
622
|
+
dep.loader = mockLoader;
|
|
623
|
+
dep.graph = mockGraph;
|
|
624
|
+
const plan = dep.getJobExecutionPlan(fullOrder);
|
|
625
|
+
expect(plan).toEqual(['job1', 'job2', 'job3', 'job10']);
|
|
626
|
+
});
|
|
627
|
+
it('should support mixed exact names and patterns', async () => {
|
|
628
|
+
const fullOrder = ['job1', 'job2', 'job3'];
|
|
629
|
+
mockGraph.getExecutionOrder.mockReturnValue(fullOrder);
|
|
630
|
+
const options = {
|
|
631
|
+
...deployerOptions,
|
|
632
|
+
runJobs: ['job1', 'job?']
|
|
633
|
+
};
|
|
634
|
+
const dep = new deployer_1.Deployer(options);
|
|
635
|
+
dep.loader = mockLoader;
|
|
636
|
+
dep.graph = mockGraph;
|
|
637
|
+
const plan = dep.getJobExecutionPlan(fullOrder);
|
|
638
|
+
expect(plan).toEqual(['job1', 'job2', 'job3']);
|
|
639
|
+
});
|
|
640
|
+
it('should throw when a pattern matches no jobs', async () => {
|
|
641
|
+
const fullOrder = ['job1', 'job2', 'job3'];
|
|
642
|
+
mockGraph.getExecutionOrder.mockReturnValue(fullOrder);
|
|
643
|
+
const options = {
|
|
644
|
+
...deployerOptions,
|
|
645
|
+
runJobs: ['does-not-exist*']
|
|
646
|
+
};
|
|
647
|
+
const dep = new deployer_1.Deployer(options);
|
|
648
|
+
dep.loader = mockLoader;
|
|
649
|
+
dep.graph = mockGraph;
|
|
650
|
+
expect(() => dep.getJobExecutionPlan(fullOrder)).toThrow('Job pattern "does-not-exist*" did not match any jobs in project.');
|
|
651
|
+
});
|
|
652
|
+
it('should match names containing slashes with patterns', async () => {
|
|
653
|
+
const jA = { ...mockJob1, name: 'sequence_v3/beta_4' };
|
|
654
|
+
const jB = { ...mockJob1, name: 'sequence_v3/rc_1' };
|
|
655
|
+
mockLoader.jobs.set(jA.name, jA);
|
|
656
|
+
mockLoader.jobs.set(jB.name, jB);
|
|
657
|
+
const fullOrder = ['job1', jA.name, jB.name, 'job2'];
|
|
658
|
+
mockGraph.getExecutionOrder.mockReturnValue(fullOrder);
|
|
659
|
+
const options = {
|
|
660
|
+
...deployerOptions,
|
|
661
|
+
runJobs: ['sequence_v3/*']
|
|
662
|
+
};
|
|
663
|
+
const dep = new deployer_1.Deployer(options);
|
|
664
|
+
dep.loader = mockLoader;
|
|
665
|
+
dep.graph = mockGraph;
|
|
666
|
+
const plan = dep.getJobExecutionPlan(fullOrder);
|
|
667
|
+
expect(plan).toEqual(['sequence_v3/beta_4', 'sequence_v3/rc_1']);
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
describe('getTargetNetworks', () => {
|
|
671
|
+
it('should return all networks when no runOnNetworks specified', () => {
|
|
672
|
+
const networks = deployer.getTargetNetworks();
|
|
673
|
+
expect(networks).toEqual([mockNetwork1, mockNetwork2]);
|
|
674
|
+
});
|
|
675
|
+
it('should filter networks by chain ID', () => {
|
|
676
|
+
const options = {
|
|
677
|
+
...deployerOptions,
|
|
678
|
+
runOnNetworks: [1]
|
|
679
|
+
};
|
|
680
|
+
const deployer = new deployer_1.Deployer(options);
|
|
681
|
+
const networks = deployer.getTargetNetworks();
|
|
682
|
+
expect(networks).toEqual([mockNetwork1]);
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
describe('shouldSkipJobOnNetwork', () => {
|
|
686
|
+
it('should return false for job with no network filters', () => {
|
|
687
|
+
const result = deployer.shouldSkipJobOnNetwork(mockJob1, mockNetwork1);
|
|
688
|
+
expect(result).toBe(false);
|
|
689
|
+
});
|
|
690
|
+
it('should return true when network not in only_networks', () => {
|
|
691
|
+
const result = deployer.shouldSkipJobOnNetwork(mockJob3, mockNetwork2);
|
|
692
|
+
expect(result).toBe(true);
|
|
693
|
+
});
|
|
694
|
+
it('should return false when network is in only_networks', () => {
|
|
695
|
+
const result = deployer.shouldSkipJobOnNetwork(mockJob3, mockNetwork1);
|
|
696
|
+
expect(result).toBe(false);
|
|
697
|
+
});
|
|
698
|
+
it('should return true when network is in skip_networks', () => {
|
|
699
|
+
const jobWithSkip = {
|
|
700
|
+
...mockJob1,
|
|
701
|
+
skip_networks: [1]
|
|
702
|
+
};
|
|
703
|
+
const result = deployer.shouldSkipJobOnNetwork(jobWithSkip, mockNetwork1);
|
|
704
|
+
expect(result).toBe(true);
|
|
705
|
+
});
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
describe('integration-like scenarios', () => {
|
|
709
|
+
it('should handle complex dependency chain with network filtering', async () => {
|
|
710
|
+
const job4 = {
|
|
711
|
+
name: 'job4',
|
|
712
|
+
version: '1.0.0',
|
|
713
|
+
depends_on: ['job3'],
|
|
714
|
+
skip_networks: [137],
|
|
715
|
+
actions: [{ name: 'action4', template: 'template1', arguments: {} }]
|
|
716
|
+
};
|
|
717
|
+
mockLoader.jobs.set('job4', job4);
|
|
718
|
+
mockGraph.getExecutionOrder.mockReturnValue(['job1', 'job2', 'job3', 'job4']);
|
|
719
|
+
mockGraph.getDependencies
|
|
720
|
+
.mockReturnValueOnce(new Set())
|
|
721
|
+
.mockReturnValueOnce(new Set(['job1']))
|
|
722
|
+
.mockReturnValueOnce(new Set(['job1', 'job2']))
|
|
723
|
+
.mockReturnValueOnce(new Set(['job1', 'job2', 'job3']));
|
|
724
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
725
|
+
await deployer.run();
|
|
726
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(6);
|
|
727
|
+
const contextCalls = MockExecutionContext.mock.calls;
|
|
728
|
+
const mainnetCalls = contextCalls.filter(call => call[0].chainId === 1);
|
|
729
|
+
const polygonCalls = contextCalls.filter(call => call[0].chainId === 137);
|
|
730
|
+
expect(mainnetCalls).toHaveLength(4);
|
|
731
|
+
expect(polygonCalls).toHaveLength(2);
|
|
732
|
+
});
|
|
733
|
+
it('should handle partial failure scenario', async () => {
|
|
734
|
+
let callCount = 0;
|
|
735
|
+
mockEngine.executeJob.mockImplementation((job, context) => {
|
|
736
|
+
const currentCall = MockExecutionContext.mock.calls[callCount];
|
|
737
|
+
const network = currentCall ? currentCall[0] : null;
|
|
738
|
+
callCount++;
|
|
739
|
+
if (job.name === 'job2' && network && network.chainId === 137) {
|
|
740
|
+
throw new Error('Polygon execution failed');
|
|
741
|
+
}
|
|
742
|
+
return Promise.resolve();
|
|
743
|
+
});
|
|
744
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
745
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
746
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
747
|
+
const job2Output = writeFileCalls.find(call => String(call[0]).includes('job2.json'));
|
|
748
|
+
if (job2Output) {
|
|
749
|
+
const job2Content = JSON.parse(job2Output[1]);
|
|
750
|
+
const errorEntries = job2Content.networks.filter((entry) => entry.status === 'error');
|
|
751
|
+
expect(errorEntries.some((entry) => entry.chainId === '137' && entry.error === 'Polygon execution failed')).toBe(true);
|
|
752
|
+
}
|
|
753
|
+
});
|
|
754
|
+
it('should handle context output aggregation correctly', async () => {
|
|
755
|
+
MockExecutionContext.mockImplementation((network) => ({
|
|
756
|
+
network,
|
|
757
|
+
getOutputs: jest.fn().mockReturnValue(new Map([
|
|
758
|
+
[`action.hash`, `0xhash-${network.chainId}`],
|
|
759
|
+
[`action.receipt`, { status: 1, blockNumber: network.chainId * 100 }]
|
|
760
|
+
])),
|
|
761
|
+
dispose: jest.fn().mockResolvedValue(undefined),
|
|
762
|
+
setOutput: jest.fn(),
|
|
763
|
+
getOutput: jest.fn()
|
|
764
|
+
}));
|
|
765
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
766
|
+
await deployer.run();
|
|
767
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
768
|
+
const job1Output = writeFileCalls.find(call => call[0] === '/test/project/output/job1.json');
|
|
769
|
+
const job1Content = JSON.parse(job1Output[1]);
|
|
770
|
+
expect(job1Content.networks).toHaveLength(2);
|
|
771
|
+
const network1Entry = job1Content.networks.find((entry) => entry.chainIds && entry.chainIds.includes('1'));
|
|
772
|
+
const network137Entry = job1Content.networks.find((entry) => entry.chainIds && entry.chainIds.includes('137'));
|
|
773
|
+
expect(network1Entry.outputs['action.hash']).toBe('0xhash-1');
|
|
774
|
+
expect(network137Entry.outputs['action.hash']).toBe('0xhash-137');
|
|
775
|
+
});
|
|
776
|
+
it('should group networks with identical outputs together', async () => {
|
|
777
|
+
MockExecutionContext.mockImplementation(() => ({
|
|
778
|
+
getOutputs: jest.fn().mockReturnValue(new Map([
|
|
779
|
+
[`contract.address`, `0x1234567890123456789012345678901234567890`],
|
|
780
|
+
[`contract.txHash`, `0xabcdef1234567890abcdef1234567890abcdef12`]
|
|
781
|
+
])),
|
|
782
|
+
dispose: jest.fn().mockResolvedValue(undefined),
|
|
783
|
+
setOutput: jest.fn(),
|
|
784
|
+
getOutput: jest.fn()
|
|
785
|
+
}));
|
|
786
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
787
|
+
await deployer.run();
|
|
788
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
789
|
+
const job1Output = writeFileCalls.find(call => call[0] === '/test/project/output/job1.json');
|
|
790
|
+
const job1Content = JSON.parse(job1Output[1]);
|
|
791
|
+
expect(job1Content.networks).toHaveLength(1);
|
|
792
|
+
expect(job1Content.networks[0].status).toBe('success');
|
|
793
|
+
expect(job1Content.networks[0].chainIds).toEqual(['1', '137']);
|
|
794
|
+
expect(job1Content.networks[0].outputs['contract.address']).toBe('0x1234567890123456789012345678901234567890');
|
|
795
|
+
});
|
|
796
|
+
it('should handle partial failure scenario with proper grouping', async () => {
|
|
797
|
+
let callCount = 0;
|
|
798
|
+
mockEngine.executeJob.mockImplementation((job, context) => {
|
|
799
|
+
const currentCall = MockExecutionContext.mock.calls[callCount];
|
|
800
|
+
const network = currentCall ? currentCall[0] : null;
|
|
801
|
+
callCount++;
|
|
802
|
+
if (job.name === 'job1' && network && network.chainId === 137) {
|
|
803
|
+
throw new Error('Polygon execution failed');
|
|
804
|
+
}
|
|
805
|
+
return Promise.resolve();
|
|
806
|
+
});
|
|
807
|
+
MockExecutionContext.mockImplementation((network) => ({
|
|
808
|
+
network,
|
|
809
|
+
getOutputs: jest.fn().mockReturnValue(new Map([
|
|
810
|
+
[`contract.address`, `0x1234567890123456789012345678901234567890`]
|
|
811
|
+
])),
|
|
812
|
+
dispose: jest.fn().mockResolvedValue(undefined),
|
|
813
|
+
setOutput: jest.fn(),
|
|
814
|
+
getOutput: jest.fn()
|
|
815
|
+
}));
|
|
816
|
+
const deployer = new deployer_1.Deployer(deployerOptions);
|
|
817
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
818
|
+
const writeFileCalls = mockFs.writeFile.mock.calls;
|
|
819
|
+
const job1Output = writeFileCalls.find(call => call[0] === '/test/project/output/job1.json');
|
|
820
|
+
const job1Content = JSON.parse(job1Output[1]);
|
|
821
|
+
expect(job1Content.networks).toHaveLength(2);
|
|
822
|
+
const successEntry = job1Content.networks.find((entry) => entry.status === 'success');
|
|
823
|
+
const errorEntry = job1Content.networks.find((entry) => entry.status === 'error');
|
|
824
|
+
expect(successEntry).toBeDefined();
|
|
825
|
+
expect(successEntry.chainIds).toEqual(['1']);
|
|
826
|
+
expect(successEntry.outputs['contract.address']).toBe('0x1234567890123456789012345678901234567890');
|
|
827
|
+
expect(errorEntry).toBeDefined();
|
|
828
|
+
expect(errorEntry.chainId).toBe('137');
|
|
829
|
+
expect(errorEntry.error).toBe('Polygon execution failed');
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
describe('fail-early functionality', () => {
|
|
834
|
+
beforeEach(() => {
|
|
835
|
+
mockEngine.executeJob.mockClear();
|
|
836
|
+
});
|
|
837
|
+
it('should stop execution immediately when failEarly is true', async () => {
|
|
838
|
+
const options = {
|
|
839
|
+
...deployerOptions,
|
|
840
|
+
runJobs: ['job1'],
|
|
841
|
+
failEarly: true
|
|
842
|
+
};
|
|
843
|
+
mockEngine.executeJob.mockRejectedValueOnce(new Error('First job failed'));
|
|
844
|
+
const deployer = new deployer_1.Deployer(options);
|
|
845
|
+
await expect(deployer.run()).rejects.toThrow('First job failed');
|
|
846
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(1);
|
|
847
|
+
});
|
|
848
|
+
it('should continue through all jobs/networks when failEarly is false', async () => {
|
|
849
|
+
const options = {
|
|
850
|
+
...deployerOptions,
|
|
851
|
+
runJobs: ['job1'],
|
|
852
|
+
failEarly: false
|
|
853
|
+
};
|
|
854
|
+
mockEngine.executeJob.mockRejectedValueOnce(new Error('First job failed'));
|
|
855
|
+
mockEngine.executeJob.mockResolvedValue(undefined);
|
|
856
|
+
const deployer = new deployer_1.Deployer(options);
|
|
857
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
858
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(2);
|
|
859
|
+
});
|
|
860
|
+
it('should default to failEarly: false when option is not provided', async () => {
|
|
861
|
+
const options = {
|
|
862
|
+
...deployerOptions,
|
|
863
|
+
runJobs: ['job1']
|
|
864
|
+
};
|
|
865
|
+
mockEngine.executeJob.mockRejectedValueOnce(new Error('First job failed'));
|
|
866
|
+
mockEngine.executeJob.mockResolvedValue(undefined);
|
|
867
|
+
const deployer = new deployer_1.Deployer(options);
|
|
868
|
+
await expect(deployer.run()).rejects.toThrow('One or more jobs failed during execution');
|
|
869
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(2);
|
|
870
|
+
});
|
|
871
|
+
it('should not throw when all jobs succeed, regardless of failEarly setting', async () => {
|
|
872
|
+
const options = {
|
|
873
|
+
...deployerOptions,
|
|
874
|
+
runJobs: ['job1'],
|
|
875
|
+
failEarly: true
|
|
876
|
+
};
|
|
877
|
+
mockEngine.executeJob.mockResolvedValue(undefined);
|
|
878
|
+
const deployer = new deployer_1.Deployer(options);
|
|
879
|
+
await expect(deployer.run()).resolves.not.toThrow();
|
|
880
|
+
expect(mockEngine.executeJob).toHaveBeenCalledTimes(2);
|
|
881
|
+
});
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
//# sourceMappingURL=deployer.spec.js.map
|