1
0
forked from boos/BoosAPI

feat: implement macOS notarization and ad-hoc signing in afterSign script

feat: add run-server script to manage server execution and compatibility checks

feat: create main application logic for desktop with window management, server handling, and auto-updater

test: add unit tests for desktop runtime helpers

feat: implement desktop server environment configuration and health check

test: add tests for server configuration and desktop routes

feat: add native module guard for better-sqlite3 compatibility

test: add tests for native module guard functionality

test: add comprehensive tests for account verification feedback handling

feat: implement account verification feedback helpers

chore: add TypeScript configuration for desktop builds
This commit is contained in:
cita
2026-03-06 16:24:41 +08:00
parent acdc846b13
commit 2a810facb8
45 changed files with 6712 additions and 405 deletions
@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';
import {
buildAddAccountPrereqHint,
buildVerifyFailureHint,
normalizeVerifyFailureMessage,
} from './accountVerifyFeedback.js';
describe('account verify feedback', () => {
it('treats fetch failures as connectivity issues instead of token mistakes', () => {
expect(normalizeVerifyFailureMessage('Failed to fetch')).not.toBe('Failed to fetch');
expect(buildVerifyFailureHint({ success: false, message: 'Failed to fetch' })).not.toBeNull();
expect(buildAddAccountPrereqHint({ success: false, message: 'Failed to fetch' })).toMatch(/metapi|站点|代理/i);
});
it('treats timeout failures as reachability issues instead of token mistakes', () => {
const timeoutMessage = 'Token verification timed out (10s)';
expect(normalizeVerifyFailureMessage(timeoutMessage)).toBe(timeoutMessage);
expect(buildVerifyFailureHint({ success: false, message: timeoutMessage })).toMatch(/Token|站点|代理|超时/i);
expect(buildAddAccountPrereqHint({ success: false, message: timeoutMessage })).toMatch(/站点|代理|超时/i);
});
it('keeps token guidance for actual credential failures', () => {
const invalidHint = buildVerifyFailureHint({ success: false, message: 'Token invalid' });
expect(invalidHint).toMatch(/Token/i);
expect(buildAddAccountPrereqHint({ success: false, message: 'Token invalid' })).toMatch(/Token/i);
});
it('normalizes invalid user-id mismatch messages for display', () => {
expect(normalizeVerifyFailureMessage('The provided user ID does not match this token. Please check your site user ID.'))
.toMatch(/ID/i);
expect(normalizeVerifyFailureMessage('The provided user ID does not match this token. Please check your site user ID.'))
.not.toContain('does not match this token');
});
it('prioritizes user-id guidance when the site requires it', () => {
expect(buildVerifyFailureHint({ success: false, needsUserId: true, message: 'missing New-Api-User' })).toBeNull();
expect(buildAddAccountPrereqHint({ success: false, needsUserId: true, message: 'missing New-Api-User' })).toMatch(/ID/i);
});
it('treats invalid user-id as a mismatch instead of a missing-value hint', () => {
const invalidUserIdHint = buildVerifyFailureHint({ success: false, invalidUserId: true, message: 'user id mismatch' });
const genericTokenHint = buildVerifyFailureHint({ success: false, message: 'Token invalid' });
const invalidUserIdPrereq = buildAddAccountPrereqHint({ success: false, invalidUserId: true, message: 'user id mismatch' });
const genericPrereq = buildAddAccountPrereqHint({ success: false, message: 'Token invalid' });
expect(invalidUserIdHint).toMatch(/ID/i);
expect(invalidUserIdHint).not.toBe(genericTokenHint);
expect(invalidUserIdPrereq).toMatch(/ID/i);
expect(invalidUserIdPrereq).not.toBe(genericPrereq);
});
});
@@ -0,0 +1,72 @@
type VerifyResultLike = {
success?: boolean;
needsUserId?: boolean;
invalidUserId?: boolean;
message?: string | null;
} | null | undefined;
function normalizeMessageText(message: unknown): string {
return typeof message === 'string' ? message.trim() : '';
}
export function isNetworkFailureMessage(message: unknown): boolean {
const lowered = normalizeMessageText(message).toLowerCase();
return lowered.includes('failed to fetch')
|| lowered.includes('fetch failed')
|| lowered.includes('networkerror')
|| lowered.includes('load failed');
}
export function isTimeoutFailureMessage(message: unknown): boolean {
const lowered = normalizeMessageText(message).toLowerCase();
return lowered.includes('timed out')
|| lowered.includes('timeout')
|| lowered.includes('请求超时');
}
export function normalizeVerifyFailureMessage(message: unknown): string {
const text = normalizeMessageText(message);
if (!text) return '验证失败';
const lowered = text.toLowerCase();
if (isNetworkFailureMessage(text)) {
return '无法连接到 metapi 服务端,请检查服务状态或网络连接';
}
if (lowered.includes('user id mismatch') || lowered.includes('does not match this token')) {
return '填写的用户 ID 与当前 Token / Cookie 不匹配';
}
return text;
}
export function buildVerifyFailureHint(result: VerifyResultLike): string | null {
if (!result || result.success || result.needsUserId) return null;
if (result.invalidUserId) {
return '这不是 Token 错误判断。请检查填写的用户 ID 是否与当前 Token / Cookie 属于同一账号。';
}
if (isNetworkFailureMessage(result.message)) {
return '这不是 Token 错误判断。请检查 metapi 服务是否在线,以及目标站点或代理是否可达。';
}
if (isTimeoutFailureMessage(result.message)) {
return '这不是 Token 错误判断。目标站点响应超时,请稍后重试或检查代理/网络。';
}
return '请检查 Token 是否正确';
}
export function buildAddAccountPrereqHint(result: VerifyResultLike): string {
if (!result) {
return '请先点击“验证 Token”,验证成功后才能添加账号。';
}
if (result.success) return '';
if (result.needsUserId) {
return '请先补充用户 ID 并重新验证,验证成功后才能添加账号。';
}
if (result.invalidUserId) {
return '请先修正用户 ID 并重新验证,验证成功后才能添加账号。';
}
if (isNetworkFailureMessage(result.message)) {
return '验证请求未成功完成,请先检查 metapi 服务、站点网络或代理配置。';
}
if (isTimeoutFailureMessage(result.message)) {
return '验证请求超时,请先检查站点或代理连通性后再添加账号。';
}
return '请先点击“验证 Token”,验证成功后才能添加账号。';
}