Download packages/sdk/src/agent.integration.test.ts from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 5.62 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/sdk/src/agent.integration.test.ts
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/sdk/src/agent.integration.test.ts
-
curl -L -o agent.integration.test.ts https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/sdk/src/agent.integration.test.ts
5.62 kB
| /** | |
| * @license | |
| * Copyright 2026 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | |
| import { GeminiCliAgent } from './agent.js'; | |
| import * as path from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| // Set this to true locally when you need to update snapshots | |
| const RECORD_MODE = process.env['RECORD_NEW_RESPONSES'] === 'true'; | |
| const getGoldenPath = (name: string) => | |
| path.resolve(__dirname, '../test-data', `${name}.json`); | |
| describe('GeminiCliAgent Integration', () => { | |
| beforeEach(() => { | |
| vi.stubEnv('GEMINI_API_KEY', 'test-api-key'); | |
| }); | |
| afterEach(() => { | |
| vi.unstubAllEnvs(); | |
| }); | |
| it('handles static instructions', async () => { | |
| const goldenFile = getGoldenPath('agent-static-instructions'); | |
| const agent = new GeminiCliAgent({ | |
| instructions: 'You are a pirate. Respond in pirate speak.', | |
| model: 'gemini-2.0-flash', | |
| recordResponses: RECORD_MODE ? goldenFile : undefined, | |
| fakeResponses: RECORD_MODE ? undefined : goldenFile, | |
| }); | |
| const session = agent.session(); | |
| expect(session.id).toMatch( | |
| /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, | |
| ); | |
| const events = []; | |
| const stream = session.sendStream('Say hello.'); | |
| for await (const event of stream) { | |
| events.push(event); | |
| } | |
| const textEvents = events.filter((e) => e.type === 'content'); | |
| const responseText = textEvents | |
| .map((e) => ('value' in e && typeof e.value === 'string' ? e.value : '')) | |
| .join(''); | |
| // Expect pirate speak | |
| expect(responseText.toLowerCase()).toMatch(/ahoy|matey|arrr/); | |
| }, 30000); | |
| it('handles dynamic instructions', async () => { | |
| const goldenFile = getGoldenPath('agent-dynamic-instructions'); | |
| let callCount = 0; | |
| const agent = new GeminiCliAgent({ | |
| instructions: (_ctx) => { | |
| callCount++; | |
| return `You are a helpful assistant. The secret number is ${callCount}. Always mention the secret number when asked.`; | |
| }, | |
| model: 'gemini-2.0-flash', | |
| recordResponses: RECORD_MODE ? goldenFile : undefined, | |
| fakeResponses: RECORD_MODE ? undefined : goldenFile, | |
| }); | |
| const session = agent.session(); | |
| // First turn | |
| const events1 = []; | |
| const stream1 = session.sendStream('What is the secret number?'); | |
| for await (const event of stream1) { | |
| events1.push(event); | |
| } | |
| const responseText1 = events1 | |
| .filter((e) => e.type === 'content') | |
| .map((e) => ('value' in e && typeof e.value === 'string' ? e.value : '')) | |
| .join(''); | |
| expect(responseText1).toContain('1'); | |
| // Second turn | |
| const events2 = []; | |
| const stream2 = session.sendStream('What is the secret number now?'); | |
| for await (const event of stream2) { | |
| events2.push(event); | |
| } | |
| const responseText2 = events2 | |
| .filter((e) => e.type === 'content') | |
| .map((e) => ('value' in e && typeof e.value === 'string' ? e.value : '')) | |
| .join(''); | |
| expect(responseText2).toContain('2'); | |
| }, 30000); | |
| it('resumes a session', async () => { | |
| const goldenFile = getGoldenPath('agent-resume-session'); | |
| // Create initial session | |
| const agent = new GeminiCliAgent({ | |
| instructions: 'You are a memory test. Remember the word "BANANA".', | |
| model: 'gemini-2.0-flash', | |
| recordResponses: RECORD_MODE ? goldenFile : undefined, | |
| fakeResponses: RECORD_MODE ? undefined : goldenFile, | |
| }); | |
| const session1 = agent.session({ sessionId: 'resume-test-fixed-id' }); | |
| const sessionId = session1.id; | |
| const stream1 = session1.sendStream('What is the word?'); | |
| for await (const _ of stream1) { | |
| // consume stream | |
| } | |
| // Resume session | |
| // Allow some time for async writes if any | |
| await new Promise((resolve) => setTimeout(resolve, 500)); | |
| const session2 = await agent.resumeSession(sessionId); | |
| expect(session2.id).toBe(sessionId); | |
| const events2 = []; | |
| const stream2 = session2.sendStream('What is the word again?'); | |
| for await (const event of stream2) { | |
| events2.push(event); | |
| } | |
| const responseText = events2 | |
| .filter((e) => e.type === 'content') | |
| .map((e) => ('value' in e && typeof e.value === 'string' ? e.value : '')) | |
| .join(''); | |
| expect(responseText).toContain('BANANA'); | |
| }, 30000); | |
| it('throws on invalid instructions', () => { | |
| // Missing instructions should be fine | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| expect(() => new GeminiCliAgent({} as any).session()).not.toThrow(); | |
| expect(() => | |
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| new GeminiCliAgent({ instructions: 123 as any }).session(), | |
| ).toThrow('Instructions must be a string or a function.'); | |
| }); | |
| it('propagates errors from dynamic instructions', async () => { | |
| const goldenFile = getGoldenPath('agent-static-instructions'); | |
| const agent = new GeminiCliAgent({ | |
| instructions: () => { | |
| throw new Error('Dynamic instruction failure'); | |
| }, | |
| model: 'gemini-2.0-flash', | |
| recordResponses: RECORD_MODE ? goldenFile : undefined, | |
| fakeResponses: RECORD_MODE ? undefined : goldenFile, | |
| }); | |
| const session = agent.session(); | |
| const stream = session.sendStream('Say hello.'); | |
| await expect(async () => { | |
| for await (const _event of stream) { | |
| // Just consume the stream | |
| } | |
| }).rejects.toThrow('Dynamic instruction failure'); | |
| }, 30000); | |
| }); | |