From 41b21eff6c7a3ed1f5f516171525bc4f6d2f4529 Mon Sep 17 00:00:00 2001 From: nothingbutsyntaxerror Date: Fri, 19 Dec 2025 12:07:59 +0000 Subject: [PATCH 1/7] test: add confDir test and fix linting in integration spec --- src/help.ts | 2 +- src/test/cli.integration.spec.ts | 4 +--- src/test/confDir.spec.ts | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/test/confDir.spec.ts diff --git a/src/help.ts b/src/help.ts index e1b0775..a33dab1 100644 --- a/src/help.ts +++ b/src/help.ts @@ -23,7 +23,7 @@ Options -l --logout nothing Accepts boolean value: use it in order to expire your current session. -r --listPlans nothing Accepts boolean value: list all the plans that are offered in your account using it. -u --serverUrl string Change the base URL for the FaaS. - -c --confDir string (TODO) Overwrite the default configuration directory.`; + -c --confDir string Overwrite the default configuration directory.`; export const printHelp = (): void => { console.log(helpText); diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index 14a0525..50dfba0 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -92,9 +92,7 @@ describe('Integration CLI (Deploy)', function () { )}` ); } catch (err) { - ok( - String(err) === '! --yeet does not exist as a valid command.\n' - ); + ok(String(err) === '! --yeet does not exist as a valid command.\n'); } }); diff --git a/src/test/confDir.spec.ts b/src/test/confDir.spec.ts new file mode 100644 index 0000000..38c8418 --- /dev/null +++ b/src/test/confDir.spec.ts @@ -0,0 +1,41 @@ +import { strictEqual } from 'assert'; +import { existsSync, mkdirSync, rmdirSync, writeFileSync } from 'fs'; +import { join } from 'path'; +import { startup } from '../startup'; + +describe('Configuration Directory Logic', () => { + // 1. Create a temporary folder path + const customPath = join(process.cwd(), 'test-custom-config'); + const configFilePath = join(customPath, 'config.ini'); + + const fakeToken = 'test-token-12345'; + + // Write in INI format (key=value) + const iniContent = `token=${fakeToken}`; + + before(() => { + if (!existsSync(customPath)) { + mkdirSync(customPath); + } + // Write the INI file + writeFileSync(configFilePath, iniContent, 'utf8'); + }); + + after(() => { + if (existsSync(customPath)) { + // Cleanup + rmdirSync(customPath, { recursive: true }); + } + }); + + it('should load config from a custom directory via --confDir', async () => { + // Run startup. It should find config.ini and use the token inside. + const config = await startup(customPath); + + strictEqual( + config.token, + fakeToken, + 'The token loaded does not match the custom config file' + ); + }); +}); From 494d0a871ed404d03c768e3bfc35af1e32a5261b Mon Sep 17 00:00:00 2001 From: nothingbutsyntaxerror Date: Fri, 19 Dec 2025 19:40:16 +0000 Subject: [PATCH 2/7] test: add integration test for --confDir flag --- src/test/confDir.spec.ts | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/test/confDir.spec.ts b/src/test/confDir.spec.ts index 38c8418..93b566d 100644 --- a/src/test/confDir.spec.ts +++ b/src/test/confDir.spec.ts @@ -1,41 +1,61 @@ import { strictEqual } from 'assert'; +import spawn from 'cross-spawn'; import { existsSync, mkdirSync, rmdirSync, writeFileSync } from 'fs'; -import { join } from 'path'; -import { startup } from '../startup'; +import { join, resolve } from 'path'; -describe('Configuration Directory Logic', () => { - // 1. Create a temporary folder path +describe('CLI Integration with --confDir', () => { + // 1. Setup paths const customPath = join(process.cwd(), 'test-custom-config'); const configFilePath = join(customPath, 'config.ini'); - const fakeToken = 'test-token-12345'; + // Path to the compiled CLI entry point + const cliPath = resolve(__dirname, '../index.js'); - // Write in INI format (key=value) + // 2. Mock Data + const fakeToken = 'test-token-12345'; const iniContent = `token=${fakeToken}`; before(() => { if (!existsSync(customPath)) { mkdirSync(customPath); } - // Write the INI file writeFileSync(configFilePath, iniContent, 'utf8'); }); after(() => { if (existsSync(customPath)) { - // Cleanup rmdirSync(customPath, { recursive: true }); } }); - it('should load config from a custom directory via --confDir', async () => { - // Run startup. It should find config.ini and use the token inside. - const config = await startup(customPath); + it('should load config from a custom directory passed via --confDir', done => { + const process = spawn('node', [cliPath, '--confDir', customPath], { + stdio: 'pipe' + }); + + let output = ''; + + // Fix: Explicitly type 'data' as Buffer so .toString() is safe + process.stdout?.on('data', (data: Buffer) => { + output += data.toString(); + }); + + process.stderr?.on('data', (data: Buffer) => { + output += data.toString(); + }); + + // Fix: Removed unused 'code' parameter + process.on('close', () => { + const promptForLogin = + output.includes('Select an option') || output.includes('Login'); + + strictEqual( + promptForLogin, + false, + 'The CLI ignored the config file and prompted for login.' + ); - strictEqual( - config.token, - fakeToken, - 'The token loaded does not match the custom config file' - ); + done(); + }); }); }); From b67b97e52f5c8d5ea6369201807317b3ebbe2db4 Mon Sep 17 00:00:00 2001 From: nothingbutsyntaxerror Date: Sat, 20 Dec 2025 10:58:59 +0000 Subject: [PATCH 3/7] test: add integration test for --confDir using runCLI --- src/test/cli.integration.spec.ts | 70 +++++++++++++------------------- src/test/confDir.spec.ts | 61 ---------------------------- 2 files changed, 29 insertions(+), 102 deletions(-) delete mode 100644 src/test/confDir.spec.ts diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index 50dfba0..c5bc236 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -1,4 +1,5 @@ import { fail, notStrictEqual, ok, strictEqual } from 'assert'; +import { writeFileSync } from 'fs'; // <--- ADDED THIS import { join } from 'path'; import { load } from '../config'; import { @@ -74,6 +75,34 @@ describe('Integration CLI (Deploy)', function () { } }); + // --confDir + it('Should be able to login using --confDir flag', async function () { + const file = await load(); + const token = file.token || ''; + + notStrictEqual(token, ''); + + await clearCache(); + + const confDir = await createTmpDirectory(); + const configPath = join(confDir, 'config.ini'); + writeFileSync(configPath, `token=${token}`, 'utf8'); + + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [`--confDir=${confDir}`, `--workdir=${workdir}`], + [keys.enter, keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + // --help it('Should be able to print help guide using --help flag', async () => { const result = await runCLI(['--help'], [keys.enter]).promise; @@ -254,44 +283,6 @@ describe('Integration CLI (Deploy)', function () { return result; }); - // TODO: - // --force - // it('Should be able to deploy forcefully using --force flag', async () => { - // const resultDel = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential', - // '--force' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDel).includes('Trying to deploy forcefully!')); - - // strictEqual(await deleted(workDirSuffix), true); - - // strictEqual( - // await runCLI(['--listPlans'], [keys.enter]).promise, - // 'i Essential: 1\n' - // ); - - // const resultDeploy = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); - - // strictEqual(await deployed(workDirSuffix), true); - - // return resultDeploy; - // }); - // --delete it('Should be able to delete deployed repository using --delete flag', async () => { const result = await runCLI(['--delete'], [keys.enter, keys.enter]) @@ -311,6 +302,3 @@ describe('Integration CLI (Deploy)', function () { 'i Essential: 2\n' )); }); - -// TODO: Tests to add -// if there is only one log file -> select it (TODO: This must be reviewed in case we use TUI) diff --git a/src/test/confDir.spec.ts b/src/test/confDir.spec.ts deleted file mode 100644 index 93b566d..0000000 --- a/src/test/confDir.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { strictEqual } from 'assert'; -import spawn from 'cross-spawn'; -import { existsSync, mkdirSync, rmdirSync, writeFileSync } from 'fs'; -import { join, resolve } from 'path'; - -describe('CLI Integration with --confDir', () => { - // 1. Setup paths - const customPath = join(process.cwd(), 'test-custom-config'); - const configFilePath = join(customPath, 'config.ini'); - - // Path to the compiled CLI entry point - const cliPath = resolve(__dirname, '../index.js'); - - // 2. Mock Data - const fakeToken = 'test-token-12345'; - const iniContent = `token=${fakeToken}`; - - before(() => { - if (!existsSync(customPath)) { - mkdirSync(customPath); - } - writeFileSync(configFilePath, iniContent, 'utf8'); - }); - - after(() => { - if (existsSync(customPath)) { - rmdirSync(customPath, { recursive: true }); - } - }); - - it('should load config from a custom directory passed via --confDir', done => { - const process = spawn('node', [cliPath, '--confDir', customPath], { - stdio: 'pipe' - }); - - let output = ''; - - // Fix: Explicitly type 'data' as Buffer so .toString() is safe - process.stdout?.on('data', (data: Buffer) => { - output += data.toString(); - }); - - process.stderr?.on('data', (data: Buffer) => { - output += data.toString(); - }); - - // Fix: Removed unused 'code' parameter - process.on('close', () => { - const promptForLogin = - output.includes('Select an option') || output.includes('Login'); - - strictEqual( - promptForLogin, - false, - 'The CLI ignored the config file and prompted for login.' - ); - - done(); - }); - }); -}); From 7c261fb53290e9f8ae88abb9ca034f373ce76369 Mon Sep 17 00:00:00 2001 From: nothingbutsyntaxerror Date: Sat, 20 Dec 2025 14:41:23 +0000 Subject: [PATCH 4/7] cli-test: cleaning up comments --- src/test/cli.integration.spec.ts | 633 ++++++++++++++++--------------- 1 file changed, 337 insertions(+), 296 deletions(-) diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index c5bc236..1fbdc68 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -1,304 +1,345 @@ import { fail, notStrictEqual, ok, strictEqual } from 'assert'; -import { writeFileSync } from 'fs'; // <--- ADDED THIS +import { writeFileSync } from 'fs'; import { join } from 'path'; import { load } from '../config'; import { - checkEnvVars, - clearCache, - createTmpDirectory, - deleted, - deployed, - keys, - runCLI + checkEnvVars, + clearCache, + createTmpDirectory, + deleted, + deployed, + keys, + runCLI } from './cli'; describe('Integration CLI (Deploy)', function () { - this.timeout(2000000); - - const url = 'https://github.com/metacall/examples'; - const addRepoSuffix = 'metacall-examples'; - - const workDirSuffix = 'time-app-web'; - const filePath = join( - process.cwd(), - 'src', - 'test', - 'resources', - 'integration', - 'time-app-web' - ); - - // --email & --password - it('Should be able to login using --email & --password flag', async function () { - await clearCache(); - const { email, password } = checkEnvVars(); - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [ - `--email=${email}`, - `--password=${password}`, - `--workdir=${workdir}` - ], - [keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --token - it('Should be able to login using --token flag', async function () { - const file = await load(); - const token = file.token || ''; - - notStrictEqual(token, ''); - - await clearCache(); - - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [`--token=${token}`, `--workdir=${workdir}`], - [keys.enter, keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --confDir - it('Should be able to login using --confDir flag', async function () { - const file = await load(); - const token = file.token || ''; - - notStrictEqual(token, ''); - - await clearCache(); - - const confDir = await createTmpDirectory(); - const configPath = join(confDir, 'config.ini'); - writeFileSync(configPath, `token=${token}`, 'utf8'); - - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [`--confDir=${confDir}`, `--workdir=${workdir}`], - [keys.enter, keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --help - it('Should be able to print help guide using --help flag', async () => { - const result = await runCLI(['--help'], [keys.enter]).promise; - - ok(String(result).includes('Official CLI for metacall-deploy\n')); - }); - - // --unknown-flags - it('Should be able to handle unknown flag', async () => { - try { - const result = await runCLI(['--yeet'], [keys.enter]).promise; - - fail( - `The CLI passed without errors and it should have failed. Result: ${String( - result - )}` - ); - } catch (err) { - ok(String(err) === '! --yeet does not exist as a valid command.\n'); - } - }); - - // --addrepo - it('Should be able to deploy repository using --addrepo flag', async () => { - const result = await runCLI( - [`--addrepo=${url}`], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes('i Deploying...\n')); - - strictEqual(await deployed(addRepoSuffix), true); - return result; - }); - - // --inspect with invalid parameter - it('Should fail --inspect command with proper output', async () => { - try { - const result = await runCLI(['--inspect', 'yeet'], [keys.enter]) - .promise; - fail( - `The CLI passed without errors and it should fail. Result: ${String( - result - )}` - ); - } catch (error) { - strictEqual( - String(error), - 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' - ); - } - }); - - // --inspect without parameter - it('Should fail --inspect command with proper output', async () => - notStrictEqual( - await runCLI(['--inspect'], [keys.enter]).promise, - 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' - )); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(addRepoSuffix), true); - - return result; - }); - - // --workdir & --projectName - it('Should be able to deploy repository using --workdir & --projectName flag', async () => { - const result = await runCLI( - [`--workdir=${filePath}`, `--projectName=${workDirSuffix}`], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${filePath}...\n`)); - - strictEqual(await deployed(workDirSuffix), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // with env vars - it('Should be able to deploy repository using --addrepo flag with environment vars', async () => { - const result = await runCLI( - [`--addrepo=${url}`], - [ - keys.enter, - 'y', - keys.enter, - 'PORT=1000, ENV=PROD', - keys.enter, - keys.kill - ] - ).promise; - - ok(String(result).includes('i Deploying...\n')); - - strictEqual(await deployed(addRepoSuffix), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // test .env file - it('Should be able to deploy repository using --workdir & getting the .env file', async () => { - const projectPath = join( - process.cwd(), - 'src', - 'test', - 'resources', - 'integration', - 'env' - ); - const result = await runCLI( - [`--workdir=${projectPath}`], - [keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${projectPath}...\n`)); - - strictEqual(await deployed('env'), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted('env'), true); - - return result; - }); - - // --workdir & --projectName & --plan - it('Should be able to deploy repository using --workdir & --plan flag', async () => { - const result = await runCLI( - [ - `--workdir=${filePath}`, - '--projectName=time-app-web', - '--plan=Essential' - ], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${filePath}...\n`)); - - strictEqual(await deployed(workDirSuffix), true); - - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // --listPlans - it("Should be able to list all the plans in user's account", async () => - strictEqual( - await runCLI(['--listPlans'], [keys.enter]).promise, - 'i Essential: 2\n' - )); + this.timeout(2000000); + + const url = 'https://github.com/metacall/examples'; + const addRepoSuffix = 'metacall-examples'; + + const workDirSuffix = 'time-app-web'; + const filePath = join( + process.cwd(), + 'src', + 'test', + 'resources', + 'integration', + 'time-app-web' + ); + + // --email & --password + it('Should be able to login using --email & --password flag', async function () { + await clearCache(); + const { email, password } = checkEnvVars(); + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [ + `--email=${email}`, + `--password=${password}`, + `--workdir=${workdir}` + ], + [keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --token + it('Should be able to login using --token flag', async function () { + const file = await load(); + const token = file.token || ''; + + notStrictEqual(token, ''); + + await clearCache(); + + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [`--token=${token}`, `--workdir=${workdir}`], + [keys.enter, keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --confDir + it('Should be able to login using --confDir flag', async function () { + const file = await load(); + const token = file.token || ''; + + notStrictEqual(token, ''); + + await clearCache(); + + const confDir = await createTmpDirectory(); + const configPath = join(confDir, 'config.ini'); + writeFileSync(configPath, `token=${token}`, 'utf8'); + + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [`--confDir=${confDir}`, `--workdir=${workdir}`], + [keys.enter, keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --help + it('Should be able to print help guide using --help flag', async () => { + const result = await runCLI(['--help'], [keys.enter]).promise; + + ok(String(result).includes('Official CLI for metacall-deploy\n')); + }); + + // --unknown-flags + it('Should be able to handle unknown flag', async () => { + try { + const result = await runCLI(['--yeet'], [keys.enter]).promise; + + fail( + `The CLI passed without errors and it should have failed. Result: ${String( + result + )}` + ); + } catch (err) { + ok(String(err) === '! --yeet does not exist as a valid command.\n'); + } + }); + + // --addrepo + it('Should be able to deploy repository using --addrepo flag', async () => { + const result = await runCLI( + [`--addrepo=${url}`], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes('i Deploying...\n')); + + strictEqual(await deployed(addRepoSuffix), true); + return result; + }); + + // --inspect with invalid parameter + it('Should fail --inspect command with proper output', async () => { + try { + const result = await runCLI(['--inspect', 'yeet'], [keys.enter]) + .promise; + fail( + `The CLI passed without errors and it should fail. Result: ${String( + result + )}` + ); + } catch (error) { + strictEqual( + String(error), + 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' + ); + } + }); + + // --inspect without parameter + it('Should fail --inspect command with proper output', async () => + notStrictEqual( + await runCLI(['--inspect'], [keys.enter]).promise, + 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' + )); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(addRepoSuffix), true); + + return result; + }); + + // --workdir & --projectName + it('Should be able to deploy repository using --workdir & --projectName flag', async () => { + const result = await runCLI( + [`--workdir=${filePath}`, `--projectName=${workDirSuffix}`], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${filePath}...\n`)); + + strictEqual(await deployed(workDirSuffix), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // with env vars + it('Should be able to deploy repository using --addrepo flag with environment vars', async () => { + const result = await runCLI( + [`--addrepo=${url}`], + [ + keys.enter, + 'y', + keys.enter, + 'PORT=1000, ENV=PROD', + keys.enter, + keys.kill + ] + ).promise; + + ok(String(result).includes('i Deploying...\n')); + + strictEqual(await deployed(addRepoSuffix), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // test .env file + it('Should be able to deploy repository using --workdir & getting the .env file', async () => { + const projectPath = join( + process.cwd(), + 'src', + 'test', + 'resources', + 'integration', + 'env' + ); + const result = await runCLI( + [`--workdir=${projectPath}`], + [keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${projectPath}...\n`)); + + strictEqual(await deployed('env'), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted('env'), true); + + return result; + }); + + // --workdir & --projectName & --plan + it('Should be able to deploy repository using --workdir & --plan flag', async () => { + const result = await runCLI( + [ + `--workdir=${filePath}`, + '--projectName=time-app-web', + '--plan=Essential' + ], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${filePath}...\n`)); + + strictEqual(await deployed(workDirSuffix), true); + + return result; + }); + + // TODO: + // --force + // it('Should be able to deploy forcefully using --force flag', async () => { + // const resultDel = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential', + // '--force' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDel).includes('Trying to deploy forcefully!')); + + // strictEqual(await deleted(workDirSuffix), true); + + // strictEqual( + // await runCLI(['--listPlans'], [keys.enter]).promise, + // 'i Essential: 1\n' + // ); + + // const resultDeploy = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); + + // strictEqual(await deployed(workDirSuffix), true); + + // return resultDeploy; + // }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // --listPlans + it("Should be able to list all the plans in user's account", async () => + strictEqual( + await runCLI(['--listPlans'], [keys.enter]).promise, + 'i Essential: 2\n' + )); }); + +// TODO: Tests to add +// if there is only one log file -> select it (TODO: This must be reviewed in case we use TUI) \ No newline at end of file From 8a2edd72ab804bb06a4e2ad3e913eb77937efd48 Mon Sep 17 00:00:00 2001 From: nothingbutsyntaxerror Date: Sat, 20 Dec 2025 14:48:55 +0000 Subject: [PATCH 5/7] cli-test: fixing linting issues --- src/test/cli.integration.spec.ts | 668 +++++++++++++++---------------- 1 file changed, 334 insertions(+), 334 deletions(-) diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index 1fbdc68..df71067 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -3,343 +3,343 @@ import { writeFileSync } from 'fs'; import { join } from 'path'; import { load } from '../config'; import { - checkEnvVars, - clearCache, - createTmpDirectory, - deleted, - deployed, - keys, - runCLI + checkEnvVars, + clearCache, + createTmpDirectory, + deleted, + deployed, + keys, + runCLI } from './cli'; describe('Integration CLI (Deploy)', function () { - this.timeout(2000000); - - const url = 'https://github.com/metacall/examples'; - const addRepoSuffix = 'metacall-examples'; - - const workDirSuffix = 'time-app-web'; - const filePath = join( - process.cwd(), - 'src', - 'test', - 'resources', - 'integration', - 'time-app-web' - ); - - // --email & --password - it('Should be able to login using --email & --password flag', async function () { - await clearCache(); - const { email, password } = checkEnvVars(); - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [ - `--email=${email}`, - `--password=${password}`, - `--workdir=${workdir}` - ], - [keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --token - it('Should be able to login using --token flag', async function () { - const file = await load(); - const token = file.token || ''; - - notStrictEqual(token, ''); - - await clearCache(); - - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [`--token=${token}`, `--workdir=${workdir}`], - [keys.enter, keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --confDir - it('Should be able to login using --confDir flag', async function () { - const file = await load(); - const token = file.token || ''; - - notStrictEqual(token, ''); - - await clearCache(); - - const confDir = await createTmpDirectory(); - const configPath = join(confDir, 'config.ini'); - writeFileSync(configPath, `token=${token}`, 'utf8'); - - const workdir = await createTmpDirectory(); - - try { - await runCLI( - [`--confDir=${confDir}`, `--workdir=${workdir}`], - [keys.enter, keys.enter] - ).promise; - } catch (err) { - strictEqual( - err, - `X The directory you specified (${workdir}) is empty.\n` - ); - } - }); - - // --help - it('Should be able to print help guide using --help flag', async () => { - const result = await runCLI(['--help'], [keys.enter]).promise; - - ok(String(result).includes('Official CLI for metacall-deploy\n')); - }); - - // --unknown-flags - it('Should be able to handle unknown flag', async () => { - try { - const result = await runCLI(['--yeet'], [keys.enter]).promise; - - fail( - `The CLI passed without errors and it should have failed. Result: ${String( - result - )}` - ); - } catch (err) { - ok(String(err) === '! --yeet does not exist as a valid command.\n'); - } - }); - - // --addrepo - it('Should be able to deploy repository using --addrepo flag', async () => { - const result = await runCLI( - [`--addrepo=${url}`], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes('i Deploying...\n')); - - strictEqual(await deployed(addRepoSuffix), true); - return result; - }); - - // --inspect with invalid parameter - it('Should fail --inspect command with proper output', async () => { - try { - const result = await runCLI(['--inspect', 'yeet'], [keys.enter]) - .promise; - fail( - `The CLI passed without errors and it should fail. Result: ${String( - result - )}` - ); - } catch (error) { - strictEqual( - String(error), - 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' - ); - } - }); - - // --inspect without parameter - it('Should fail --inspect command with proper output', async () => - notStrictEqual( - await runCLI(['--inspect'], [keys.enter]).promise, - 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' - )); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(addRepoSuffix), true); - - return result; - }); - - // --workdir & --projectName - it('Should be able to deploy repository using --workdir & --projectName flag', async () => { - const result = await runCLI( - [`--workdir=${filePath}`, `--projectName=${workDirSuffix}`], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${filePath}...\n`)); - - strictEqual(await deployed(workDirSuffix), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // with env vars - it('Should be able to deploy repository using --addrepo flag with environment vars', async () => { - const result = await runCLI( - [`--addrepo=${url}`], - [ - keys.enter, - 'y', - keys.enter, - 'PORT=1000, ENV=PROD', - keys.enter, - keys.kill - ] - ).promise; - - ok(String(result).includes('i Deploying...\n')); - - strictEqual(await deployed(addRepoSuffix), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // test .env file - it('Should be able to deploy repository using --workdir & getting the .env file', async () => { - const projectPath = join( - process.cwd(), - 'src', - 'test', - 'resources', - 'integration', - 'env' - ); - const result = await runCLI( - [`--workdir=${projectPath}`], - [keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${projectPath}...\n`)); - - strictEqual(await deployed('env'), true); - return result; - }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted('env'), true); - - return result; - }); - - // --workdir & --projectName & --plan - it('Should be able to deploy repository using --workdir & --plan flag', async () => { - const result = await runCLI( - [ - `--workdir=${filePath}`, - '--projectName=time-app-web', - '--plan=Essential' - ], - [keys.enter, 'n', keys.enter, keys.kill] - ).promise; - - ok(String(result).includes(`i Deploying ${filePath}...\n`)); - - strictEqual(await deployed(workDirSuffix), true); - - return result; - }); - - // TODO: - // --force - // it('Should be able to deploy forcefully using --force flag', async () => { - // const resultDel = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential', - // '--force' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDel).includes('Trying to deploy forcefully!')); - - // strictEqual(await deleted(workDirSuffix), true); - - // strictEqual( - // await runCLI(['--listPlans'], [keys.enter]).promise, - // 'i Essential: 1\n' - // ); - - // const resultDeploy = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); - - // strictEqual(await deployed(workDirSuffix), true); - - // return resultDeploy; - // }); - - // --delete - it('Should be able to delete deployed repository using --delete flag', async () => { - const result = await runCLI(['--delete'], [keys.enter, keys.enter]) - .promise; - - ok(String(result).includes('i Deploy Delete Succeed\n')); - - strictEqual(await deleted(workDirSuffix), true); - - return result; - }); - - // --listPlans - it("Should be able to list all the plans in user's account", async () => - strictEqual( - await runCLI(['--listPlans'], [keys.enter]).promise, - 'i Essential: 2\n' - )); + this.timeout(2000000); + + const url = 'https://github.com/metacall/examples'; + const addRepoSuffix = 'metacall-examples'; + + const workDirSuffix = 'time-app-web'; + const filePath = join( + process.cwd(), + 'src', + 'test', + 'resources', + 'integration', + 'time-app-web' + ); + + // --email & --password + it('Should be able to login using --email & --password flag', async function () { + await clearCache(); + const { email, password } = checkEnvVars(); + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [ + `--email=${email}`, + `--password=${password}`, + `--workdir=${workdir}` + ], + [keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --token + it('Should be able to login using --token flag', async function () { + const file = await load(); + const token = file.token || ''; + + notStrictEqual(token, ''); + + await clearCache(); + + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [`--token=${token}`, `--workdir=${workdir}`], + [keys.enter, keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --confDir + it('Should be able to login using --confDir flag', async function () { + const file = await load(); + const token = file.token || ''; + + notStrictEqual(token, ''); + + await clearCache(); + + const confDir = await createTmpDirectory(); + const configPath = join(confDir, 'config.ini'); + writeFileSync(configPath, `token=${token}`, 'utf8'); + + const workdir = await createTmpDirectory(); + + try { + await runCLI( + [`--confDir=${confDir}`, `--workdir=${workdir}`], + [keys.enter, keys.enter] + ).promise; + } catch (err) { + strictEqual( + err, + `X The directory you specified (${workdir}) is empty.\n` + ); + } + }); + + // --help + it('Should be able to print help guide using --help flag', async () => { + const result = await runCLI(['--help'], [keys.enter]).promise; + + ok(String(result).includes('Official CLI for metacall-deploy\n')); + }); + + // --unknown-flags + it('Should be able to handle unknown flag', async () => { + try { + const result = await runCLI(['--yeet'], [keys.enter]).promise; + + fail( + `The CLI passed without errors and it should have failed. Result: ${String( + result + )}` + ); + } catch (err) { + ok(String(err) === '! --yeet does not exist as a valid command.\n'); + } + }); + + // --addrepo + it('Should be able to deploy repository using --addrepo flag', async () => { + const result = await runCLI( + [`--addrepo=${url}`], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes('i Deploying...\n')); + + strictEqual(await deployed(addRepoSuffix), true); + return result; + }); + + // --inspect with invalid parameter + it('Should fail --inspect command with proper output', async () => { + try { + const result = await runCLI(['--inspect', 'yeet'], [keys.enter]) + .promise; + fail( + `The CLI passed without errors and it should fail. Result: ${String( + result + )}` + ); + } catch (error) { + strictEqual( + String(error), + 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' + ); + } + }); + + // --inspect without parameter + it('Should fail --inspect command with proper output', async () => + notStrictEqual( + await runCLI(['--inspect'], [keys.enter]).promise, + 'X Invalid format passed to inspect, valid formats are: Table, Raw, OpenAPIv3\n' + )); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(addRepoSuffix), true); + + return result; + }); + + // --workdir & --projectName + it('Should be able to deploy repository using --workdir & --projectName flag', async () => { + const result = await runCLI( + [`--workdir=${filePath}`, `--projectName=${workDirSuffix}`], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${filePath}...\n`)); + + strictEqual(await deployed(workDirSuffix), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // with env vars + it('Should be able to deploy repository using --addrepo flag with environment vars', async () => { + const result = await runCLI( + [`--addrepo=${url}`], + [ + keys.enter, + 'y', + keys.enter, + 'PORT=1000, ENV=PROD', + keys.enter, + keys.kill + ] + ).promise; + + ok(String(result).includes('i Deploying...\n')); + + strictEqual(await deployed(addRepoSuffix), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // test .env file + it('Should be able to deploy repository using --workdir & getting the .env file', async () => { + const projectPath = join( + process.cwd(), + 'src', + 'test', + 'resources', + 'integration', + 'env' + ); + const result = await runCLI( + [`--workdir=${projectPath}`], + [keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${projectPath}...\n`)); + + strictEqual(await deployed('env'), true); + return result; + }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted('env'), true); + + return result; + }); + + // --workdir & --projectName & --plan + it('Should be able to deploy repository using --workdir & --plan flag', async () => { + const result = await runCLI( + [ + `--workdir=${filePath}`, + '--projectName=time-app-web', + '--plan=Essential' + ], + [keys.enter, 'n', keys.enter, keys.kill] + ).promise; + + ok(String(result).includes(`i Deploying ${filePath}...\n`)); + + strictEqual(await deployed(workDirSuffix), true); + + return result; + }); + + // TODO: + // --force + // it('Should be able to deploy forcefully using --force flag', async () => { + // const resultDel = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential', + // '--force' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDel).includes('Trying to deploy forcefully!')); + + // strictEqual(await deleted(workDirSuffix), true); + + // strictEqual( + // await runCLI(['--listPlans'], [keys.enter]).promise, + // 'i Essential: 1\n' + // ); + + // const resultDeploy = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); + + // strictEqual(await deployed(workDirSuffix), true); + + // return resultDeploy; + // }); + + // --delete + it('Should be able to delete deployed repository using --delete flag', async () => { + const result = await runCLI(['--delete'], [keys.enter, keys.enter]) + .promise; + + ok(String(result).includes('i Deploy Delete Succeed\n')); + + strictEqual(await deleted(workDirSuffix), true); + + return result; + }); + + // --listPlans + it("Should be able to list all the plans in user's account", async () => + strictEqual( + await runCLI(['--listPlans'], [keys.enter]).promise, + 'i Essential: 2\n' + )); }); // TODO: Tests to add -// if there is only one log file -> select it (TODO: This must be reviewed in case we use TUI) \ No newline at end of file +// if there is only one log file -> select it (TODO: This must be reviewed in case we use TUI) From 087faac643b514fd3c85feb2289c2be70d9dee7a Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Fri, 26 Dec 2025 07:39:23 -0500 Subject: [PATCH 6/7] Update cli.integration.spec.ts --- src/test/cli.integration.spec.ts | 66 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index df71067..2a3ea40 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -286,39 +286,39 @@ describe('Integration CLI (Deploy)', function () { // TODO: // --force // it('Should be able to deploy forcefully using --force flag', async () => { - // const resultDel = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential', - // '--force' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDel).includes('Trying to deploy forcefully!')); - - // strictEqual(await deleted(workDirSuffix), true); - - // strictEqual( - // await runCLI(['--listPlans'], [keys.enter]).promise, - // 'i Essential: 1\n' - // ); - - // const resultDeploy = await runCLI( - // [ - // `--workdir=${filePath}`, - // `--projectName=${workDirSuffix}`, - // '--plan=Essential' - // ], - // [keys.enter, keys.kill] - // ).promise; - - // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); - - // strictEqual(await deployed(workDirSuffix), true); - - // return resultDeploy; + // const resultDel = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential', + // '--force' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDel).includes('Trying to deploy forcefully!')); + + // strictEqual(await deleted(workDirSuffix), true); + + // strictEqual( + // await runCLI(['--listPlans'], [keys.enter]).promise, + // 'i Essential: 1\n' + // ); + + // const resultDeploy = await runCLI( + // [ + // `--workdir=${filePath}`, + // `--projectName=${workDirSuffix}`, + // '--plan=Essential' + // ], + // [keys.enter, keys.kill] + // ).promise; + + // ok(String(resultDeploy).includes(`i Deploying ${filePath}...\n`)); + + // strictEqual(await deployed(workDirSuffix), true); + + // return resultDeploy; // }); // --delete From 05ff0680f06e7d382b8b39c484d8a1dd4330a99b Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia <7854099+viferga@users.noreply.github.com> Date: Fri, 26 Dec 2025 07:41:33 -0500 Subject: [PATCH 7/7] Update cli.integration.spec.ts --- src/test/cli.integration.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/cli.integration.spec.ts b/src/test/cli.integration.spec.ts index 2a3ea40..fbe748b 100644 --- a/src/test/cli.integration.spec.ts +++ b/src/test/cli.integration.spec.ts @@ -1,5 +1,5 @@ import { fail, notStrictEqual, ok, strictEqual } from 'assert'; -import { writeFileSync } from 'fs'; +import { writeFile } from 'fs/promises'; import { join } from 'path'; import { load } from '../config'; import { @@ -86,7 +86,7 @@ describe('Integration CLI (Deploy)', function () { const confDir = await createTmpDirectory(); const configPath = join(confDir, 'config.ini'); - writeFileSync(configPath, `token=${token}`, 'utf8'); + await writeFile(configPath, `token=${token}`, 'utf8'); const workdir = await createTmpDirectory();