test: add Stage 1 lib and Stage 2 services test coverage
Add comprehensive unit tests for:
- lib layer: config, device-fingerprint, errors, storage, hooks/useBreadcrumbs, http
- services layer: devices, login-logs, operation-logs, permissions, profile, roles, settings, stats, import-export
All 491 tests pass across 74 test files.
2026-04-17 23:59:15 +08:00
|
|
|
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
getDeviceFingerprint,
|
|
|
|
|
clearDeviceFingerprint,
|
|
|
|
|
} from './device-fingerprint'
|
|
|
|
|
|
|
|
|
|
describe('device-fingerprint', () => {
|
|
|
|
|
// 保存原始 navigator
|
|
|
|
|
const originalNavigator = global.navigator
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
// 清除缓存
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
vi.clearAllMocks()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
global.navigator = originalNavigator
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('getDeviceFingerprint', () => {
|
|
|
|
|
it('should return a device fingerprint object', () => {
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint).toBeDefined()
|
|
|
|
|
expect(fingerprint).toHaveProperty('device_id')
|
|
|
|
|
expect(fingerprint).toHaveProperty('device_name')
|
|
|
|
|
expect(fingerprint).toHaveProperty('device_browser')
|
|
|
|
|
expect(fingerprint).toHaveProperty('device_os')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return the same fingerprint on multiple calls (singleton)', () => {
|
|
|
|
|
const fingerprint1 = getDeviceFingerprint()
|
|
|
|
|
const fingerprint2 = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint1).toBe(fingerprint2)
|
|
|
|
|
expect(fingerprint1.device_id).toBe(fingerprint2.device_id)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return valid device_id', () => {
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint.device_id).toBeTruthy()
|
|
|
|
|
expect(typeof fingerprint.device_id).toBe('string')
|
|
|
|
|
expect(fingerprint.device_id.length).toBeGreaterThan(0)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return valid device_name format', () => {
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint.device_name).toBeTruthy()
|
|
|
|
|
expect(typeof fingerprint.device_name).toBe('string')
|
|
|
|
|
// device_name 格式: "Browser on OS"
|
|
|
|
|
expect(fingerprint.device_name).toMatch(/.+\s+on\s+.+/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return valid device_browser', () => {
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint.device_browser).toBeTruthy()
|
|
|
|
|
expect(typeof fingerprint.device_browser).toBe('string')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should return valid device_os', () => {
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
expect(fingerprint.device_os).toBeTruthy()
|
|
|
|
|
expect(typeof fingerprint.device_os).toBe('string')
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('clearDeviceFingerprint', () => {
|
|
|
|
|
it('should clear cached fingerprint', () => {
|
|
|
|
|
// 先获取一次生成缓存
|
|
|
|
|
const fingerprint1 = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 清除缓存
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 再次获取应该是新的指纹
|
|
|
|
|
const fingerprint2 = getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 两个指纹不应该相同
|
|
|
|
|
expect(fingerprint1.device_id).not.toBe(fingerprint2.device_id)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should allow multiple clears without error', () => {
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
clearDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 不应该抛出错误
|
|
|
|
|
expect(true).toBe(true)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('browser detection', () => {
|
|
|
|
|
it('should detect browser from user agent', () => {
|
2026-04-18 09:32:54 +08:00
|
|
|
// 注意:实际测试中 navigator.userAgent 是只读的
|
|
|
|
|
// 这里主要验证函数能正常工作
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
expect(fingerprint.device_browser).toBeTruthy()
|
test: add Stage 1 lib and Stage 2 services test coverage
Add comprehensive unit tests for:
- lib layer: config, device-fingerprint, errors, storage, hooks/useBreadcrumbs, http
- services layer: devices, login-logs, operation-logs, permissions, profile, roles, settings, stats, import-export
All 491 tests pass across 74 test files.
2026-04-17 23:59:15 +08:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('OS detection', () => {
|
|
|
|
|
it('should detect OS from user agent', () => {
|
|
|
|
|
// 类似浏览器检测,验证函数能正常工作
|
|
|
|
|
const fingerprint = getDeviceFingerprint()
|
|
|
|
|
expect(fingerprint.device_os).toBeTruthy()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
describe('security considerations', () => {
|
|
|
|
|
it('should not store fingerprint in localStorage', () => {
|
|
|
|
|
getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 设备指纹不应该存储在 localStorage
|
|
|
|
|
const deviceId = localStorage.getItem('device_id')
|
|
|
|
|
const fingerprint = localStorage.getItem('device_fingerprint')
|
|
|
|
|
expect(deviceId).toBeFalsy() // null or undefined
|
|
|
|
|
expect(fingerprint).toBeFalsy()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should not store fingerprint in sessionStorage', () => {
|
|
|
|
|
getDeviceFingerprint()
|
|
|
|
|
|
|
|
|
|
// 设备指纹不应该存储在 sessionStorage
|
|
|
|
|
const deviceId = sessionStorage.getItem('device_id')
|
|
|
|
|
const fingerprint = sessionStorage.getItem('device_fingerprint')
|
|
|
|
|
expect(deviceId).toBeFalsy()
|
|
|
|
|
expect(fingerprint).toBeFalsy()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|