fix(测试数据生成器): 修复 Worker 取消失效与规则存储静默失败 (#69)

- 为生成任务引入 generationId,忽略过期 Worker 响应,避免取消后展示错误数据
- Worker 每 100 行让出事件循环,使 cancel 消息能被及时处理
- ruleStorage.setAll 返回写入结果,save/update/delete 在 QuotaExceeded 时返回失败
- 补充 useGenerator 与 ruleStorage 单元测试
This commit is contained in:
cursor[bot]
2026-06-26 17:07:35 +08:00
committed by GitHub
parent 5ae9cf065f
commit 4b38133cf8
6 changed files with 262 additions and 25 deletions
+65
View File
@@ -0,0 +1,65 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import * as ruleStorage from '../ruleStorage';
const STORAGE_KEY = 'testDataGenerator_rules';
const mockField = {
id: 'field-1',
name: 'username',
generatorId: 'string',
params: {},
required: true,
nullRate: 0,
unique: false,
};
describe('ruleStorage', () => {
beforeEach(() => {
localStorage.clear();
vi.restoreAllMocks();
});
it('save 在 localStorage 写入失败时应返回 null', () => {
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new DOMException('QuotaExceededError');
});
const result = ruleStorage.save({
name: 'Test Rule',
fields: [mockField],
});
expect(result).toBeNull();
expect(localStorage.getItem(STORAGE_KEY)).toBeNull();
});
it('update 在 localStorage 写入失败时应返回 null', () => {
const saved = ruleStorage.save({
name: 'Test Rule',
fields: [mockField],
});
expect(saved).not.toBeNull();
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new DOMException('QuotaExceededError');
});
const updated = ruleStorage.update(saved!.id, { name: 'Updated Rule' });
expect(updated).toBeNull();
});
it('deleteRule 在 localStorage 写入失败时应返回 false', () => {
const saved = ruleStorage.save({
name: 'Test Rule',
fields: [mockField],
});
expect(saved).not.toBeNull();
vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
throw new DOMException('QuotaExceededError');
});
expect(ruleStorage.deleteRule(saved!.id)).toBe(false);
expect(ruleStorage.getById(saved!.id)).toBeDefined();
});
});
+16 -7
View File
@@ -78,7 +78,9 @@ export function save(
useCount: 0,
};
rules.unshift(newRule);
setAll(rules);
if (!setAll(rules)) {
return null;
}
return newRule;
}
@@ -95,7 +97,9 @@ export function save(
updatedAt: now,
};
rules[index] = updatedRule;
setAll(rules);
if (!setAll(rules)) {
return null;
}
return updatedRule;
}
@@ -117,7 +121,9 @@ export function update(id: string, updates: Partial<DataRule>): DataRule | null
updatedAt: Date.now(),
};
rules[index] = updatedRule;
setAll(rules);
if (!setAll(rules)) {
return null;
}
return updatedRule;
}
@@ -132,8 +138,7 @@ export function deleteRule(id: string): boolean {
return false;
}
rules.splice(index, 1);
setAll(rules);
return true;
return setAll(rules);
}
/**
@@ -165,7 +170,9 @@ export function duplicate(id: string, copySuffix = '(副本)'): DataRule | n
const rules = getAll();
rules.unshift(newRule);
setAll(rules);
if (!setAll(rules)) {
return null;
}
return newRule;
}
@@ -267,11 +274,13 @@ export function clear(): void {
/**
* 保存所有规则到存储
*/
function setAll(rules: DataRule[]): void {
function setAll(rules: DataRule[]): boolean {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(rules));
return true;
} catch (error) {
console.error('[ruleStorage] 保存规则失败:', error);
return false;
}
}