UNPKG

@tevm/base-bundler

Version:

Internal bundler for Tevm

314 lines (276 loc) 8.07 kB
import { tmpdir } from 'node:os' import { createCache } from '@tevm/bundler-cache' import { resolveArtifacts } from '@tevm/compiler' import { describe, expect, it, type Mock, vi } from 'vitest' import { bundler } from './bundler.js' // Mock @tevm/compiler vi.mock('@tevm/compiler', () => ({ resolveArtifacts: vi.fn(), resolveArtifactsSync: vi.fn(), })) describe('bundler concurrency', () => { const mockLogger = { error: vi.fn(), warn: vi.fn(), info: vi.fn(), debug: vi.fn(), log: vi.fn(), } const mockConfig = { jsonAsConst: [], foundryProject: false, libs: [], remappings: {}, cacheDir: '/tmp/cache', } const mockFao = { writeFileSync: vi.fn(), writeFile: vi.fn(), readFile: vi.fn(), readFileSync: vi.fn(), exists: vi.fn(), existsSync: vi.fn(), statSync: vi.fn(), stat: vi.fn(), mkdirSync: vi.fn(), mkdir: vi.fn(), } const mockSolc = {} as any const contractPackage = '@tevm/contract' const mockCache = createCache(tmpdir(), mockFao, tmpdir()) const mockModules = { module1: { id: 'id', rawCode: `import { TestContract } from 'module2' contract TestContract {}`, code: `import { TestContract } from 'module2' contract TestContract {}`, importedIds: ['module2'], }, } const resolver = bundler(mockConfig as any, mockLogger, mockFao as any, mockSolc, mockCache, contractPackage) const mockResolveArtifacts = resolveArtifacts as Mock it('should process multiple files concurrently', async () => { // Setup for first module const firstArtifacts = { Contract1: { contractName: 'Contract1', abi: [ { name: 'method1', type: 'function', inputs: [], outputs: [], stateMutability: 'nonpayable', }, ], userdoc: { methods: {}, kind: 'user', version: 1, }, evm: { deployedBytecode: { object: '0x111' } }, }, } // Setup for second module const secondArtifacts = { Contract2: { contractName: 'Contract2', abi: [ { name: 'method2', type: 'function', inputs: [], outputs: [], stateMutability: 'nonpayable', }, ], userdoc: { methods: {}, kind: 'user', version: 1, }, evm: { deployedBytecode: { object: '0x222' } }, }, } // Mock for first module call mockResolveArtifacts.mockImplementationOnce(async () => ({ artifacts: firstArtifacts, modules: mockModules, asts: { 'Contract1.sol': {} }, solcInput: { language: 'Solidity', settings: {}, sources: {} }, solcOutput: { contracts: {}, sources: {} }, })) // Mock for second module call mockResolveArtifacts.mockImplementationOnce(async () => ({ artifacts: secondArtifacts, modules: mockModules, asts: { 'Contract2.sol': {} }, solcInput: { language: 'Solidity', settings: {}, sources: {} }, solcOutput: { contracts: {}, sources: {} }, })) // Start concurrent processing of both modules const firstPromise = resolver.resolveEsmModule('module1.sol', 'basedir', false, true) const secondPromise = resolver.resolveEsmModule('module2.sol', 'basedir', false, true) // Wait for both to complete const [firstResult, secondResult] = await Promise.all([firstPromise, secondPromise]) // Verify results contain correct contract names expect(firstResult.code).toContain('Contract1') expect(secondResult.code).toContain('Contract2') // Verify correct module names were passed to the compiler expect(mockResolveArtifacts).toHaveBeenCalledWith( 'module1.sol', 'basedir', expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), ) expect(mockResolveArtifacts).toHaveBeenCalledWith( 'module2.sol', 'basedir', expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), expect.anything(), ) }) it('should handle multiple module types concurrently', async () => { const artifacts = { Contract: { contractName: 'Contract', abi: [ { name: 'method', type: 'function', inputs: [], outputs: [], stateMutability: 'nonpayable', }, ], userdoc: { methods: {}, kind: 'user', version: 1, }, evm: { deployedBytecode: { object: '0x111' } }, }, } // Reset mock and configure for multiple calls mockResolveArtifacts.mockReset() mockResolveArtifacts.mockImplementation(async () => ({ artifacts, modules: mockModules, asts: { 'Contract.sol': {} }, solcInput: { language: 'Solidity', settings: {}, sources: {} }, solcOutput: { contracts: {}, sources: {} }, })) // Process the same module with different module types concurrently const mjsPromise = resolver.resolveEsmModule('module.sol', 'basedir', false, true) const dtsPromise = resolver.resolveDts('module.sol', 'basedir', false, true) // Wait for both to complete const [mjsResult, dtsResult] = await Promise.all([mjsPromise, dtsPromise]) // Verify distinct output formats expect(mjsResult.code).toContain('export const Contract') expect(dtsResult.code).toContain('Contract<') }) it('should handle error in one concurrent operation without affecting others', async () => { // Setup for successful module const artifacts = { Contract: { contractName: 'Contract', abi: [ { name: 'method', type: 'function', inputs: [], outputs: [], stateMutability: 'nonpayable', }, ], userdoc: { methods: {}, kind: 'user', version: 1, }, evm: { deployedBytecode: { object: '0x111' } }, }, } // Reset mock to clear previous calls mockResolveArtifacts.mockReset() // Mock for successful call mockResolveArtifacts.mockImplementationOnce(async () => ({ artifacts, modules: mockModules, asts: { 'Contract.sol': {} }, solcInput: { language: 'Solidity', settings: {}, sources: {} }, solcOutput: { contracts: {}, sources: {} }, })) // Mock for failing call mockResolveArtifacts.mockImplementationOnce(async () => { throw new Error('Compilation failed') }) // Start concurrent operations const successPromise = resolver.resolveEsmModule('success.sol', 'basedir', false, true) const failPromise = resolver.resolveEsmModule('fail.sol', 'basedir', false, true) // Wait for successful operation const successResult = await successPromise expect(successResult.code).toContain('Contract') // Verify failing operation errors await expect(failPromise).rejects.toThrow('Compilation failed') }) it('should handle large number of concurrent operations', async () => { const numOperations = 5 // Simulate 5 concurrent operations // Reset mock mockResolveArtifacts.mockReset() // Create artifacts for each operation const promises: Promise<any>[] = [] for (let i = 0; i < numOperations; i++) { const contractName = `Contract${i}` const artifacts = { [contractName]: { contractName, abi: [ { name: `method${i}`, type: 'function', inputs: [], outputs: [], stateMutability: 'nonpayable', }, ], userdoc: { methods: {}, kind: 'user', version: 1, }, evm: { deployedBytecode: { object: `0x${i}${i}${i}` } }, }, } // Setup mock mockResolveArtifacts.mockImplementationOnce(async () => ({ artifacts, modules: mockModules, asts: { [`Contract${i}.sol`]: {} }, solcInput: { language: 'Solidity', settings: {}, sources: {} }, solcOutput: { contracts: {}, sources: {} }, })) // Create promise for this operation promises.push(resolver.resolveEsmModule(`module${i}.sol`, 'basedir', false, true)) } // Wait for all operations to complete const results = await Promise.all(promises) // Verify each result for (let i = 0; i < numOperations; i++) { if (results[i]) { expect(results[i].code).toContain(`Contract${i}`) } } // Verify correct number of calls expect(mockResolveArtifacts).toHaveBeenCalledTimes(numOperations) }) })