mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-22 19:52:14 +08:00
* refactor(ipc): generate bindings with Specta * fix(profile): project IPC inputs to consumed fields * fix(events): remove unused refresh payloads * fix(webdav): return only consumed backup metadata * fix(network): trim unused interface metadata * fix(sysinfo): return only displayed system fields * chore(knip): check generated runtime exports * fix(proxy): confirm runtime chain updates before UI changes * fix(clipboard): propagate proxy environment copy failures * refactor(dns): remove ignored runtime patch * refactor(core): trim unused upgrade response fields * refactor(ipc): avoid redundant response copies * refactor(proxy): clarify automatic proxy query naming * chore(tests): remove added IPC regression tests * refactor(ipc): enforce typed command boundaries * chore(contract): check generated bindings before push * refactor(ipc): remove unused proxy liveness payloads
125 lines
3.7 KiB
TypeScript
125 lines
3.7 KiB
TypeScript
import { afterEach, expect, test, vi } from 'vitest'
|
|
|
|
vi.mock('@/services/cmds', () => ({
|
|
getProfiles: vi.fn(),
|
|
patchProfile: vi.fn().mockResolvedValue(undefined),
|
|
syncTrayProxySelection: vi.fn().mockResolvedValue(undefined),
|
|
}))
|
|
vi.mock('@/services/query-client', () => ({ revalidateQueries: vi.fn() }))
|
|
vi.mock('@/services/notice-service', () => ({ showNotice: { error: vi.fn() } }))
|
|
|
|
import {
|
|
getProfiles,
|
|
patchProfile,
|
|
syncTrayProxySelection,
|
|
} from '@/services/cmds'
|
|
import { revalidateQueries } from '@/services/query-client'
|
|
|
|
import {
|
|
profileTestUrls,
|
|
saveProfileTestUrls,
|
|
commitProfileTestUrl,
|
|
} from './proxy-test-url'
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
test('committed edits retain their profile and merge with fresh saved URLs', async () => {
|
|
vi.mocked(getProfiles).mockResolvedValue({
|
|
current: 'new-profile',
|
|
items: [
|
|
{
|
|
uid: 'old-profile',
|
|
type: null,
|
|
name: null,
|
|
file: null,
|
|
updated: null,
|
|
latency_test_urls: { untouched: 'https://example.com/' },
|
|
},
|
|
],
|
|
})
|
|
await commitProfileTestUrl('old-profile', 'A', ' http://localhost/new ')
|
|
expect(patchProfile).toHaveBeenCalledExactlyOnceWith('old-profile', {
|
|
latency_test_urls: {
|
|
untouched: 'https://example.com/',
|
|
A: 'http://localhost/new',
|
|
},
|
|
})
|
|
expect(revalidateQueries).toHaveBeenCalledWith([['getProfiles']])
|
|
expect(syncTrayProxySelection).toHaveBeenCalledOnce()
|
|
})
|
|
|
|
test('migration cannot overwrite a previously saved or cleared override', async () => {
|
|
vi.mocked(getProfiles).mockResolvedValue({
|
|
items: [
|
|
{
|
|
uid: 'p',
|
|
type: null,
|
|
name: null,
|
|
file: null,
|
|
updated: null,
|
|
latency_test_urls: { A: '' },
|
|
},
|
|
],
|
|
})
|
|
await saveProfileTestUrls('p', { A: 'http://localhost/stale' }, true)
|
|
expect(patchProfile).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('pending edits survive a reload and older save acknowledgements', async () => {
|
|
vi.useFakeTimers()
|
|
const profile = {
|
|
uid: 'pending-profile',
|
|
type: null,
|
|
name: null,
|
|
file: null,
|
|
updated: null,
|
|
latency_test_urls: { A: 'http://localhost/old' },
|
|
}
|
|
vi.mocked(getProfiles).mockResolvedValue({ items: [profile] })
|
|
let finish!: () => void
|
|
vi.mocked(patchProfile).mockImplementationOnce(
|
|
() =>
|
|
new Promise<null>((resolve) => {
|
|
finish = () => resolve(null)
|
|
}),
|
|
)
|
|
const first = commitProfileTestUrl(profile.uid, 'A', 'http://localhost/new')
|
|
expect(profileTestUrls(profile).A).toBe('http://localhost/new')
|
|
await vi.advanceTimersByTimeAsync(300)
|
|
expect(profileTestUrls(profile).A).toBe('http://localhost/new')
|
|
const second = commitProfileTestUrl(
|
|
profile.uid,
|
|
'A',
|
|
'http://localhost/newest',
|
|
)
|
|
finish()
|
|
await first
|
|
expect(profileTestUrls(profile).A).toBe('http://localhost/newest')
|
|
await second
|
|
expect(patchProfile).toHaveBeenLastCalledWith(profile.uid, {
|
|
latency_test_urls: { A: 'http://localhost/newest' },
|
|
})
|
|
})
|
|
|
|
test('a failed save preserves the committed input across profile refreshes', async () => {
|
|
const profile = {
|
|
uid: 'failed-profile',
|
|
type: null,
|
|
name: null,
|
|
file: null,
|
|
updated: null,
|
|
latency_test_urls: { A: 'http://localhost/old' },
|
|
}
|
|
vi.mocked(getProfiles).mockResolvedValue({ items: [profile] })
|
|
vi.mocked(patchProfile).mockRejectedValueOnce(new Error('save failed'))
|
|
const log = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
await commitProfileTestUrl(profile.uid, 'A', 'http://localhost/new')
|
|
expect(profileTestUrls(profile).A).toBe('http://localhost/new')
|
|
await commitProfileTestUrl(profile.uid, 'A', 'http://localhost/new')
|
|
expect(profileTestUrls(profile).A).toBe('http://localhost/old')
|
|
log.mockRestore()
|
|
})
|