Skip to content

Commit a2397da

Browse files
committed
test(copilot): add unit tests for SessionManager and copilot utilities
1 parent 205ab81 commit a2397da

File tree

2 files changed

+286
-0
lines changed

2 files changed

+286
-0
lines changed
+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { expect } from 'chai'
2+
import { SessionManager } from '@/utils/ai/SessionManager'
3+
4+
describe('SessionManager', () => {
5+
let sessionManager: SessionManager
6+
7+
beforeEach(() => {
8+
// Create a new SessionManager instance before each test
9+
sessionManager = new SessionManager()
10+
})
11+
12+
describe('getState', () => {
13+
it('should return a copy of the state', () => {
14+
const state = sessionManager.getState()
15+
16+
// Verify that it returns an object
17+
expect(state).to.be.an('object')
18+
19+
// Verify that the returned state contains expected properties
20+
expect(state).to.have.property('systemPrompt')
21+
expect(state).to.have.property('isNewSession')
22+
expect(state).to.have.property('presetPrompt')
23+
24+
// Modifying the returned state should not affect the original state
25+
state.isNewSession = false
26+
expect(sessionManager.getState().isNewSession).to.be.true
27+
})
28+
})
29+
30+
describe('resetSession', () => {
31+
it('should reset the session to initial values', () => {
32+
// First modify the session state
33+
sessionManager.startSession()
34+
sessionManager.updatePreset('test-preset')
35+
36+
// Then reset
37+
sessionManager.resetSession()
38+
39+
// Verify the state has been reset
40+
const state = sessionManager.getState()
41+
expect(state.isNewSession).to.be.true
42+
expect(state.systemPrompt).to.equal('')
43+
expect(state.presetPrompt).to.equal('')
44+
})
45+
})
46+
47+
describe('resetSessionKeepPreset', () => {
48+
it('should reset the session while keeping preset prompt', () => {
49+
// First modify the session state
50+
sessionManager.startSession()
51+
sessionManager.updatePreset('test-preset')
52+
53+
// Then reset, but keep the preset prompt
54+
sessionManager.resetSessionKeepPreset()
55+
56+
// Verify the state has been reset, but preset prompt is retained
57+
const state = sessionManager.getState()
58+
expect(state.isNewSession).to.be.true
59+
expect(state.systemPrompt).to.equal('')
60+
expect(state.presetPrompt).to.equal('test-preset')
61+
})
62+
})
63+
64+
describe('startSession', () => {
65+
it('should mark the session as not new', () => {
66+
// Initially isNewSession should be true
67+
expect(sessionManager.getState().isNewSession).to.be.true
68+
69+
// Start the session
70+
sessionManager.startSession()
71+
72+
// Verify isNewSession becomes false
73+
expect(sessionManager.getState().isNewSession).to.be.false
74+
})
75+
76+
it('should not affect other state properties', () => {
77+
// Set some initial state
78+
sessionManager.updatePreset('test-preset')
79+
80+
// Record the current system prompt state
81+
const initialSystemPrompt = sessionManager.getState().systemPrompt
82+
83+
// Start the session
84+
sessionManager.startSession()
85+
86+
// Verify only the isNewSession property was modified
87+
const state = sessionManager.getState()
88+
expect(state.systemPrompt).to.equal(initialSystemPrompt)
89+
expect(state.presetPrompt).to.equal('test-preset')
90+
})
91+
})
92+
93+
describe('updatePreset', () => {
94+
it('should update the preset prompt', () => {
95+
sessionManager.updatePreset('new-preset')
96+
97+
expect(sessionManager.getState().presetPrompt).to.equal('new-preset')
98+
})
99+
100+
it('should mark the session as new', () => {
101+
// First mark the session as not new
102+
sessionManager.startSession()
103+
expect(sessionManager.getState().isNewSession).to.be.false
104+
105+
// Update the preset prompt
106+
sessionManager.updatePreset('new-preset')
107+
108+
// Verify the session is marked as new
109+
expect(sessionManager.getState().isNewSession).to.be.true
110+
})
111+
112+
it('should clear the system prompt', () => {
113+
// Simulate having an existing system prompt
114+
sessionManager.getSystemPrompt('en')
115+
expect(sessionManager.getState().systemPrompt).to.not.equal('')
116+
117+
// Update the preset prompt
118+
sessionManager.updatePreset('new-preset')
119+
120+
// Verify the system prompt is cleared
121+
expect(sessionManager.getState().systemPrompt).to.equal('')
122+
})
123+
})
124+
125+
describe('getSystemPrompt', () => {
126+
it('should load system prompt if session is new', () => {
127+
// Using sinon would encounter import issues, so we simply test behavior here
128+
const result = sessionManager.getSystemPrompt('en')
129+
130+
// Verify it returned a non-empty string
131+
expect(result).to.be.a('string')
132+
expect(result).to.not.equal('')
133+
134+
// Verify the state was updated
135+
expect(sessionManager.getState().systemPrompt).to.equal(result)
136+
})
137+
138+
it('should return cached system prompt if session is not new', () => {
139+
// Get the system prompt for the first time
140+
const firstPrompt = sessionManager.getSystemPrompt('en')
141+
142+
// Mark the session as not new
143+
sessionManager.startSession()
144+
145+
// Get the system prompt again
146+
const secondPrompt = sessionManager.getSystemPrompt('en')
147+
148+
// Verify the same prompt is returned
149+
expect(secondPrompt).to.equal(firstPrompt)
150+
})
151+
152+
it('should reload system prompt if empty even if session is not new', () => {
153+
// Get the system prompt for the first time
154+
sessionManager.getSystemPrompt('en')
155+
156+
// Mark the session as not new
157+
sessionManager.startSession()
158+
159+
// Manually clear the system prompt
160+
sessionManager['state'].systemPrompt = ''
161+
162+
// Get the system prompt again
163+
const reloadedPrompt = sessionManager.getSystemPrompt('en')
164+
165+
// Verify it returned a non-empty prompt
166+
expect(reloadedPrompt).to.be.a('string')
167+
expect(reloadedPrompt).to.not.equal('')
168+
})
169+
})
170+
})

tests/unit/utils/ai/copilot.spec.ts

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { expect } from 'chai'
2+
import { loadSystemPrompt, getModelProvider, AImodelsOptions } from '@/utils/ai/copilot'
3+
4+
describe('copilot', () => {
5+
describe('loadSystemPrompt', () => {
6+
it('should load base prompt with language', () => {
7+
const result = loadSystemPrompt('en')
8+
9+
// Check if it includes the base prompt
10+
expect(result).to.include('You are MQTTX Copilot')
11+
12+
// Check if it includes language setting
13+
expect(result).to.include('Please answer in English')
14+
})
15+
16+
it('should include different content based on language', () => {
17+
const zhResult = loadSystemPrompt('zh')
18+
const enResult = loadSystemPrompt('en')
19+
const jaResult = loadSystemPrompt('ja')
20+
21+
expect(zhResult).to.include('请使用中文回答')
22+
expect(enResult).to.include('Please answer in English')
23+
expect(jaResult).to.include('日本語で回答してください')
24+
})
25+
26+
it('should handle undefined command parameter', () => {
27+
const result = loadSystemPrompt('en', undefined)
28+
29+
// Result should include base prompt but not command-specific prompts
30+
expect(result).to.include('You are MQTTX Copilot')
31+
expect(result).to.include('Please answer in English')
32+
})
33+
34+
it('should handle different command inputs', () => {
35+
// Here we only test that the function handles different commands without throwing errors
36+
expect(() => loadSystemPrompt('en', 'some-code-command')).to.not.throw()
37+
expect(() => loadSystemPrompt('en', 'some-payload-command')).to.not.throw()
38+
expect(() => loadSystemPrompt('en', 'some-emqx-command')).to.not.throw()
39+
expect(() => loadSystemPrompt('en', 'some-mqtt-faq-command')).to.not.throw()
40+
expect(() => loadSystemPrompt('en', 'some-function-command')).to.not.throw()
41+
expect(() => loadSystemPrompt('en', 'some-schema-command')).to.not.throw()
42+
})
43+
})
44+
45+
describe('AImodelsOptions', () => {
46+
it('should contain all supported providers', () => {
47+
const providerNames = AImodelsOptions.map((provider) => provider.value)
48+
49+
expect(providerNames).to.include('OpenAI')
50+
expect(providerNames).to.include('DeepSeek')
51+
expect(providerNames).to.include('Anthropic')
52+
expect(providerNames).to.include('xAI')
53+
expect(providerNames).to.include('SiliconFlow')
54+
})
55+
56+
it('should have valid model options for each provider', () => {
57+
// Check OpenAI provider
58+
const openAI = AImodelsOptions.find((p) => p.value === 'OpenAI')
59+
expect(openAI).to.exist
60+
expect(openAI?.children).to.be.an('array').that.is.not.empty
61+
expect(openAI?.children.map((c) => c.value)).to.include('gpt-4o')
62+
63+
// Check DeepSeek provider
64+
const deepSeek = AImodelsOptions.find((p) => p.value === 'DeepSeek')
65+
expect(deepSeek).to.exist
66+
expect(deepSeek?.children).to.be.an('array').that.is.not.empty
67+
expect(deepSeek?.children.map((c) => c.value)).to.include('deepseek-chat')
68+
69+
// Check Anthropic provider
70+
const anthropic = AImodelsOptions.find((p) => p.value === 'Anthropic')
71+
expect(anthropic).to.exist
72+
expect(anthropic?.children).to.be.an('array').that.is.not.empty
73+
expect(anthropic?.children.map((c) => c.value)).to.include('claude-3-opus-latest')
74+
})
75+
76+
it('should have provider creator functions', () => {
77+
// Verify each provider has a creator function
78+
AImodelsOptions.forEach((provider) => {
79+
expect(provider.providerCreator).to.be.a('function')
80+
})
81+
})
82+
})
83+
84+
describe('getModelProvider', () => {
85+
it('should accept valid parameters', () => {
86+
// This test only verifies the function accepts parameters without throwing errors
87+
expect(() => {
88+
getModelProvider({
89+
model: 'gpt-4o',
90+
baseURL: 'https://api.openai.com/v1',
91+
apiKey: 'test-key',
92+
})
93+
}).to.not.throw()
94+
})
95+
96+
it('should work with different model types', () => {
97+
// Verify the function can handle different model types
98+
const validModels = [
99+
'gpt-4o', // OpenAI
100+
'deepseek-chat', // DeepSeek
101+
'claude-3-opus-latest', // Anthropic
102+
'grok-2-1212', // xAI
103+
]
104+
105+
validModels.forEach((model) => {
106+
expect(() => {
107+
getModelProvider({
108+
model: model as any,
109+
baseURL: 'https://api.example.com/v1',
110+
apiKey: 'test-key',
111+
})
112+
}).to.not.throw()
113+
})
114+
})
115+
})
116+
})

0 commit comments

Comments
 (0)