217 lines
8.3 KiB
TypeScript
217 lines
8.3 KiB
TypeScript
|
|
import { flushPromises, mount } from '@vue/test-utils'
|
||
|
|
import { describe, expect, it, vi } from 'vitest'
|
||
|
|
import Server from '../Server.vue'
|
||
|
|
import { rpcClient } from '@/api/rpc-client'
|
||
|
|
|
||
|
|
vi.mock('@/stores/app', () => ({
|
||
|
|
useAppStore: () => ({ packages: {} }),
|
||
|
|
}))
|
||
|
|
|
||
|
|
vi.mock('@/api/rpc-client', () => ({
|
||
|
|
rpcClient: {
|
||
|
|
call: vi.fn(),
|
||
|
|
vpnStatus: vi.fn(),
|
||
|
|
dnsStatus: vi.fn(),
|
||
|
|
diskStatus: vi.fn(),
|
||
|
|
},
|
||
|
|
}))
|
||
|
|
|
||
|
|
function deferred<T>() {
|
||
|
|
let resolve!: (value: T) => void
|
||
|
|
let reject!: (reason?: unknown) => void
|
||
|
|
const promise = new Promise<T>((res, rej) => {
|
||
|
|
resolve = res
|
||
|
|
reject = rej
|
||
|
|
})
|
||
|
|
return { promise, resolve, reject }
|
||
|
|
}
|
||
|
|
|
||
|
|
function mountServer(options: { renderTorServices?: boolean } = {}) {
|
||
|
|
return mount(Server, {
|
||
|
|
global: {
|
||
|
|
stubs: {
|
||
|
|
QuickActionsCard: true,
|
||
|
|
TorServicesCard: options.renderTorServices ? false : true,
|
||
|
|
ServerModals: true,
|
||
|
|
FipsNetworkCard: true,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
describe('Server network refresh states', () => {
|
||
|
|
it('keeps network overview visible while refresh is pending', async () => {
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'network.diagnostics') {
|
||
|
|
return Promise.resolve({ tor_connected: true, wifi_count: 2, wifi_ssid: 'Lab WiFi' })
|
||
|
|
}
|
||
|
|
if (request.method === 'router.list-forwards') {
|
||
|
|
return Promise.resolve({ forwards: [{}, {}] })
|
||
|
|
}
|
||
|
|
if (request.method === 'network.list-interfaces') {
|
||
|
|
return Promise.resolve({ interfaces: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'tor.list-services') {
|
||
|
|
return Promise.resolve({ services: [], tor_running: false })
|
||
|
|
}
|
||
|
|
if (request.method === 'vpn.list-peers') {
|
||
|
|
return Promise.resolve({ peers: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'fips.status') {
|
||
|
|
return Promise.resolve({ installed: false, service_active: false, key_present: false })
|
||
|
|
}
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
vi.mocked(rpcClient.vpnStatus).mockResolvedValue({ connected: true, provider: 'wireguard', ip_address: '10.0.0.2/32', wg_ip: '10.0.0.1/24' } as never)
|
||
|
|
vi.mocked(rpcClient.dnsStatus).mockResolvedValue({ provider: 'cloudflare', resolv_conf_servers: ['1.1.1.1'], doh_enabled: true } as never)
|
||
|
|
vi.mocked(rpcClient.diskStatus).mockResolvedValue({ encrypted: false, warnings: [] } as never)
|
||
|
|
|
||
|
|
const wrapper = mountServer()
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('Lab WiFi')
|
||
|
|
expect(wrapper.text()).toContain('2 rules')
|
||
|
|
|
||
|
|
const pendingDiagnostics = deferred<{ tor_connected: boolean; wifi_count: number; wifi_ssid: string }>()
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'network.diagnostics') return pendingDiagnostics.promise
|
||
|
|
if (request.method === 'router.list-forwards') return Promise.reject(new Error('offline'))
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
vi.mocked(rpcClient.vpnStatus).mockRejectedValueOnce(new Error('offline') as never)
|
||
|
|
vi.mocked(rpcClient.dnsStatus).mockRejectedValueOnce(new Error('offline') as never)
|
||
|
|
|
||
|
|
const refresh = (wrapper.vm as unknown as { loadNetworkData: () => Promise<void> }).loadNetworkData()
|
||
|
|
await wrapper.vm.$nextTick()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('Lab WiFi')
|
||
|
|
expect(wrapper.text()).toContain('2 rules')
|
||
|
|
expect(wrapper.text()).toContain('Refreshing network...')
|
||
|
|
|
||
|
|
pendingDiagnostics.reject(new Error('offline'))
|
||
|
|
await refresh
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('Lab WiFi')
|
||
|
|
expect(wrapper.text()).toContain('2 rules')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('keeps network interfaces visible while refresh is pending or fails', async () => {
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'network.list-interfaces') {
|
||
|
|
return Promise.resolve({
|
||
|
|
interfaces: [{ name: 'eth0', type: 'ethernet', state: 'up', mac: '00:11:22:33:44:55', ipv4: ['192.168.1.10'] }],
|
||
|
|
})
|
||
|
|
}
|
||
|
|
if (request.method === 'network.diagnostics') {
|
||
|
|
return Promise.resolve({ tor_connected: false })
|
||
|
|
}
|
||
|
|
if (request.method === 'router.list-forwards') {
|
||
|
|
return Promise.resolve({ forwards: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'tor.list-services') {
|
||
|
|
return Promise.resolve({ services: [], tor_running: false })
|
||
|
|
}
|
||
|
|
if (request.method === 'vpn.list-peers') {
|
||
|
|
return Promise.resolve({ peers: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'fips.status') {
|
||
|
|
return Promise.resolve({ installed: false, service_active: false, key_present: false })
|
||
|
|
}
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
vi.mocked(rpcClient.vpnStatus).mockResolvedValue({ connected: false } as never)
|
||
|
|
vi.mocked(rpcClient.dnsStatus).mockResolvedValue({ provider: 'system', resolv_conf_servers: [], doh_enabled: false } as never)
|
||
|
|
vi.mocked(rpcClient.diskStatus).mockResolvedValue({ encrypted: false, warnings: [] } as never)
|
||
|
|
|
||
|
|
const wrapper = mountServer()
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('eth0')
|
||
|
|
expect(wrapper.text()).toContain('192.168.1.10')
|
||
|
|
|
||
|
|
const pendingInterfaces = deferred<{ interfaces: [] }>()
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'network.list-interfaces') return pendingInterfaces.promise
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
|
||
|
|
const refresh = (wrapper.vm as unknown as { loadInterfaces: () => Promise<void> }).loadInterfaces()
|
||
|
|
await wrapper.vm.$nextTick()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('eth0')
|
||
|
|
expect(wrapper.text()).toContain('192.168.1.10')
|
||
|
|
expect(wrapper.text()).toContain('Refreshing interfaces...')
|
||
|
|
|
||
|
|
pendingInterfaces.reject(new Error('offline'))
|
||
|
|
await refresh
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('eth0')
|
||
|
|
expect(wrapper.text()).toContain('192.168.1.10')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('keeps Tor services visible while refresh is pending or fails', async () => {
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'tor.list-services') {
|
||
|
|
return Promise.resolve({
|
||
|
|
services: [{
|
||
|
|
name: 'filebrowser',
|
||
|
|
local_port: 8080,
|
||
|
|
onion_address: 'filebrowser123456789.onion',
|
||
|
|
enabled: true,
|
||
|
|
unauthenticated: false,
|
||
|
|
protocol: false,
|
||
|
|
}],
|
||
|
|
tor_running: true,
|
||
|
|
})
|
||
|
|
}
|
||
|
|
if (request.method === 'network.diagnostics') {
|
||
|
|
return Promise.resolve({ tor_connected: true })
|
||
|
|
}
|
||
|
|
if (request.method === 'router.list-forwards') {
|
||
|
|
return Promise.resolve({ forwards: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'network.list-interfaces') {
|
||
|
|
return Promise.resolve({ interfaces: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'vpn.list-peers') {
|
||
|
|
return Promise.resolve({ peers: [] })
|
||
|
|
}
|
||
|
|
if (request.method === 'fips.status') {
|
||
|
|
return Promise.resolve({ installed: false, service_active: false, key_present: false })
|
||
|
|
}
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
vi.mocked(rpcClient.vpnStatus).mockResolvedValue({ connected: false } as never)
|
||
|
|
vi.mocked(rpcClient.dnsStatus).mockResolvedValue({ provider: 'system', resolv_conf_servers: [], doh_enabled: false } as never)
|
||
|
|
vi.mocked(rpcClient.diskStatus).mockResolvedValue({ encrypted: false, warnings: [] } as never)
|
||
|
|
|
||
|
|
const wrapper = mountServer({ renderTorServices: true })
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('filebrowser')
|
||
|
|
expect(wrapper.text()).toContain('filebrowser123456789.onion')
|
||
|
|
|
||
|
|
const pendingTor = deferred<{ services: []; tor_running: boolean }>()
|
||
|
|
vi.mocked(rpcClient.call).mockImplementation((request: { method: string }) => {
|
||
|
|
if (request.method === 'tor.list-services') return pendingTor.promise
|
||
|
|
return Promise.resolve({})
|
||
|
|
})
|
||
|
|
|
||
|
|
const refresh = (wrapper.vm as unknown as { loadTorServices: () => Promise<void> }).loadTorServices()
|
||
|
|
await wrapper.vm.$nextTick()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('filebrowser')
|
||
|
|
expect(wrapper.text()).toContain('filebrowser123456789.onion')
|
||
|
|
expect(wrapper.text()).toContain('Refreshing Tor services...')
|
||
|
|
|
||
|
|
pendingTor.reject(new Error('offline'))
|
||
|
|
await refresh
|
||
|
|
await flushPromises()
|
||
|
|
|
||
|
|
expect(wrapper.text()).toContain('filebrowser')
|
||
|
|
expect(wrapper.text()).toContain('filebrowser123456789.onion')
|
||
|
|
})
|
||
|
|
})
|