Files
hms/apps/miniprogram/__tests__/services/ble/DataBuffer.test.ts
iven 62c02e0f15
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
feat(miniprogram): BLE 增强层 — DataBuffer + GenericBleAdapter + DataSyncScheduler
- DataBuffer: 离线持久化缓冲(分桶存储 + 去重 + 容量管理)
- GenericBleAdapter: 基于 Bluetooth SIG 标准 Health Profile 的通用适配器
  (Heart Rate 0x180D / Health Thermometer 0x1809 / Blood Pressure 0x1810)
- DataSyncScheduler: 定时自动同步调度(基于时间间隔判断是否需要同步)
- BLEManager: 集成 DataBuffer 替换简单 Storage 缓存
- device-sync 页面: 注册 CustomBandAdapter + 自动同步 + 状态显示
- 新增 vitest 单元测试配置,30 个测试全部通过
2026-05-04 02:42:58 +08:00

90 lines
2.8 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
import { DataBuffer } from '@/services/ble/DataBuffer';
import type { NormalizedReading } from '@/services/ble/types';
// Mock Taro Storage
const storage = new Map<string, string>();
vi.mock('@tarojs/taro', () => ({
default: {
getStorageSync: vi.fn((key: string) => storage.get(key) || ''),
setStorageSync: vi.fn((key: string, value: string) => { storage.set(key, value); }),
removeStorageSync: vi.fn((key: string) => { storage.delete(key); }),
getStorageInfoSync: vi.fn(() => ({ keys: Array.from(storage.keys()), limitSize: 10240, currentSize: storage.size })),
},
}));
function makeReading(overrides: Partial<NormalizedReading> = {}): NormalizedReading {
return {
device_type: 'heart_rate',
values: { heart_rate: 72 },
measured_at: new Date().toISOString(),
...overrides,
};
}
describe('DataBuffer', () => {
let buffer: DataBuffer;
beforeEach(() => {
storage.clear();
buffer = new DataBuffer({ bucketSize: 100 });
});
it('push 添加读数并持久化', () => {
const reading = makeReading();
buffer.push(reading);
expect(buffer.size()).toBe(1);
});
it('push 批量添加读数', () => {
const readings = Array.from({ length: 10 }, (_, i) =>
makeReading({ measured_at: new Date(Date.now() + i * 1000).toISOString() }),
);
buffer.push(readings);
expect(buffer.size()).toBe(10);
});
it('flush 返回并清空缓冲区', () => {
buffer.push([
makeReading({ measured_at: '2026-05-04T10:00:00.000Z' }),
makeReading({ measured_at: '2026-05-04T10:00:01.000Z' }),
]);
const flushed = buffer.flush();
expect(flushed.length).toBe(2);
expect(buffer.size()).toBe(0);
});
it('超过 maxTotal 时丢弃最旧数据', () => {
const smallBuffer = new DataBuffer({ bucketSize: 5, maxTotal: 10 });
for (let i = 0; i < 15; i++) {
smallBuffer.push(makeReading({ measured_at: new Date(i * 1000).toISOString() }));
}
expect(smallBuffer.size()).toBe(10);
});
it('去重:相同 measured_at + device_type 不重复存储', () => {
const ts = '2026-05-04T10:00:00.000Z';
buffer.push(makeReading({ measured_at: ts }));
buffer.push(makeReading({ measured_at: ts }));
expect(buffer.size()).toBe(1);
});
it('restore 从 Storage 恢复未上传数据', () => {
buffer.push([
makeReading({ measured_at: '2026-05-04T10:00:00.000Z' }),
makeReading({ measured_at: '2026-05-04T10:00:01.000Z' }),
]);
// 模拟重启:新建 DataBuffer 并 restore
const restored = new DataBuffer({ bucketSize: 100 });
const count = restored.restore();
expect(count).toBe(2);
expect(restored.size()).toBe(2);
});
it('clear 清空缓冲区和 Storage', () => {
buffer.push(makeReading());
buffer.clear();
expect(buffer.size()).toBe(0);
});
});