diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 578b5cc..eec1232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -136,4 +136,4 @@ jobs: if: ${{ failure() }} run: | echo "========== Ocean Node Logs ==========" - tac ${{ github.workspace }}/ocean-node/ocean-node.log || echo "Log file not found" + docker logs --tail 1000 ocean-node-1 2>&1 | tac || echo "Ocean Node container logs not available" diff --git a/package-lock.json b/package-lock.json index 614f079..20d58e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "license": "Apache-2.0", "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", - "@oceanprotocol/contracts": "^2.4.0", - "@oceanprotocol/ddo-js": "^0.1.3", - "@oceanprotocol/lib": "^5.0.0", + "@oceanprotocol/contracts": "^2.4.1", + "@oceanprotocol/ddo-js": "^0.1.4", + "@oceanprotocol/lib": "^5.0.3", "commander": "^13.1.0", "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", @@ -3151,15 +3151,15 @@ } }, "node_modules/@oceanprotocol/contracts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.0.tgz", - "integrity": "sha512-OqDUBqQXPT68geLgGJDyH9Wsg1lE1V4ZS7aCJSfGdDWlMOBUAh/KxuaY46ga6WwWz8XVGTB+HZLGLQTYROo5tw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.1.tgz", + "integrity": "sha512-mOad2bCyOqYTp5oSiivH23shU/AyfEfp/beBl9zkSEhz7LagvFvDAIicELfHd0NaSVSwvTbM5NbMuOC0Wd5NSA==", "license": "Apache-2.0" }, "node_modules/@oceanprotocol/ddo-js": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@oceanprotocol/ddo-js/-/ddo-js-0.1.3.tgz", - "integrity": "sha512-CHZ0dZIM85xND6ckrVg5KIIXN5bYcNQV8udTISgCP3cTNOOlgnXdBPXELTKPg5h89qP8oEjofKC/kJjg+CJbng==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@oceanprotocol/ddo-js/-/ddo-js-0.1.4.tgz", + "integrity": "sha512-+9nOslJCYMiIldIQ8dixdSjBjrrlqzb9q+nRQXqn60ZIi45AoR4ajv1LdqPwScCmoeLZgMxiZsnUXq/suDSLNg==", "license": "Apache-2.0", "dependencies": { "@rdfjs/formats-common": "^3.1.0", @@ -3230,9 +3230,9 @@ } }, "node_modules/@oceanprotocol/lib": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-5.0.0.tgz", - "integrity": "sha512-gDnvsRivwMV2rlpeikmMXwVT73TK41FVd1cm6q55QMPUOpOsdW5K7gybFHbB+hwNd2FZuvpxwIHkH2PqKMCO1Q==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@oceanprotocol/lib/-/lib-5.0.3.tgz", + "integrity": "sha512-j+5QOs8JDSwiZvezHxSQVkQmL5ELG/isfCmGOgW22PbFT2xoMJUe2wWtAfhbDs/YgWsYP/mscwUUuwKOD2QcWA==", "license": "Apache-2.0", "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", diff --git a/package.json b/package.json index 43f2045..40e8789 100644 --- a/package.json +++ b/package.json @@ -45,9 +45,9 @@ }, "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", - "@oceanprotocol/contracts": "^2.4.0", - "@oceanprotocol/ddo-js": "^0.1.3", - "@oceanprotocol/lib": "^5.0.0", + "@oceanprotocol/contracts": "^2.4.1", + "@oceanprotocol/ddo-js": "^0.1.4", + "@oceanprotocol/lib": "^5.0.3", "commander": "^13.1.0", "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", diff --git a/src/cli.ts b/src/cli.ts index b00d7b0..0b5b3e1 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -490,5 +490,76 @@ export async function createCLI() { await commands.getAuthorizationsEscrow(token || options.token, payee || options.payee); }); + program + .command('createAccessList') + .description('Create a new access list contract') + .argument('', 'Name for the access list') + .argument('', 'Symbol for the access list') + .argument('[transferable]', 'Whether tokens are transferable (true/false)', 'false') + .argument('[initialUsers]', 'Comma-separated list of initial user addresses', '') + .option('-n, --name ', 'Name for the access list') + .option('-s, --symbol ', 'Symbol for the access list') + .option('-t, --transferable [transferable]', 'Whether tokens are transferable (true/false)', 'false') + .option('-u, --users [initialUsers]', 'Comma-separated list of initial user addresses', '') + .action(async (name, symbol, transferable, initialUsers, options) => { + const { signer, chainId } = await initializeSigner(); + const commands = new Commands(signer, chainId); + await commands.createAccessList([ + options.name || name, + options.symbol || symbol, + options.transferable || transferable, + options.users || initialUsers + ]); + }); + + program + .command('addToAccessList') + .description('Add user(s) to an access list') + .argument('', 'Address of the access list contract') + .argument('', 'Comma-separated list of user addresses to add') + .option('-a, --address ', 'Address of the access list contract') + .option('-u, --users ', 'Comma-separated list of user addresses to add') + .action(async (accessListAddress, users, options) => { + const { signer, chainId } = await initializeSigner(); + const commands = new Commands(signer, chainId); + await commands.addToAccessList([ + options.address || accessListAddress, + options.users || users + ]); + }); + + program + .command('checkAccessList') + .description('Check if user(s) are on an access list') + .argument('', 'Address of the access list contract') + .argument('', 'Comma-separated list of user addresses to check') + .option('-a, --address ', 'Address of the access list contract') + .option('-u, --users ', 'Comma-separated list of user addresses to check') + .action(async (accessListAddress, users, options) => { + const { signer, chainId } = await initializeSigner(); + const commands = new Commands(signer, chainId); + await commands.checkAccessList([ + options.address || accessListAddress, + options.users || users + ]); + }); + + program + .command('removeFromAccessList') + .description('Remove user(s) from an access list') + .argument('', 'Address of the access list contract') + .argument('', 'Comma-separated list of user addresses to remove') + .option('-a, --address ', 'Address of the access list contract') + .option('-u, --users ', 'Comma-separated list of user addresses to remove') + .action(async (accessListAddress, users, options) => { + const { signer, chainId } = await initializeSigner(); + const commands = new Commands(signer, chainId); + await commands.removeFromAccessList([ + options.address || accessListAddress, + options.users || users + ]); + }); + + return program; } \ No newline at end of file diff --git a/src/commands.ts b/src/commands.ts index 41807b8..112e8e2 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -27,7 +27,9 @@ import { sendTx, unitsToAmount, EscrowContract, - getTokenDecimals + getTokenDecimals, + AccesslistFactory, + AccessListContract } from "@oceanprotocol/lib"; import { Asset } from '@oceanprotocol/ddo-js'; import { Signer, ethers, getAddress } from "ethers"; @@ -729,6 +731,7 @@ export class Commands { ) console.log("Verifying payment..."); await new Promise(resolve => setTimeout(resolve, 3000)) + const validationEscrow = await escrow.verifyFundsForEscrowPayment( paymentToken, computeEnv.consumerAddress, @@ -791,10 +794,10 @@ export class Commands { null, null, // additionalDatasets, only c2d v1 - output, + output ); - console.log("compute jobs: ", computeJobs); + console.log("computeJobs: ", computeJobs); if (computeJobs && computeJobs[0]) { const { jobId, payment } = computeJobs[0]; @@ -1454,4 +1457,176 @@ export class Commands { return authorizations; } + + public async createAccessList(args: string[]): Promise { + try { + const name = args[0]; + const symbol = args[1]; + const transferable = args[2] === 'true'; + const initialUsers = args[3] ? args[3].split(',').map(u => u.trim()) : []; + + if (!name || !symbol) { + console.error(chalk.red('Name and symbol are required')); + return; + } + + const config = await getConfigByChainId(Number(this.config.chainId)); + if (!config.AccessListFactory) { + console.error(chalk.red('Access list factory not found. Check local address.json file')); + return; + } + const accessListFactory = new AccesslistFactory( + config.AccessListFactory, + this.signer, + Number(this.config.chainId) + ); + + const owner = await this.signer.getAddress(); + const tokenURIs = initialUsers.map(() => 'https://oceanprotocol.com/nft/'); + + console.log(chalk.cyan('Creating new access list...')); + console.log(`Name: ${name}`); + console.log(`Symbol: ${symbol}`); + console.log(`Transferable: ${transferable}`); + console.log(`Owner: ${owner}`); + console.log(`Initial users: ${initialUsers.length > 0 ? initialUsers.join(', ') : 'none'}`); + + const accessListAddress = await accessListFactory.deployAccessListContract( + name, + symbol, + tokenURIs, + transferable, + owner, + initialUsers + ); + + console.log(chalk.green(`\nAccess list created successfully!`)); + console.log(chalk.green(`Contract address: ${accessListAddress}`)); + } catch (error) { + console.error(chalk.red('Error creating access list:'), error); + } + } + + public async addToAccessList(args: string[]): Promise { + try { + const accessListAddress = args[0]; + const users = args[1].split(',').map(u => u.trim()); + + if (!accessListAddress || users.length === 0) { + console.error(chalk.red('Access list address and at least one user are required')); + return; + } + + const accessList = new AccessListContract( + accessListAddress, + this.signer, + Number(this.config.chainId) + ); + + console.log(chalk.cyan(`Adding ${users.length} user(s) to access list...`)); + + if (users.length === 1) { + const tx = await accessList.mint(users[0], 'https://oceanprotocol.com/nft/'); + await tx.wait(); + console.log(chalk.green(`Successfully added user ${users[0]} to access list`)); + return; + } + + const tokenURIs = users.map(() => 'https://oceanprotocol.com/nft/'); + const tx = await accessList.batchMint(users, tokenURIs); + await tx.wait(); + console.log(chalk.green(`Successfully added ${users.length} users to access list:`)); + users.forEach(user => console.log(` - ${user}`)); + } catch (error) { + console.error(chalk.red('Error adding users to access list:'), error); + } + } + + + public async checkAccessList(args: string[]): Promise { + try { + const accessListAddress = args[0]; + const users = args[1].split(',').map(u => u.trim()); + + if (!accessListAddress || users.length === 0) { + console.error(chalk.red('Access list address and at least one user are required')); + return; + } + + const accessList = new AccessListContract( + accessListAddress, + this.signer, + Number(this.config.chainId) + ); + + console.log(chalk.cyan(`Checking access list for ${users.length} user(s)...\n`)); + + for (const user of users) { + const balance = await accessList.balance(user); + const hasAccess = Number(balance) > 0; + + if (hasAccess) { + console.log(chalk.green(`✓ ${user}: Has access (balance: ${balance})`)); + } else { + console.log(chalk.red(`✗ ${user}: No access`)); + } + } + } catch (error) { + console.error(chalk.red('Error checking access list:'), error); + } + } + + + public async removeFromAccessList(args: string[]): Promise { + try { + const accessListAddress = args[0]; + const users = args[1].split(',').map(u => u.trim()); + + if (!accessListAddress || users.length === 0) { + console.error(chalk.red('Access list address and at least one user address are required')); + return; + } + + const accessList = new AccessListContract( + accessListAddress, + this.signer, + Number(this.config.chainId) + ); + + console.log(chalk.cyan(`Removing ${users.length} user(s) from access list...`)); + for (const user of users) { + const balance = await accessList.balance(user); + + if (Number(balance) === 0) { + console.log(chalk.yellow(`⚠ User ${user} is not on the access list, skipping...`)); + continue; + } + + const balanceNum = Number(balance); + const contract = accessList.contract; + + let removedCount = 0; + for (let index = 0; index < balanceNum; index++) { + try { + const tokenId = await contract.tokenOfOwnerByIndex(user, index); + const tx = await accessList.burn(Number(tokenId)); + await tx.wait(); + + console.log(chalk.green(`✓ Successfully removed user ${user} (token ID: ${tokenId})`)); + removedCount++; + } catch (e: any) { + console.log(chalk.yellow(`⚠ Could not remove token at index ${index} for user ${user}: ${e.message}`)); + } + } + + if (removedCount === 0) { + console.log(chalk.yellow(`⚠ Could not remove any tokens for user ${user}`)); + } else if (removedCount < balanceNum) { + console.log(chalk.yellow(`⚠ Only removed ${removedCount} of ${balanceNum} tokens for user ${user}`)); + } + } + } catch (error) { + console.error(chalk.red('Error removing users from access list:'), error); + } + } } diff --git a/src/helpers.ts b/src/helpers.ts index 0d7647b..e42eb81 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -5,8 +5,7 @@ import * as path from "path"; import * as sapphire from '@oasisprotocol/sapphire-paratime'; import { Asset, DDO } from '@oceanprotocol/ddo-js'; import { - AccesslistFactory, - Aquarius, + AccesslistFactory, Aquarius, Nft, NftFactory, ProviderInstance, diff --git a/test/accessList.test.ts b/test/accessList.test.ts new file mode 100644 index 0000000..39780d2 --- /dev/null +++ b/test/accessList.test.ts @@ -0,0 +1,295 @@ +import { expect } from "chai"; +import { homedir } from 'os'; +import { runCommand } from "./util.js"; +import { getConfigByChainId } from "../src/helpers.js"; +import { JsonRpcProvider, ethers } from "ethers"; +import { AccessListContract, AccesslistFactory } from "@oceanprotocol/lib"; + +describe("Ocean CLI Access List", function () { + this.timeout(120000); + + let chainConfig: any; + let accessListAddress: string; + let owner: ethers.Wallet; + let testUser1: ethers.Wallet; + let testUser2: ethers.Wallet; + + before(async function () { + process.env.AVOID_LOOP_RUN = "true"; + process.env.PRIVATE_KEY = "0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58"; + process.env.RPC = "http://localhost:8545"; + process.env.NODE_URL = "http://localhost:8000"; + process.env.ADDRESS_FILE = `${homedir}/.ocean/ocean-contracts/artifacts/address.json`; + + chainConfig = await getConfigByChainId(8996); + + const provider = new JsonRpcProvider(process.env.RPC); + owner = new ethers.Wallet(process.env.PRIVATE_KEY, provider); + testUser1 = new ethers.Wallet('0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209', provider); + testUser2 = new ethers.Wallet('0x5d75837c166221195c8763c7f6fc5f6b8f0e8f43f9f4e0b0e7a3f7e2f9f4e3a1', provider); + }); + + describe("Create Access List", function () { + it("should create a new access list contract", async function () { + const name = "TestAccessList"; + const symbol = "TAL"; + const transferable = "false"; + + const output = await runCommand( + `npm run cli createAccessList ${name} ${symbol} ${transferable}` + ); + + expect(output).to.include("Access list created successfully"); + expect(output).to.include("Contract address:"); + + const addressMatch = output.match(/Contract address: (0x[a-fA-F0-9]{40})/); + if (addressMatch) { + accessListAddress = addressMatch[1]; + console.log(`Created access list at: ${accessListAddress}`); + } else { + throw new Error("Could not extract access list address from output"); + } + }); + + it("should create a new access list with initial users", async function () { + const name = "TestAccessListWithUsers"; + const symbol = "TALWU"; + const transferable = "false"; + const initialUsers = `${testUser1.address},${testUser2.address}`; + + const output = await runCommand( + `npm run cli createAccessList ${name} ${symbol} ${transferable} ${initialUsers}` + ); + + expect(output).to.include("Access list created successfully"); + expect(output).to.include("Contract address:"); + expect(output).to.include(`Initial users: ${testUser1.address}, ${testUser2.address}`); + }); + + it("should fail to create access list without required parameters", async function () { + try { + await runCommand(`npm run cli createAccessList`); + throw new Error("Should have thrown an error"); + } catch (error: any) { + expect(error.stderr || error.message).to.satisfy((msg: string) => + msg.includes("error: missing required argument") || + msg.includes("Name and symbol are required") + ); + } + }); + }); + + describe("Add Users to Access List", function () { + it("should add a single user to the access list", async function () { + const output = await runCommand( + `npm run cli addToAccessList ${accessListAddress} ${testUser1.address}` + ); + + expect(output).to.include(`Successfully added user ${testUser1.address}`); + }); + + it("should add multiple users to the access list", async function () { + const users = `${testUser2.address},${owner.address}`; + + const output = await runCommand( + `npm run cli addToAccessList ${accessListAddress} ${users}` + ); + + expect(output).to.include("Successfully added"); + expect(output).to.include("users to access list"); + }); + + it("should fail to add users without required parameters", async function () { + try { + await runCommand(`npm run cli addToAccessList ${accessListAddress}`); + throw new Error("Should have thrown an error"); + } catch (error: any) { + expect(error.stderr || error.message).to.satisfy((msg: string) => + msg.includes("error: missing required argument") || + msg.includes("at least one user are required") + ); + } + }); + }); + + describe("Check Users on Access List", function () { + it("should check if a single user is on the access list", async function () { + const output = await runCommand( + `npm run cli checkAccessList ${accessListAddress} ${testUser1.address}` + ); + + expect(output).to.include(testUser1.address); + expect(output).to.satisfy((msg: string) => + msg.includes("Has access") || msg.includes("No access") + ); + }); + + it("should check multiple users on the access list", async function () { + const users = `${testUser1.address},${testUser2.address}`; + + const output = await runCommand( + `npm run cli checkAccessList ${accessListAddress} ${users}` + ); + + expect(output).to.include(testUser1.address); + expect(output).to.include(testUser2.address); + }); + + it("should show 'Has access' for users on the list", async function () { + const output = await runCommand( + `npm run cli checkAccessList ${accessListAddress} ${testUser1.address}` + ); + + expect(output).to.include("Has access"); + }); + + it("should verify user access using direct contract call", async function () { + const accessList = new AccessListContract( + accessListAddress, + owner, + chainConfig.chainId + ); + + const balance = await accessList.balance(testUser1.address); + expect(Number(balance)).to.be.greaterThan(0); + }); + }); + + describe("Remove Users from Access List", function () { + it("should remove a user from the access list by address", async function () { + const output = await runCommand( + `npm run cli removeFromAccessList ${accessListAddress} ${testUser1.address}` + ); + + expect(output).to.include("Successfully removed user"); + expect(output).to.include(testUser1.address); + }); + + it("should remove multiple users by addresses", async function () { + await runCommand( + `npm run cli addToAccessList ${accessListAddress} ${testUser1.address}` + ); + + await runCommand( + `npm run cli addToAccessList ${accessListAddress} ${testUser2.address}` + ); + + const users = `${testUser1.address},${testUser2.address}`; + + const output = await runCommand( + `npm run cli removeFromAccessList ${accessListAddress} ${users}` + ); + + expect(output).to.include("Successfully removed user"); + }); + + it("should handle removing a user not on the access list", async function () { + const nonExistentUser = "0x0000000000000000000000000000000000000001"; + + const output = await runCommand( + `npm run cli removeFromAccessList ${accessListAddress} ${nonExistentUser}` + ); + + expect(output).to.include("not on the access list"); + }); + + it("should fail to remove with invalid address", async function () { + const invalidAddress = "invalid-address"; + + try { + await runCommand( + `npm run cli removeFromAccessList ${accessListAddress} ${invalidAddress}` + ); + } catch (error: any) { + expect(error.stderr || error.message).to.satisfy((msg: string) => + msg.includes("Error removing users") || + msg.includes("error") || + msg.includes("invalid address") + ); + } + }); + }); + + describe("Access List Factory", function () { + it("should verify access list is deployed via factory", async function () { + const factory = new AccesslistFactory( + chainConfig.AccessListFactory, + owner, + chainConfig.chainId + ); + + const isDeployed = await factory.isDeployed(accessListAddress); + expect(isDeployed).to.be.true; + }); + + it("should verify access list is soulbound", async function () { + const factory = new AccesslistFactory( + chainConfig.AccessListFactory, + owner, + chainConfig.chainId + ); + + const isSoulbound = await factory.isSoulbound(accessListAddress); + expect(isSoulbound).to.be.true; + }); + }); + + + describe("Edge Cases", function () { + it("should handle empty initial users list", async function () { + const output = await runCommand( + `npm run cli createAccessList EmptyList EL false ""` + ); + + expect(output).to.include("Access list created successfully"); + expect(output).to.include("Initial users: none"); + }); + + it("should fail with invalid address format", async function () { + try { + await runCommand( + `npm run cli checkAccessList ${accessListAddress} invalid-address` + ); + } catch (error: any) { + expect(error.stderr || error.message).to.satisfy((msg: string) => + msg.includes("Error checking access list") || + msg.includes("error") || + msg.includes("invalid address") + ); + } + }); + }); + + describe("E2E Workflow", function () { + it("should complete a full access list workflow", async function () { + const createOutput = await runCommand( + `npm run cli createAccessList WorkflowTest WT false` + ); + expect(createOutput).to.include("Access list created successfully"); + + const addressMatch = createOutput.match(/Contract address: (0x[a-fA-F0-9]{40})/); + const workflowAccessList = addressMatch ? addressMatch[1] : ""; + + const addOutput = await runCommand( + `npm run cli addToAccessList ${workflowAccessList} ${testUser1.address}` + ); + expect(addOutput).to.include("Successfully added"); + + const checkOutput = await runCommand( + `npm run cli checkAccessList ${workflowAccessList} ${testUser1.address}` + ); + expect(checkOutput).to.include("Has access"); + + const removeOutput = await runCommand( + `npm run cli removeFromAccessList ${workflowAccessList} ${testUser1.address}` + ); + expect(removeOutput).to.include("Successfully removed"); + + const finalCheckOutput = await runCommand( + `npm run cli checkAccessList ${workflowAccessList} ${testUser1.address}` + ); + expect(finalCheckOutput).to.include("No access"); + }); + }); +}); + diff --git a/test/paidComputeFlow.test.ts b/test/paidComputeFlow.test.ts index b53395b..8847cb3 100644 --- a/test/paidComputeFlow.test.ts +++ b/test/paidComputeFlow.test.ts @@ -18,6 +18,7 @@ describe("Ocean CLI Paid Compute", function() { let resources: any; let computeJobId: string; let agreementId: string; + process.env.AVOID_LOOP_RUN = "true"; const getAddresses = () => { const data = JSON.parse( @@ -37,10 +38,10 @@ describe("Ocean CLI Paid Compute", function() { throw new Error("Metadata file not found: " + metadataFile); } - process.env.PRIVATE_KEY = "0x1d751ded5a32226054cd2e71261039b65afb9ee1c746d055dd699b1150a5befc"; - // Using this account: 0x529043886F21D9bc1AE0feDb751e34265a246e47 - process.env.RPC = "http://127.0.0.1:8545"; - process.env.NODE_URL = "http://127.0.0.1:8001"; + process.env.PRIVATE_KEY = "0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58"; + // Using this account: 0xe2DD09d719Da89e5a3D0F2549c7E24566e947260 + process.env.RPC = "http://localhost:8545"; + process.env.NODE_URL = "http://localhost:8001"; process.env.ADDRESS_FILE = path.join(process.env.HOME || "", ".ocean/ocean-contracts/artifacts/address.json"); const output = await runCommand(`npm run cli publish ${metadataFile}`); @@ -152,20 +153,7 @@ describe("Ocean CLI Paid Compute", function() { const env = computeEnvs[0]; expect(env).to.be.an('object').and.to.not.be.null.and.to.not.be.undefined; - resources = [ - { - id: 'cpu', - amount: env.resources[0].max - env.resources[0].inUse - 1 - }, - { - id: 'ram', - amount: env.resources[1].max - env.resources[1].inUse - 1000 - }, - { - id: 'disk', - amount: 0 - } - ] + resources = [] const paymentToken = getAddresses().Ocean const output = await runCommand(`npm run cli -- startCompute ${computeDatasetDid} ${jsAlgoDid} ${computeEnvId} 900 ${paymentToken} '${JSON.stringify(resources)}' --accept true`); const jobIdMatch = output.match(/JobID:\s*([^\s]+)/); diff --git a/test/setup.test.ts b/test/setup.test.ts index b1c7dc4..8702483 100644 --- a/test/setup.test.ts +++ b/test/setup.test.ts @@ -8,6 +8,7 @@ import { fileURLToPath } from 'url' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) describe("Ocean CLI Setup", function() { + process.env.AVOID_LOOP_RUN = "true"; this.timeout(20000); // Set a longer timeout to allow the command to execute it("should return a valid response for 'npm run cli h'", function(done) { @@ -51,7 +52,15 @@ describe("Ocean CLI Setup", function() { expect(stdout).to.contain("Gets the existing compute environments"); expect(stdout).to.contain("computeStreamableLogs"); expect(stdout).to.contain("Gets the existing compute streamable logs"); - + expect(stdout).to.contain("createAccessList"); + expect(stdout).to.contain("Create a new access list contract"); + expect(stdout).to.contain("addToAccessList"); + expect(stdout).to.contain("Add user(s) to an access list"); + expect(stdout).to.contain("checkAccessList"); + expect(stdout).to.contain("Check if user(s) are on an access list"); + expect(stdout).to.contain("removeFromAccessList"); + expect(stdout).to.contain("Remove user(s) from an access list"); + done(); } catch (assertionError) { done(assertionError); diff --git a/test/util.ts b/test/util.ts index d2fb9d3..a00cfcc 100644 --- a/test/util.ts +++ b/test/util.ts @@ -2,8 +2,8 @@ import { exec } from "child_process"; import path from "path"; import util from "util"; -import { dirname } from 'path' -import { fileURLToPath } from 'url' +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; export const execPromise = util.promisify(exec); @@ -14,13 +14,13 @@ export const __dirname = dirname(__filename) export const projectRoot = path.resolve(__dirname, ".."); export const runCommand = async (command: string): Promise => { - console.log(`\n[CMD]: ${command}`); - try { - const { stdout } = await execPromise(command, { cwd: projectRoot }); - console.log(`[OUTPUT]:\n${stdout}`); - return stdout; - } catch (error: any) { - console.error(`[ERROR]:\n${error.stderr || error.message}`); - throw error; - } + console.log(`\n[CMD]: ${command}`); + try { + const { stdout } = await execPromise(command, { cwd: projectRoot }); + console.log(`[OUTPUT]:\n${stdout}`); + return stdout; + } catch (error: any) { + console.error(`[ERROR]:\n${error.stderr || error.message}`); + throw error; + } }; \ No newline at end of file