Skip to content

test: Add unit test for projects/app/src/web/core/chat/context/useChatStore.ts #4812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 21 additions & 6 deletions projects/app/src/web/core/chat/context/useChatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,25 @@ type State = {
const createCustomStorage = () => {
const sessionKeys = ['source', 'chatId', 'appId'];

// 从 URL 中获取 appId 作为存储键的一部分
const getStorageKey = (name: string) => {
let appId = '';
if (typeof window !== 'undefined') {
const urlParams = new URLSearchParams(window.location.search);
appId = urlParams.get('appId') || '';
}
return appId ? `${name}_${appId}` : name;
};

return {
getItem: (name: string) => {
const sessionData = JSON.parse(sessionStorage.getItem(name) || '{}');
const localData = JSON.parse(localStorage.getItem(name) || '{}');
const storageKey = getStorageKey(name);
const sessionData = JSON.parse(sessionStorage.getItem(storageKey) || '{}');
const localData = JSON.parse(localStorage.getItem(storageKey) || '{}');
return JSON.stringify({ ...localData, ...sessionData });
},
setItem: (name: string, value: string) => {
const storageKey = getStorageKey(name);
const data = JSON.parse(value);

// 分离 session 和 local 数据
Expand All @@ -42,15 +54,16 @@ const createCustomStorage = () => {

// 分别存储
if (Object.keys(sessionData).length > 0) {
sessionStorage.setItem(name, JSON.stringify({ state: sessionData, version: 0 }));
sessionStorage.setItem(storageKey, JSON.stringify({ state: sessionData, version: 0 }));
}
if (Object.keys(localData).length > 0) {
localStorage.setItem(name, JSON.stringify({ state: localData, version: 0 }));
localStorage.setItem(storageKey, JSON.stringify({ state: localData, version: 0 }));
}
},
removeItem: (name: string) => {
sessionStorage.removeItem(name);
localStorage.removeItem(name);
const storageKey = getStorageKey(name);
sessionStorage.removeItem(storageKey);
localStorage.removeItem(storageKey);
}
};
};
Expand Down Expand Up @@ -125,3 +138,5 @@ export const useChatStore = create<State>()(
)
)
);

export { createCustomStorage };
170 changes: 170 additions & 0 deletions test/cases/web/core/chat/context/useChatStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { useChatStore, createCustomStorage } from '@/web/core/chat/context/useChatStore';
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { getNanoid } from '@fastgpt/global/common/string/tools';

vi.mock('@fastgpt/global/common/string/tools', () => ({
getNanoid: vi.fn().mockReturnValue('test-nanoid')
}));

const mockStorage = () => {
const store = new Map();
return {
getItem: (key: string) => store.get(key) || null,
setItem: (key: string, value: string) => store.set(key, value),
clear: () => store.clear(),
removeItem: (key: string) => store.delete(key)
};
};

const mockWindow = () => {
const windowMock = {
location: {
search: '?appId=test123'
},
sessionStorage: mockStorage(),
localStorage: mockStorage()
};

vi.stubGlobal('window', windowMock);
global.sessionStorage = windowMock.sessionStorage;
global.localStorage = windowMock.localStorage;
};

beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
mockWindow();
const store = useChatStore.getState();
store.source = undefined;
store.appId = '';
store.chatId = '';
store.lastChatId = '';
store.lastChatAppId = '';
store.outLinkAuthData = {};
sessionStorage.clear();
localStorage.clear();
});

describe('useChatStore', () => {
it('should set source and restore last chat if available', () => {
const store = useChatStore.getState();
store.lastChatAppId = 'app123';
store.lastChatId = `${ChatSourceEnum.share}-chat123`;

store.setSource(ChatSourceEnum.share);

const updatedStore = useChatStore.getState();
expect(updatedStore.source).toBe(ChatSourceEnum.share);
expect(updatedStore.chatId).toBe('chat123');
expect(updatedStore.lastChatAppId).toBe('app123');
});

it('should generate new chatId when source changes', () => {
const store = useChatStore.getState();
store.source = ChatSourceEnum.share;
store.chatId = 'old-id';

store.setSource(ChatSourceEnum.api);
const updatedStore = useChatStore.getState();

expect(updatedStore.chatId).toBe('test-nanoid');
expect(updatedStore.chatId).not.toBe('old-id');
});

it('should set appId and lastChatAppId', () => {
const store = useChatStore.getState();
store.setAppId('test123');
const updatedStore = useChatStore.getState();

expect(updatedStore.appId).toBe('test123');
expect(updatedStore.lastChatAppId).toBe('test123');
});

it('should not set empty appId', () => {
const store = useChatStore.getState();
store.setAppId('test123');
store.setAppId('');
const updatedStore = useChatStore.getState();

expect(updatedStore.appId).toBe('test123');
expect(updatedStore.lastChatAppId).toBe('test123');
});

it('should set chatId and lastChatId', () => {
const store = useChatStore.getState();
store.source = ChatSourceEnum.share;
store.setChatId('test-id');
const updatedStore = useChatStore.getState();

expect(updatedStore.chatId).toBe('test-id');
expect(updatedStore.lastChatId).toBe(`${ChatSourceEnum.share}-test-id`);
});

it('should generate new chatId if none provided', () => {
const store = useChatStore.getState();
store.source = ChatSourceEnum.share;
store.setChatId();
const updatedStore = useChatStore.getState();

expect(updatedStore.chatId).toBe('test-nanoid');
});

it('should set outLinkAuthData', () => {
const store = useChatStore.getState();
const authData = { apikey: 'test-key' };
store.setOutLinkAuthData(authData);
const updatedStore = useChatStore.getState();

expect(updatedStore.outLinkAuthData).toEqual(authData);
});
});

describe('createCustomStorage', () => {
it('should create storage with appId in key', () => {
const storage = createCustomStorage();
const testData = {
state: {
source: ChatSourceEnum.share,
chatId: '123',
appId: 'app123',
lastChatId: 'last123',
lastChatAppId: 'lastApp123'
},
version: 0
};

storage.setItem('test', JSON.stringify(testData));

const sessionResult = JSON.parse(sessionStorage.getItem('test_test123') || '{}');
const localResult = JSON.parse(localStorage.getItem('test_test123') || '{}');

expect(sessionResult.state).toEqual({
source: ChatSourceEnum.share,
chatId: '123',
appId: 'app123'
});

expect(localResult.state).toEqual({
lastChatId: 'last123',
lastChatAppId: 'lastApp123'
});
});

it('should remove items from both storages', () => {
const storage = createCustomStorage();
const testData = {
state: {
source: ChatSourceEnum.share,
chatId: '123'
},
version: 0
};

storage.setItem('test', JSON.stringify(testData));
storage.removeItem('test');

expect(sessionStorage.getItem('test_test123')).toBeNull();
expect(localStorage.getItem('test_test123')).toBeNull();
});
});
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.