mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-10 12:22:07 +08:00
test(connections): retain only allocation and formatting guards
Reduce snapshot and table tests to reference reuse, notification suppression, and visible-row formatting cost.
This commit is contained in:
@@ -35,7 +35,7 @@ vi.mock('./connection-row-view', async (importOriginal) => {
|
||||
}
|
||||
})
|
||||
|
||||
it('sorts numeric columns above the cache bound while formatting only virtual rows', () => {
|
||||
it('formats only virtual rows when sorting numeric columns above the cache bound', () => {
|
||||
const connections: IConnectionsItem[] = Array.from(
|
||||
{ length: 2_101 },
|
||||
(_, i) => ({
|
||||
@@ -59,33 +59,17 @@ it('sorts numeric columns above the cache bound while formatting only virtual ro
|
||||
}),
|
||||
)
|
||||
|
||||
for (const [column, field] of [
|
||||
['download', 'download'],
|
||||
['upload', 'upload'],
|
||||
['dlSpeed', 'curDownload'],
|
||||
['ulSpeed', 'curUpload'],
|
||||
] as const) {
|
||||
for (const desc of [false, true]) {
|
||||
sorting.id = column
|
||||
sorting.desc = desc
|
||||
vi.mocked(formatConnectionTraffic).mockClear()
|
||||
const html = renderToStaticMarkup(
|
||||
createElement(ConnectionTable, {
|
||||
connections,
|
||||
onShowDetail: vi.fn(),
|
||||
columnManagerOpen: false,
|
||||
onCloseColumnManager: vi.fn(),
|
||||
}),
|
||||
)
|
||||
const expected = connections
|
||||
.slice()
|
||||
.sort((a, b) => ((a[field] ?? 0) - (b[field] ?? 0)) * (desc ? -1 : 1))
|
||||
expect(html.match(/host-\d+:443/g)).toEqual(
|
||||
expected.slice(0, 6).map((row) => `${row.metadata.host}:443`),
|
||||
)
|
||||
expect(formatConnectionTraffic).toHaveBeenCalledTimes(24)
|
||||
}
|
||||
for (const column of ['download', 'upload', 'dlSpeed', 'ulSpeed']) {
|
||||
sorting.id = column
|
||||
vi.mocked(formatConnectionTraffic).mockClear()
|
||||
renderToStaticMarkup(
|
||||
createElement(ConnectionTable, {
|
||||
connections,
|
||||
onShowDetail: vi.fn(),
|
||||
columnManagerOpen: false,
|
||||
onCloseColumnManager: vi.fn(),
|
||||
}),
|
||||
)
|
||||
expect(formatConnectionTraffic).toHaveBeenCalledTimes(24)
|
||||
}
|
||||
expect(connections[0].id).toBe('0')
|
||||
expect(connections.at(-1)?.id).toBe('2100')
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useSyncExternalStore } from 'react'
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
|
||||
const socket = vi.hoisted(() => ({
|
||||
addListener:
|
||||
@@ -18,38 +18,15 @@ vi.mock('tauri-plugin-mihomo-api', () => ({
|
||||
MihomoWebSocket: { connect_connections: async () => socket },
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
vi.clearAllMocks()
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(10_000)
|
||||
vi.stubGlobal('window', globalThis)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
const connection = (id: string, upload = 0): IConnectionsItem => ({
|
||||
id,
|
||||
metadata: {
|
||||
network: 'tcp',
|
||||
type: 'HTTP',
|
||||
host: `${id}.example`,
|
||||
sourceIP: '127.0.0.1',
|
||||
sourcePort: '1234',
|
||||
destinationPort: '443',
|
||||
},
|
||||
chains: ['DIRECT'],
|
||||
rule: 'Match',
|
||||
rulePayload: '',
|
||||
start: '2026-01-01T00:00:00Z',
|
||||
upload,
|
||||
download: 0,
|
||||
})
|
||||
|
||||
const monitor = async () => {
|
||||
it('reuses unchanged snapshot and list references without notifying subscribers', async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(10_000)
|
||||
vi.stubGlobal('window', globalThis)
|
||||
const { useConnectionData: readConnectionData } = await import(
|
||||
'./use-connection-data'
|
||||
)
|
||||
@@ -65,7 +42,26 @@ const monitor = async () => {
|
||||
const unsubscribe = subscribe(listener)
|
||||
await Promise.resolve()
|
||||
const onMessage = socket.addListener.mock.calls[0][0]
|
||||
const send = (connections: IConnectionsItem[], uploadTotal = 0) => {
|
||||
const connections: IConnectionsItem[] = [
|
||||
{
|
||||
id: 'a',
|
||||
metadata: {
|
||||
network: 'tcp',
|
||||
type: 'HTTP',
|
||||
host: 'example.com',
|
||||
sourceIP: '',
|
||||
sourcePort: '',
|
||||
destinationPort: '443',
|
||||
},
|
||||
chains: [],
|
||||
rule: '',
|
||||
rulePayload: '',
|
||||
start: '',
|
||||
upload: 0,
|
||||
download: 0,
|
||||
},
|
||||
]
|
||||
const send = (uploadTotal = 0) => {
|
||||
vi.advanceTimersByTime(500)
|
||||
onMessage({
|
||||
type: 'Text',
|
||||
@@ -73,69 +69,11 @@ const monitor = async () => {
|
||||
})
|
||||
return read()
|
||||
}
|
||||
return { send, listener, unsubscribe }
|
||||
}
|
||||
|
||||
it('reuses unchanged snapshots and lists without notifying, including totals-only updates', async () => {
|
||||
const { send, listener, unsubscribe } = await monitor()
|
||||
const first = send([connection('a'), connection('b')])
|
||||
Object.freeze(first.activeConnections)
|
||||
first.activeConnections.forEach(Object.freeze)
|
||||
expect(send([connection('a'), connection('b')])).toBe(first)
|
||||
const first = send()
|
||||
expect(send()).toBe(first)
|
||||
expect(listener).toHaveBeenCalledTimes(1)
|
||||
|
||||
const totalsOnly = send([connection('a'), connection('b')], 100)
|
||||
expect(totalsOnly).not.toBe(first)
|
||||
const totalsOnly = send(100)
|
||||
expect(totalsOnly.activeConnections).toBe(first.activeConnections)
|
||||
expect(totalsOnly.closedConnections).toBe(first.closedConnections)
|
||||
expect(totalsOnly.uploadTotal).toBe(100)
|
||||
expect(listener).toHaveBeenCalledTimes(2)
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
it('updates traffic, decays speed to zero, and preserves immutable history through reorder and removal', async () => {
|
||||
const { send, unsubscribe } = await monitor()
|
||||
const first = send([connection('a'), connection('b')])
|
||||
const changed = send([connection('a', 50), connection('b')])
|
||||
expect(changed.activeConnections[0].curUpload).toBe(50)
|
||||
expect(changed.activeConnections[0].metadata).toBe(
|
||||
first.activeConnections[0].metadata,
|
||||
)
|
||||
expect(changed.activeConnections[0].chains).toBe(
|
||||
first.activeConnections[0].chains,
|
||||
)
|
||||
expect(changed.activeConnections[1]).toBe(first.activeConnections[1])
|
||||
expect(first.activeConnections[0].upload).toBe(0)
|
||||
|
||||
const idle = send([connection('a', 50), connection('b')])
|
||||
expect(idle.activeConnections[0].curUpload).toBe(0)
|
||||
expect(send([connection('a', 50), connection('b')])).toBe(idle)
|
||||
const reordered = send([connection('b'), connection('a', 50)])
|
||||
expect(reordered.activeConnections).toEqual([
|
||||
idle.activeConnections[1],
|
||||
idle.activeConnections[0],
|
||||
])
|
||||
expect(reordered.closedConnections).toBe(idle.closedConnections)
|
||||
const removed = send([connection('b'), connection('c')])
|
||||
expect(removed.closedConnections).toEqual([idle.activeConnections[0]])
|
||||
expect(removed.closedConnections[0]).toBe(idle.activeConnections[0])
|
||||
const next = send([connection('b', 10), connection('c')])
|
||||
expect(next.closedConnections).toBe(removed.closedConnections)
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
it('evicts the oldest closed entries at 500 without mutating retained history', async () => {
|
||||
const { send, unsubscribe } = await monitor()
|
||||
send(Array.from({ length: 502 }, (_, i) => connection(`${i}`)))
|
||||
const closed = send([])
|
||||
expect(closed.closedConnections).toHaveLength(500)
|
||||
expect(closed.closedConnections[0].id).toBe('2')
|
||||
send([connection('new-a'), connection('new-b')])
|
||||
const evicted = send([])
|
||||
expect(evicted.closedConnections).toHaveLength(500)
|
||||
expect(evicted.closedConnections[0].id).toBe('4')
|
||||
expect(evicted.closedConnections.at(-1)?.id).toBe('new-b')
|
||||
expect(closed.closedConnections[0].id).toBe('2')
|
||||
|
||||
unsubscribe()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user