|
| 1 | +# Bug Fix: Rule Deletion Issue |
| 2 | + |
| 3 | +## Problem Description |
| 4 | + |
| 5 | +Rules could not be permanently deleted from the system. When a user deleted a rule through the UI, the rule would disappear temporarily but would reappear after clicking the refresh button. |
| 6 | + |
| 7 | +## Root Cause Analysis |
| 8 | + |
| 9 | +The issue was in the `IndexedDBStorageManager.saveRules()` method in `src/utils/indexedDBStorage.ts`. The method was using only `put()` operations to save rules, which would add or update existing rules but would not remove rules that were no longer in the provided array. |
| 10 | + |
| 11 | +### Before Fix |
| 12 | +```typescript |
| 13 | +async saveRules(rules: FilterRule[]): Promise<boolean> { |
| 14 | + try { |
| 15 | + await this.executeTransaction(STORES.RULES, 'readwrite', (store) => { |
| 16 | + const rulesStore = store as IDBObjectStore; |
| 17 | + return Promise.all(rules.map(rule => |
| 18 | + new Promise<void>((resolve, reject) => { |
| 19 | + const request = rulesStore.put(rule); |
| 20 | + request.onsuccess = () => resolve(); |
| 21 | + request.onerror = () => reject(request.error); |
| 22 | + }) |
| 23 | + )); |
| 24 | + }); |
| 25 | + return true; |
| 26 | + } catch (error) { |
| 27 | + console.error('批量保存规则失败:', error); |
| 28 | + return false; |
| 29 | + } |
| 30 | +} |
| 31 | +``` |
| 32 | + |
| 33 | +### Problem Flow |
| 34 | +1. User deletes a rule → `handleDeleteRule` creates new array without deleted rule |
| 35 | +2. `saveRulesToStorage(newRules)` is called with filtered array |
| 36 | +3. `saveRules()` uses `put()` to save remaining rules |
| 37 | +4. **Deleted rule remains in IndexedDB** because `put()` doesn't remove it |
| 38 | +5. When refreshing, `loadRules()` loads all rules from IndexedDB, including the "deleted" one |
| 39 | + |
| 40 | +## Solution |
| 41 | + |
| 42 | +Modified the `saveRules()` method to first clear all existing rules before saving the new ones, ensuring the database state exactly matches the provided rule array. |
| 43 | + |
| 44 | +### After Fix |
| 45 | +```typescript |
| 46 | +async saveRules(rules: FilterRule[]): Promise<boolean> { |
| 47 | + try { |
| 48 | + await this.executeTransaction(STORES.RULES, 'readwrite', (store) => { |
| 49 | + const rulesStore = store as IDBObjectStore; |
| 50 | + |
| 51 | + // First clear all existing rules |
| 52 | + const clearPromise = new Promise<void>((resolve, reject) => { |
| 53 | + const clearRequest = rulesStore.clear(); |
| 54 | + clearRequest.onsuccess = () => resolve(); |
| 55 | + clearRequest.onerror = () => reject(clearRequest.error); |
| 56 | + }); |
| 57 | + |
| 58 | + // Wait for clear to complete, then save new rules |
| 59 | + return clearPromise.then(() => { |
| 60 | + return Promise.all(rules.map(rule => |
| 61 | + new Promise<void>((resolve, reject) => { |
| 62 | + const request = rulesStore.put(rule); |
| 63 | + request.onsuccess = () => resolve(); |
| 64 | + request.onerror = () => reject(request.error); |
| 65 | + }) |
| 66 | + )); |
| 67 | + }); |
| 68 | + }); |
| 69 | + return true; |
| 70 | + } catch (error) { |
| 71 | + console.error('批量保存规则失败:', error); |
| 72 | + return false; |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +## Testing |
| 78 | + |
| 79 | +### Manual Testing with Playwright |
| 80 | +1. Created two test rules |
| 81 | +2. Deleted one rule → Rule count changed from (2/2) to (1/1) |
| 82 | +3. Clicked refresh button → Rule count remained (1/1), deleted rule did not reappear |
| 83 | +4. Deleted remaining rule → Rule count changed to (0/0) |
| 84 | +5. Clicked refresh button → No rules reappeared |
| 85 | + |
| 86 | +### Automated Tests |
| 87 | +Added unit tests in: |
| 88 | +- `src/utils/__tests__/indexedDBStorage.test.ts` - Tests the fix logic |
| 89 | +- `src/components/RuleManager/__tests__/RuleManager.test.tsx` - Tests the complete deletion flow |
| 90 | + |
| 91 | +All tests pass successfully. |
| 92 | + |
| 93 | +## Impact |
| 94 | + |
| 95 | +- ✅ Rules can now be permanently deleted |
| 96 | +- ✅ Refresh button works correctly after deletion |
| 97 | +- ✅ No data corruption or loss |
| 98 | +- ✅ Backward compatible with existing data |
| 99 | +- ✅ Performance impact is minimal (clear + put operations) |
| 100 | + |
| 101 | +## Files Modified |
| 102 | + |
| 103 | +1. `src/utils/indexedDBStorage.ts` - Fixed the `saveRules()` method |
| 104 | +2. `vitest.config.ts` - Added test configuration |
| 105 | +3. `src/test/setup.ts` - Test setup file |
| 106 | +4. `src/utils/__tests__/indexedDBStorage.test.ts` - Unit tests |
| 107 | +5. `src/components/RuleManager/__tests__/RuleManager.test.tsx` - Integration tests |
| 108 | +6. `package.json` - Added test scripts |
| 109 | + |
| 110 | +## Verification |
| 111 | + |
| 112 | +To verify the fix works: |
| 113 | + |
| 114 | +1. Start the development server: `npm run dev` |
| 115 | +2. Open the application and go to Rule Management |
| 116 | +3. Create some test rules |
| 117 | +4. Delete a rule and observe it disappears |
| 118 | +5. Click the refresh button |
| 119 | +6. Verify the deleted rule does not reappear |
| 120 | + |
| 121 | +Or run the automated tests: |
| 122 | +```bash |
| 123 | +npm run test:run |
| 124 | +``` |
0 commit comments