summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/commands/memoryCommand.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/commands/memoryCommand.test.ts')
-rw-r--r--packages/cli/src/ui/commands/memoryCommand.test.ts249
1 files changed, 249 insertions, 0 deletions
diff --git a/packages/cli/src/ui/commands/memoryCommand.test.ts b/packages/cli/src/ui/commands/memoryCommand.test.ts
new file mode 100644
index 00000000..47d098b1
--- /dev/null
+++ b/packages/cli/src/ui/commands/memoryCommand.test.ts
@@ -0,0 +1,249 @@
+/**
+ * @license
+ * Copyright 2025 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { vi, describe, it, expect, beforeEach, Mock } from 'vitest';
+import { memoryCommand } from './memoryCommand.js';
+import { type CommandContext, SlashCommand } from './types.js';
+import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
+import { MessageType } from '../types.js';
+import { getErrorMessage } from '@google/gemini-cli-core';
+
+vi.mock('@google/gemini-cli-core', async (importOriginal) => {
+ const original =
+ await importOriginal<typeof import('@google/gemini-cli-core')>();
+ return {
+ ...original,
+ getErrorMessage: vi.fn((error: unknown) => {
+ if (error instanceof Error) return error.message;
+ return String(error);
+ }),
+ };
+});
+
+describe('memoryCommand', () => {
+ let mockContext: CommandContext;
+
+ const getSubCommand = (name: 'show' | 'add' | 'refresh'): SlashCommand => {
+ const subCommand = memoryCommand.subCommands?.find(
+ (cmd) => cmd.name === name,
+ );
+ if (!subCommand) {
+ throw new Error(`/memory ${name} command not found.`);
+ }
+ return subCommand;
+ };
+
+ describe('/memory show', () => {
+ let showCommand: SlashCommand;
+ let mockGetUserMemory: Mock;
+ let mockGetGeminiMdFileCount: Mock;
+
+ beforeEach(() => {
+ showCommand = getSubCommand('show');
+
+ mockGetUserMemory = vi.fn();
+ mockGetGeminiMdFileCount = vi.fn();
+
+ mockContext = createMockCommandContext({
+ services: {
+ config: {
+ getUserMemory: mockGetUserMemory,
+ getGeminiMdFileCount: mockGetGeminiMdFileCount,
+ },
+ },
+ });
+ });
+
+ it('should display a message if memory is empty', async () => {
+ if (!showCommand.action) throw new Error('Command has no action');
+
+ mockGetUserMemory.mockReturnValue('');
+ mockGetGeminiMdFileCount.mockReturnValue(0);
+
+ await showCommand.action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Memory is currently empty.',
+ },
+ expect.any(Number),
+ );
+ });
+
+ it('should display the memory content and file count if it exists', async () => {
+ if (!showCommand.action) throw new Error('Command has no action');
+
+ const memoryContent = 'This is a test memory.';
+
+ mockGetUserMemory.mockReturnValue(memoryContent);
+ mockGetGeminiMdFileCount.mockReturnValue(1);
+
+ await showCommand.action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: `Current memory content from 1 file(s):\n\n---\n${memoryContent}\n---`,
+ },
+ expect.any(Number),
+ );
+ });
+ });
+
+ describe('/memory add', () => {
+ let addCommand: SlashCommand;
+
+ beforeEach(() => {
+ addCommand = getSubCommand('add');
+ mockContext = createMockCommandContext();
+ });
+
+ it('should return an error message if no arguments are provided', () => {
+ if (!addCommand.action) throw new Error('Command has no action');
+
+ const result = addCommand.action(mockContext, ' ');
+ expect(result).toEqual({
+ type: 'message',
+ messageType: 'error',
+ content: 'Usage: /memory add <text to remember>',
+ });
+
+ expect(mockContext.ui.addItem).not.toHaveBeenCalled();
+ });
+
+ it('should return a tool action and add an info message when arguments are provided', () => {
+ if (!addCommand.action) throw new Error('Command has no action');
+
+ const fact = 'remember this';
+ const result = addCommand.action(mockContext, ` ${fact} `);
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: `Attempting to save to memory: "${fact}"`,
+ },
+ expect.any(Number),
+ );
+
+ expect(result).toEqual({
+ type: 'tool',
+ toolName: 'save_memory',
+ toolArgs: { fact },
+ });
+ });
+ });
+
+ describe('/memory refresh', () => {
+ let refreshCommand: SlashCommand;
+ let mockRefreshMemory: Mock;
+
+ beforeEach(() => {
+ refreshCommand = getSubCommand('refresh');
+ mockRefreshMemory = vi.fn();
+ mockContext = createMockCommandContext({
+ services: {
+ config: {
+ refreshMemory: mockRefreshMemory,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ } as any,
+ },
+ });
+ });
+
+ it('should display success message when memory is refreshed with content', async () => {
+ if (!refreshCommand.action) throw new Error('Command has no action');
+
+ const refreshResult = {
+ memoryContent: 'new memory content',
+ fileCount: 2,
+ };
+ mockRefreshMemory.mockResolvedValue(refreshResult);
+
+ await refreshCommand.action(mockContext, '');
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Refreshing memory from source files...',
+ },
+ expect.any(Number),
+ );
+
+ expect(mockRefreshMemory).toHaveBeenCalledOnce();
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Memory refreshed successfully. Loaded 18 characters from 2 file(s).',
+ },
+ expect.any(Number),
+ );
+ });
+
+ it('should display success message when memory is refreshed with no content', async () => {
+ if (!refreshCommand.action) throw new Error('Command has no action');
+
+ const refreshResult = { memoryContent: '', fileCount: 0 };
+ mockRefreshMemory.mockResolvedValue(refreshResult);
+
+ await refreshCommand.action(mockContext, '');
+
+ expect(mockRefreshMemory).toHaveBeenCalledOnce();
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Memory refreshed successfully. No memory content found.',
+ },
+ expect.any(Number),
+ );
+ });
+
+ it('should display an error message if refreshing fails', async () => {
+ if (!refreshCommand.action) throw new Error('Command has no action');
+
+ const error = new Error('Failed to read memory files.');
+ mockRefreshMemory.mockRejectedValue(error);
+
+ await refreshCommand.action(mockContext, '');
+
+ expect(mockRefreshMemory).toHaveBeenCalledOnce();
+
+ expect(mockContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.ERROR,
+ text: `Error refreshing memory: ${error.message}`,
+ },
+ expect.any(Number),
+ );
+
+ expect(getErrorMessage).toHaveBeenCalledWith(error);
+ });
+
+ it('should not throw if config service is unavailable', async () => {
+ if (!refreshCommand.action) throw new Error('Command has no action');
+
+ const nullConfigContext = createMockCommandContext({
+ services: { config: null },
+ });
+
+ await expect(
+ refreshCommand.action(nullConfigContext, ''),
+ ).resolves.toBeUndefined();
+
+ expect(nullConfigContext.ui.addItem).toHaveBeenCalledWith(
+ {
+ type: MessageType.INFO,
+ text: 'Refreshing memory from source files...',
+ },
+ expect.any(Number),
+ );
+
+ expect(mockRefreshMemory).not.toHaveBeenCalled();
+ });
+ });
+});