Files
MyGoNavi/frontend/src/utils/securityUpdateRepairFlow.test.ts
2026-04-11 20:12:23 +08:00

156 lines
5.2 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { SavedConnection, SecurityUpdateIssue, SecurityUpdateStatus } from '../types';
import {
hasSecurityUpdateRecentResult,
resolveSecurityUpdateFocusState,
resolveSecurityUpdateRepairEntry,
resolveSecurityUpdateSettingsFocusTarget,
shouldRefreshSecurityUpdateDetailsFocus,
shouldReopenSecurityUpdateDetails,
shouldRetrySecurityUpdateAfterRepairSave,
} from './securityUpdateRepairFlow';
const createConnection = (id: string): SavedConnection => ({
id,
name: `连接-${id}`,
config: {
id,
type: 'postgres',
host: 'db.local',
port: 5432,
user: 'postgres',
},
});
const createStatus = (overrides: Partial<SecurityUpdateStatus> = {}): SecurityUpdateStatus => ({
overallStatus: 'needs_attention',
summary: {
total: 1,
updated: 0,
pending: 1,
skipped: 0,
failed: 0,
},
issues: [],
...overrides,
});
describe('securityUpdateRepairFlow', () => {
it('opens the matching connection and preserves the return source for security update repairs', () => {
const target = createConnection('conn-1');
const issue: SecurityUpdateIssue = {
id: 'issue-1',
action: 'open_connection',
refId: 'conn-1',
};
expect(resolveSecurityUpdateRepairEntry(issue, [target])).toEqual({
type: 'connection',
connection: target,
repairSource: 'connection',
});
});
it('returns a user-facing warning when the target connection no longer exists', () => {
const issue: SecurityUpdateIssue = {
id: 'issue-1',
action: 'open_connection',
refId: 'missing-conn',
};
expect(resolveSecurityUpdateRepairEntry(issue, [createConnection('conn-1')])).toEqual({
type: 'warning',
message: '未找到对应连接,请先重新检查最新状态',
});
});
it('maps proxy, ai and retry actions to the expected repair entry', () => {
expect(resolveSecurityUpdateRepairEntry({ id: 'proxy', action: 'open_proxy_settings' }, [])).toEqual({
type: 'proxy',
repairSource: 'proxy',
});
expect(resolveSecurityUpdateRepairEntry({ id: 'ai', action: 'open_ai_settings', refId: 'provider-1' }, [])).toEqual({
type: 'ai',
providerId: 'provider-1',
repairSource: 'ai',
});
expect(resolveSecurityUpdateRepairEntry({ id: 'retry', action: 'retry_update' }, [])).toEqual({
type: 'retry',
});
});
it('routes view_details actions to the latest result section when a recent result exists', () => {
const status = createStatus({
backupPath: '/tmp/gonavi-backup.json',
lastError: '写入新密钥失败',
});
expect(hasSecurityUpdateRecentResult(status)).toBe(true);
expect(resolveSecurityUpdateSettingsFocusTarget(status)).toBe('recent_result');
expect(resolveSecurityUpdateRepairEntry({ id: 'details', action: 'view_details' }, [], status)).toEqual({
type: 'details',
focusTarget: 'recent_result',
});
});
it('falls back to the status section when no recent result is available yet', () => {
const status = createStatus();
expect(hasSecurityUpdateRecentResult(status)).toBe(false);
expect(resolveSecurityUpdateSettingsFocusTarget(status)).toBe('status');
expect(resolveSecurityUpdateRepairEntry({ id: 'details', action: 'view_details' }, [], status)).toEqual({
type: 'details',
focusTarget: 'status',
});
});
it('builds a fresh focus pulse for repeated details clicks and clears it when the modal closes', () => {
expect(resolveSecurityUpdateFocusState(true, 'status', 1)).toEqual({
target: 'status',
pulseKey: 'status:1',
});
expect(resolveSecurityUpdateFocusState(true, 'status', 2)).toEqual({
target: 'status',
pulseKey: 'status:2',
});
expect(resolveSecurityUpdateFocusState(false, 'status', 2)).toEqual({
target: null,
pulseKey: null,
});
expect(resolveSecurityUpdateFocusState(true, null, 3)).toEqual({
target: null,
pulseKey: null,
});
});
it('reopens security update details after closing a repair entry opened from that page', () => {
expect(shouldReopenSecurityUpdateDetails('connection')).toBe(true);
expect(shouldReopenSecurityUpdateDetails('proxy')).toBe(true);
expect(shouldReopenSecurityUpdateDetails('ai')).toBe(true);
expect(shouldReopenSecurityUpdateDetails(null)).toBe(false);
});
it('retries the current round automatically after saving a connection from the repair flow', () => {
expect(shouldRetrySecurityUpdateAfterRepairSave('connection')).toBe(true);
expect(shouldRetrySecurityUpdateAfterRepairSave('proxy')).toBe(false);
expect(shouldRetrySecurityUpdateAfterRepairSave('ai')).toBe(false);
expect(shouldRetrySecurityUpdateAfterRepairSave(null)).toBe(false);
});
it('does not force a new focus pulse when the details modal is already open and only the round result is refreshing', () => {
expect(shouldRefreshSecurityUpdateDetailsFocus({
requestedOpen: true,
wasOpen: true,
})).toBe(false);
expect(shouldRefreshSecurityUpdateDetailsFocus({
requestedOpen: true,
wasOpen: false,
})).toBe(true);
expect(shouldRefreshSecurityUpdateDetailsFocus({
requestedOpen: false,
wasOpen: true,
})).toBe(false);
});
});