fix(core): create token file on first save (#5367)
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run

This commit is contained in:
tt-a1i 2026-06-19 09:03:08 +08:00 committed by GitHub
parent 7525c18c5f
commit 88d7e7ef1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 40 additions and 2 deletions

View file

@ -120,6 +120,39 @@ describe('FileTokenStorage', () => {
});
describe('setCredentials', () => {
it('should create token file when saving credentials for the first time', async () => {
mockFs.readFile.mockRejectedValue({ code: 'ENOENT' });
mockFs.mkdir.mockResolvedValue(undefined);
vi.mocked(atomicWriteFile).mockResolvedValue(undefined);
const credentials: OAuthCredentials = {
serverName: 'test-server',
token: {
accessToken: 'access-token',
tokenType: 'Bearer',
},
updatedAt: Date.now() - 10000,
};
await storage.setCredentials(credentials);
expect(mockFs.mkdir).toHaveBeenCalledWith(
path.join('/home/test', '.qwen'),
{ recursive: true, mode: 0o700 },
);
expect(atomicWriteFile).toHaveBeenCalled();
const writeCall = vi.mocked(atomicWriteFile).mock.calls[0];
const decrypted = storage['decrypt'](writeCall[1] as string);
const saved = JSON.parse(decrypted);
expect(Object.keys(saved)).toEqual(['test-server']);
expect(saved['test-server']).toEqual({
...credentials,
updatedAt: expect.any(Number),
});
});
it('should save credentials with encryption', async () => {
const encryptedData = storage['encrypt'](
JSON.stringify({ 'existing-server': existingCredentials }),

View file

@ -78,7 +78,9 @@ export class FileTokenStorage
await fs.mkdir(dir, { recursive: true, mode: 0o700 });
}
private async loadTokens(): Promise<Map<string, OAuthCredentials>> {
private async loadTokens(
allowMissing = false,
): Promise<Map<string, OAuthCredentials>> {
try {
const data = await fs.readFile(this.tokenFilePath, 'utf-8');
const decrypted = this.decrypt(data);
@ -87,6 +89,9 @@ export class FileTokenStorage
} catch (error: unknown) {
const err = error as NodeJS.ErrnoException & { message?: string };
if (err.code === 'ENOENT') {
if (allowMissing) {
return new Map();
}
throw new Error('Token file does not exist');
}
if (
@ -135,7 +140,7 @@ export class FileTokenStorage
async setCredentials(credentials: OAuthCredentials): Promise<void> {
this.validateCredentials(credentials);
const tokens = await this.loadTokens();
const tokens = await this.loadTokens(true);
const updatedCredentials: OAuthCredentials = {
...credentials,
updatedAt: Date.now(),