diff options
| author | matt korwel <[email protected]> | 2025-06-16 08:27:29 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-06-16 15:27:29 +0000 |
| commit | df938d6ee833ded59f6d12528105e6165ed76a92 (patch) | |
| tree | 712b82037a378b74f5fd68ed5b06f1aae56a88c9 /integration-tests | |
| parent | a600588c200f85e9f3bfe46ef7f836387e7349e5 (diff) | |
Preflight and integration npx (#1096)
Diffstat (limited to 'integration-tests')
| -rw-r--r-- | integration-tests/file-system.test.js | 30 | ||||
| -rw-r--r-- | integration-tests/google_web_search.test.js | 19 | ||||
| -rw-r--r-- | integration-tests/list_directory.test.js | 22 | ||||
| -rw-r--r-- | integration-tests/read_many_files.test.js | 22 | ||||
| -rw-r--r-- | integration-tests/replace.test.js | 22 | ||||
| -rw-r--r-- | integration-tests/run-tests.js | 122 | ||||
| -rw-r--r-- | integration-tests/run_shell_command.test.js | 20 | ||||
| -rw-r--r-- | integration-tests/save_memory.test.js | 22 | ||||
| -rw-r--r-- | integration-tests/test-helper.js | 80 | ||||
| -rw-r--r-- | integration-tests/write_file.test.js | 21 |
10 files changed, 380 insertions, 0 deletions
diff --git a/integration-tests/file-system.test.js b/integration-tests/file-system.test.js new file mode 100644 index 00000000..87e9efe2 --- /dev/null +++ b/integration-tests/file-system.test.js @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { strict as assert } from 'assert'; +import { test } from 'node:test'; +import { TestRig } from './test-helper.js'; + +test('reads a file', (t) => { + const rig = new TestRig(); + rig.setup(t.name); + rig.createFile('test.txt', 'hello world'); + + const output = rig.run(`read the file name test.txt`); + + assert.ok(output.toLowerCase().includes('hello')); +}); + +test('writes a file', (t) => { + const rig = new TestRig(); + rig.setup(t.name); + rig.createFile('test.txt', ''); + + rig.run(`edit test.txt to have a hello world message`); + + const fileContent = rig.readFile('test.txt'); + assert.ok(fileContent.toLowerCase().includes('hello')); +}); diff --git a/integration-tests/google_web_search.test.js b/integration-tests/google_web_search.test.js new file mode 100644 index 00000000..a8968117 --- /dev/null +++ b/integration-tests/google_web_search.test.js @@ -0,0 +1,19 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to search the web', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + + const prompt = `what planet do we live on`; + const result = await rig.run(prompt); + + assert.ok(result.toLowerCase().includes('earth')); +}); diff --git a/integration-tests/list_directory.test.js b/integration-tests/list_directory.test.js new file mode 100644 index 00000000..987a2351 --- /dev/null +++ b/integration-tests/list_directory.test.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to list a directory', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + rig.createFile('file1.txt', 'file 1 content'); + rig.mkdir('subdir'); + + const prompt = `Can you list the files in the current directory`; + const result = await rig.run(prompt); + + assert.ok(result.includes('file1.txt')); + assert.ok(result.includes('subdir')); +}); diff --git a/integration-tests/read_many_files.test.js b/integration-tests/read_many_files.test.js new file mode 100644 index 00000000..7e770036 --- /dev/null +++ b/integration-tests/read_many_files.test.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test.skip('should be able to read multiple files', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + rig.createFile('file1.txt', 'file 1 content'); + rig.createFile('file2.txt', 'file 2 content'); + + const prompt = `Read the files in this directory, list them and print them to the screen`; + const result = await rig.run(prompt); + + assert.ok(result.includes('file 1 content')); + assert.ok(result.includes('file 2 content')); +}); diff --git a/integration-tests/replace.test.js b/integration-tests/replace.test.js new file mode 100644 index 00000000..060aba55 --- /dev/null +++ b/integration-tests/replace.test.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to replace content in a file', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + + const fileName = 'file_to_replace.txt'; + rig.createFile(fileName, 'original content'); + const prompt = `Can you replace 'original' with 'replaced' in the file 'file_to_replace.txt'`; + + await rig.run(prompt); + const newFileContent = rig.readFile(fileName); + assert.strictEqual(newFileContent, 'replaced content'); +}); diff --git a/integration-tests/run-tests.js b/integration-tests/run-tests.js new file mode 100644 index 00000000..d4b9cf49 --- /dev/null +++ b/integration-tests/run-tests.js @@ -0,0 +1,122 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { spawnSync } from 'child_process'; +import { spawn } from 'child_process'; +import { mkdirSync, rmSync, createWriteStream } from 'fs'; +import { join, dirname, basename } from 'path'; +import { fileURLToPath } from 'url'; +import { glob } from 'glob'; + +async function main() { + const __dirname = dirname(fileURLToPath(import.meta.url)); + const rootDir = join(__dirname, '..'); + const integrationTestsDir = join(rootDir, '.integration-tests'); + + if (process.env.GEMINI_SANDBOX === 'docker' && !process.env.IS_DOCKER) { + console.log('Building sandbox for Docker...'); + const buildResult = spawnSync('npm', ['run', 'build:all'], { + stdio: 'inherit', + }); + if (buildResult.status !== 0) { + console.error('Sandbox build failed.'); + process.exit(1); + } + } + + const runId = `${Date.now()}`; + const runDir = join(integrationTestsDir, runId); + + mkdirSync(runDir, { recursive: true }); + + const args = process.argv.slice(2); + const keepOutput = + process.env.KEEP_OUTPUT === 'true' || args.includes('--keep-output'); + if (keepOutput) { + const keepOutputIndex = args.indexOf('--keep-output'); + if (keepOutputIndex > -1) { + args.splice(keepOutputIndex, 1); + } + console.log(`Keeping output for test run in: ${runDir}`); + } + + const verbose = args.includes('--verbose'); + if (verbose) { + const verboseIndex = args.indexOf('--verbose'); + if (verboseIndex > -1) { + args.splice(verboseIndex, 1); + } + } + + const testPatterns = + args.length > 0 + ? args.map((arg) => `integration-tests/${arg}.test.js`) + : ['integration-tests/*.test.js']; + const testFiles = glob.sync(testPatterns, { cwd: rootDir, absolute: true }); + + for (const testFile of testFiles) { + const testFileName = basename(testFile); + console.log(`\tFound test file: ${testFileName}`); + } + + let allTestsPassed = true; + + for (const testFile of testFiles) { + const testFileName = basename(testFile); + const testFileDir = join(runDir, testFileName); + mkdirSync(testFileDir, { recursive: true }); + + console.log( + `------------- Running test file: ${testFileName} ------------------------------`, + ); + + const child = spawn('node', ['--test', testFile], { + stdio: 'pipe', + env: { + ...process.env, + INTEGRATION_TEST_FILE_DIR: testFileDir, + KEEP_OUTPUT: keepOutput.toString(), + TEST_FILE_NAME: testFileName, + }, + }); + + if (verbose) { + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); + } + + if (keepOutput) { + const outputFile = join(testFileDir, 'output.log'); + const outputStream = createWriteStream(outputFile); + child.stdout.pipe(outputStream); + child.stderr.pipe(outputStream); + console.log(`Output for ${testFileName} written to: ${outputFile}`); + } else if (!verbose) { + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); + } + + const exitCode = await new Promise((resolve) => { + child.on('close', resolve); + }); + + if (exitCode !== 0) { + console.error(`Test file failed: ${testFileName}`); + allTestsPassed = false; + } + } + + if (!keepOutput) { + rmSync(runDir, { recursive: true, force: true }); + } + + if (!allTestsPassed) { + console.error('One or more test files failed.'); + process.exit(1); + } +} + +main(); diff --git a/integration-tests/run_shell_command.test.js b/integration-tests/run_shell_command.test.js new file mode 100644 index 00000000..04cc02fa --- /dev/null +++ b/integration-tests/run_shell_command.test.js @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to run a shell command', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + rig.createFile('blah.txt', 'some content'); + + const prompt = `Can you use ls to list the contexts of the current folder`; + const result = await rig.run(prompt); + + assert.ok(result.includes('blah.txt')); +}); diff --git a/integration-tests/save_memory.test.js b/integration-tests/save_memory.test.js new file mode 100644 index 00000000..db2370d7 --- /dev/null +++ b/integration-tests/save_memory.test.js @@ -0,0 +1,22 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to save to memory', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + + const prompt = `remember that my favorite color is blue`; + await rig.run(prompt); + const result = await rig.run( + 'what is my favorite color? tell me that and surround it with $ symbol', + ); + + assert.ok(result.toLowerCase().includes('$blue$')); +}); diff --git a/integration-tests/test-helper.js b/integration-tests/test-helper.js new file mode 100644 index 00000000..acc0035a --- /dev/null +++ b/integration-tests/test-helper.js @@ -0,0 +1,80 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { execSync } from 'child_process'; +import { mkdirSync, writeFileSync, readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { env } from 'process'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +function sanitizeTestName(name) { + return name + .toLowerCase() + .replace(/[^a-z0-9]/g, '-') + .replace(/-+/g, '-'); +} + +export class TestRig { + constructor() { + this.bundlePath = join(__dirname, '..', 'bundle/gemini.js'); + this.testDir = null; + } + + setup(testName) { + this.testName = testName; + const sanitizedName = sanitizeTestName(testName); + this.testDir = join(env.INTEGRATION_TEST_FILE_DIR, sanitizedName); + mkdirSync(this.testDir, { recursive: true }); + } + + createFile(fileName, content) { + const filePath = join(this.testDir, fileName); + writeFileSync(filePath, content); + return filePath; + } + + mkdir(dir) { + mkdirSync(join(this.testDir, dir)); + } + + run(prompt, ...args) { + const output = execSync( + `node ${this.bundlePath} --yolo --prompt "${prompt}" ${args.join(' ')}`, + { + cwd: this.testDir, + encoding: 'utf-8', + }, + ); + + if (env.KEEP_OUTPUT === 'true') { + const testId = `${env.TEST_FILE_NAME.replace( + '.test.js', + '', + )}:${this.testName.replace(/ /g, '-')}`; + console.log(`--- TEST: ${testId} ---`); + console.log(output); + console.log(`--- END TEST: ${testId} ---`); + } + + return output; + } + + readFile(fileName) { + const content = readFileSync(join(this.testDir, fileName), 'utf-8'); + if (env.KEEP_OUTPUT === 'true') { + const testId = `${env.TEST_FILE_NAME.replace( + '.test.js', + '', + )}:${this.testName.replace(/ /g, '-')}`; + console.log(`--- FILE: ${testId}/${fileName} ---`); + console.log(content); + console.log(`--- END FILE: ${testId}/${fileName} ---`); + } + return content; + } +} diff --git a/integration-tests/write_file.test.js b/integration-tests/write_file.test.js new file mode 100644 index 00000000..46a15f3c --- /dev/null +++ b/integration-tests/write_file.test.js @@ -0,0 +1,21 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { test } from 'node:test'; +import { strict as assert } from 'assert'; +import { TestRig } from './test-helper.js'; + +test('should be able to write a file', async (t) => { + const rig = new TestRig(); + rig.setup(t.name); + const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`; + + await rig.run(prompt); + const newFilePath = 'dad.txt'; + + const newFileContent = rig.readFile(newFilePath); + assert.notEqual(newFileContent, ''); +}); |
