mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-13 09:00:03 +08:00
156 lines
5.2 KiB
TypeScript
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);
|
|
});
|
|
});
|