test: fix

This commit is contained in:
lin onetwo 2025-08-25 22:07:26 +08:00
parent 7dfda0e12d
commit 31e24de58e
4 changed files with 31 additions and 48 deletions

View file

@ -161,3 +161,25 @@ const esbuild = require('esbuild');
```
If tried to add this to `esbuildLoaderRule` will cause this error. The object contains an internal reference chain (`.default.default`) that triggers recursion when webpack-merge/clone-deep attempts to merge it.
## Error: Can't resolve 'os' in
```log
@ ./src/services/libs/i18n/i18next-electron-fs-backend.ts 3:0-44 147:10-22 172:10-22
@ ./src/services/libs/i18n/renderer.ts 4:0-77 8:20-37
@ ./src/renderer.tsx 20:0-65 36:5-21
ERROR in ./node_modules/winston/dist/winston/transports/stream.js 26:9-22
Module not found: Error: Can't resolve 'os' in 'C:\Users\linonetwo\Documents\repo-c\TidGi-Desktop\node_modules\winston\dist\winston\transports'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "os": require.resolve("os-browserify/browser") }'
- install 'os-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "os": false }
```
Usually because you import the server-side `logger` in renderer process code. You have to use `console` or add new transport in [rendererTransport.ts](src/services/libs/log/rendererTransport.ts).

View file

@ -142,23 +142,21 @@ Feature: Agent Workflow - Tool Usage and Multi-Round Conversation
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki test" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
And I wait for 1 seconds
And I wait for 0.2 seconds
And I should see a "user message" element with selector "*:has-text(' wiki test')"
And I should see a "tool use indicator" element with selector "*:has-text('tool_use')"
And I should see a "wiki operation tool" element with selector "*:has-text('wiki-operation')"
And I wait for 2 seconds
And I wait for 0.2 seconds
And I should see a "function result error" element with selector "*:has-text('functions_result')"
And I should see a "workspace not found" element with selector "*:has-text('Workspace with name or ID \"default\" does not exist')"
# Second round: assistant suggests wiki workspace and operation succeeds (automated by assistant/tool)
And I wait for 2 seconds
And I wait for 0.2 seconds
And I should see a "assistant suggestion" element with selector "*:has-text('tool_use')"
And I should see a "tool use indicator" element with selector "*:has-text('tool_use')"
And I should see a "wiki operation tool" element with selector "*:has-text('wiki-operation')"
And I wait for 2 seconds
And I wait for 0.2 seconds
And I should see a "function result success" element with selector "*:has-text('functions_result')"
And I wait for 5 seconds
And I should see a "success message" element with selector "*:has-text('Successfully added tiddler')"
And I wait for 2 seconds
And I wait for 0.2 seconds
And I should see a "assistant confirmation" element with selector "*:has-text(' wiki ')"
Then I should see 6 messages in chat history

View file

@ -1,4 +1,3 @@
import { logger } from '@services/libs/log';
import { createServer, IncomingMessage, Server, ServerResponse } from 'http';
import { AddressInfo } from 'net';
@ -143,27 +142,13 @@ export class MockOpenAIServer {
private generateChatCompletionResponse(chatRequest: ChatRequest) {
const modelName = chatRequest.model || 'test-model';
// Increment call count for each API request
this.callCount++;
logger.debug('[mockOpenAI] generateChatCompletionResponse', {
modelName,
callCount: this.callCount,
totalMessages: chatRequest.messages.length,
});
logger.debug('mockOpenAI message types', {
messageTypes: chatRequest.messages.map((m) =>
`${m.role}:${String(m.content).includes('<functions_result>') ? 'FUNCTIONS_RESULT' : String(m.content).substring(0, 30) + '...'}`
),
});
// Use call count to determine which response to return (1-indexed)
const ruleIndex = this.callCount - 1;
const responseRule = this.rules[ruleIndex];
if (!responseRule) {
logger.debug('[mockOpenAI] No more responses available for call', { callCount: this.callCount });
return {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
@ -174,8 +159,6 @@ export class MockOpenAIServer {
};
}
logger.debug('[mockOpenAI] Using rule for call', { callCount: this.callCount, preview: responseRule.response.substring(0, 100) + '...' });
return {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
@ -199,20 +182,12 @@ export class MockOpenAIServer {
if (response.writableEnded) return;
const modelName = chatRequest.model || 'test-model';
// Increment call count for streaming requests too
this.callCount++;
// Use call count to determine which response to return (1-indexed)
const ruleIndex = this.callCount - 1;
const responseRule = this.rules[ruleIndex];
logger.debug('mockOpenAI streaming check', {
modelName,
callCount: this.callCount,
matched: !!responseRule,
});
// If matched: honor client's stream request. If client requests stream, always stream the matched.response.
if (responseRule && chatRequest.stream) {
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
@ -268,26 +243,16 @@ export class MockOpenAIServer {
const roleLine = `data: ${JSON.stringify(roleChunk)}\n\n`;
const contentLine = `data: ${JSON.stringify(contentChunk)}\n\n`;
const finalLine = `data: ${JSON.stringify(finalChunk)}\n\n`;
logger.debug('mockOpenAI sending streaming chunks', {
responseContentPreview: responseRule.response.substring(0, 200),
role: roleLine.substring(0, 100) + '...',
contentPreview: contentLine.substring(0, 200) + '...',
finalPreview: finalLine.substring(0, 100) + '...',
});
response.write(roleLine);
response.write(contentLine);
response.write(finalLine);
response.write('data: [DONE]\n\n');
logger.debug('mockOpenAI stream finished', { callCount: this.callCount });
response.end();
return;
}
// If matched but client did not request stream, return a regular JSON chat completion
if (responseRule && !chatRequest.stream) {
logger.debug('[mockOpenAI] Using non-stream rule for call', { callCount: this.callCount, preview: responseRule.response.substring(0, 100) + '...' });
const resp = {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
@ -305,7 +270,6 @@ export class MockOpenAIServer {
],
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
};
if (!response.writableEnded) {
response.setHeader('Content-Type', 'application/json');
response.writeHead(200);

View file

@ -1,5 +1,4 @@
import { I18NChannels } from '@/constants/channels';
import { logger } from '@services/libs/log';
import type { BackendModule, InitOptions, MultiReadCallback, ReadCallback, Services } from 'i18next';
import { cloneDeep, merge, Object } from 'lodash';
@ -188,7 +187,7 @@ export class Backend implements BackendModule {
// }
const { keys } = payload;
if (!keys) return;
for (const key of keys) {
for (const key of keys) {
// Write methods don't have any callbacks from what I've seen,
// so this is called more than I thought; but necessary!
const entry = this.writeCallbacks[key];
@ -217,7 +216,7 @@ export class Backend implements BackendModule {
for (const element of toWork as Array<{ key: string; values: Array<{ key: string; fallbackValue: string; callback?: (error?: unknown) => void }> }>) {
const anonymous = (error: unknown, data: unknown) => {
if (error) {
logger.error(`${this.rendererLog} encountered error when trying to read file '${element.key}' before writing missing translation`, { error });
console.error(`${this.rendererLog} encountered error when trying to read file '${element.key}' before writing missing translation`, { error });
return;
}
const keySeparator = Boolean(this.i18nextOptions.keySeparator); // Do we have a key separator or not?
@ -245,7 +244,7 @@ export class Backend implements BackendModule {
}
// Send out the message to the ipcMain process
if (debug) {
logger.debug(`${this.rendererLog} requesting the missing key '${String(writeKeys)}' be written to file '${element.key}'.`);
console.debug(`${this.rendererLog} requesting the missing key '${String(writeKeys)}' be written to file '${element.key}'.`);
}
i18nextElectronBackend.send(I18NChannels.writeFileRequest, {
keys: writeKeys,
@ -280,7 +279,7 @@ export class Backend implements BackendModule {
// Reads a given translation file
read(language: string, namespace: string, callback: ReadCallback) {
const loadPathString = String(this.backendOptions.loadPath ?? defaultOptions.loadPath);
const loadPathString = String(this.backendOptions.loadPath ?? defaultOptions.loadPath);
const filename = safeInterpolate(this.services.interpolator, loadPathString, { lng: language, ns: namespace });
this.requestFileRead(filename, (error?: unknown, data?: unknown) => {
type ReadCallbackParameters = Parameters<ReadCallback>;