@haposoft/cafekit 0.3.2 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,439 @@
1
+ # Edge Case Identification - Xác Định Edge Cases
2
+
3
+ Phương pháp phát hiện và phân tích edge cases để tránh bugs tiềm ẩn.
4
+
5
+ ## Edge Case Categories
6
+
7
+ ### 1. Data Flow Issues
8
+
9
+ #### Null/Undefined Handling
10
+
11
+ ```typescript
12
+ // ❌ Dangerous
13
+ function getUser(id: string) {
14
+ const user = users.find(u => u.id === id);
15
+ return user.name; // Crash if user not found
16
+ }
17
+
18
+ // ✅ Safe
19
+ function getUser(id: string) {
20
+ const user = users.find(u => u.id === id);
21
+ return user?.name ?? 'Unknown';
22
+ }
23
+ ```
24
+
25
+ **Check:**
26
+ - [ ] Có check null/undefined trước khi access?
27
+ - [ ] Optional chaining (`?.`) được sử dụng?
28
+ - [ ] Nullish coalescing (`??`) cho default values?
29
+ - [ ] Type guards cho union types?
30
+
31
+ #### Empty Collections
32
+
33
+ ```typescript
34
+ // ❌ Assumes array has items
35
+ const firstUser = users[0].name;
36
+
37
+ // ✅ Handles empty array
38
+ const firstUser = users.length > 0 ? users[0].name : null;
39
+ ```
40
+
41
+ **Check:**
42
+ - [ ] Array.length check trước khi access index?
43
+ - [ ] Empty array handling trong map/filter/reduce?
44
+ - [ ] Object.keys() check trước khi iterate?
45
+
46
+ #### Type Coercion
47
+
48
+ ```typescript
49
+ // ❌ Implicit coercion
50
+ if (value) { } // false for 0, '', false, null, undefined
51
+
52
+ // ✅ Explicit check
53
+ if (value !== null && value !== undefined) { }
54
+ ```
55
+
56
+ **Check:**
57
+ - [ ] Strict equality (`===`) thay vì loose (`==`)?
58
+ - [ ] Explicit type checks cho boolean logic?
59
+ - [ ] Number validation (isNaN, isFinite)?
60
+
61
+ ### 2. Boundary Conditions
62
+
63
+ #### String Length
64
+
65
+ ```typescript
66
+ // Edge cases
67
+ const inputs = [
68
+ '', // Empty
69
+ 'a', // Single char
70
+ 'x'.repeat(1000), // Very long
71
+ '🎉', // Unicode/emoji
72
+ 'hello\nworld', // Newlines
73
+ ' spaces ' // Leading/trailing spaces
74
+ ];
75
+ ```
76
+
77
+ **Check:**
78
+ - [ ] Min/max length validation?
79
+ - [ ] Trim whitespace?
80
+ - [ ] Unicode/emoji handling?
81
+ - [ ] Special characters escaped?
82
+
83
+ #### Numeric Ranges
84
+
85
+ ```typescript
86
+ // Edge cases
87
+ const numbers = [
88
+ 0, // Zero
89
+ -1, // Negative
90
+ Number.MAX_SAFE_INTEGER, // Max
91
+ Number.MIN_SAFE_INTEGER, // Min
92
+ 0.1 + 0.2, // Float precision
93
+ Infinity, // Infinity
94
+ NaN // Not a number
95
+ ];
96
+ ```
97
+
98
+ **Check:**
99
+ - [ ] Min/max bounds validation?
100
+ - [ ] Zero handling (division by zero)?
101
+ - [ ] Negative number handling?
102
+ - [ ] Float precision issues?
103
+ - [ ] Infinity/NaN checks?
104
+
105
+ #### Date/Time
106
+
107
+ ```typescript
108
+ // Edge cases
109
+ const dates = [
110
+ new Date('2024-02-29'), // Leap year
111
+ new Date('2024-12-31'), // Year boundary
112
+ new Date('invalid'), // Invalid date
113
+ new Date(0), // Unix epoch
114
+ new Date('2024-03-10T02:30:00'), // DST transition
115
+ ];
116
+ ```
117
+
118
+ **Check:**
119
+ - [ ] Timezone handling?
120
+ - [ ] Leap year logic?
121
+ - [ ] Date validation?
122
+ - [ ] DST transitions?
123
+ - [ ] Date range limits?
124
+
125
+ ### 3. Error Scenarios
126
+
127
+ #### Network Failures
128
+
129
+ ```typescript
130
+ // ❌ No error handling
131
+ const data = await fetch('/api/users');
132
+ return data.json();
133
+
134
+ // ✅ Comprehensive error handling
135
+ try {
136
+ const response = await fetch('/api/users');
137
+ if (!response.ok) {
138
+ throw new Error(`HTTP ${response.status}`);
139
+ }
140
+ return await response.json();
141
+ } catch (error) {
142
+ if (error instanceof TypeError) {
143
+ // Network error
144
+ } else {
145
+ // Other errors
146
+ }
147
+ throw error;
148
+ }
149
+ ```
150
+
151
+ **Check:**
152
+ - [ ] Network timeout handling?
153
+ - [ ] HTTP error status handling?
154
+ - [ ] Retry logic for transient failures?
155
+ - [ ] Offline mode support?
156
+
157
+ #### Database Errors
158
+
159
+ ```typescript
160
+ // ❌ No transaction
161
+ await prisma.user.create({ data: userData });
162
+ await prisma.profile.create({ data: profileData });
163
+
164
+ // ✅ Transaction with rollback
165
+ await prisma.$transaction(async (tx) => {
166
+ await tx.user.create({ data: userData });
167
+ await tx.profile.create({ data: profileData });
168
+ });
169
+ ```
170
+
171
+ **Check:**
172
+ - [ ] Transaction boundaries?
173
+ - [ ] Rollback on error?
174
+ - [ ] Unique constraint violations?
175
+ - [ ] Foreign key violations?
176
+ - [ ] Connection pool exhaustion?
177
+
178
+ #### Permission Denied
179
+
180
+ ```typescript
181
+ // ❌ Assumes authorized
182
+ function deleteUser(userId: string) {
183
+ return prisma.user.delete({ where: { id: userId } });
184
+ }
185
+
186
+ // ✅ Authorization check
187
+ function deleteUser(userId: string, currentUserId: string) {
188
+ if (userId !== currentUserId && !isAdmin(currentUserId)) {
189
+ throw new UnauthorizedError();
190
+ }
191
+ return prisma.user.delete({ where: { id: userId } });
192
+ }
193
+ ```
194
+
195
+ **Check:**
196
+ - [ ] Authentication check?
197
+ - [ ] Authorization check?
198
+ - [ ] Role-based access control?
199
+ - [ ] Resource ownership validation?
200
+
201
+ ### 4. Race Conditions
202
+
203
+ #### Concurrent Requests
204
+
205
+ ```typescript
206
+ // ❌ Race condition
207
+ let counter = 0;
208
+ async function increment() {
209
+ const current = counter;
210
+ await delay(10);
211
+ counter = current + 1; // Lost updates!
212
+ }
213
+
214
+ // ✅ Atomic operation
215
+ async function increment() {
216
+ await prisma.counter.update({
217
+ where: { id: 1 },
218
+ data: { value: { increment: 1 } }
219
+ });
220
+ }
221
+ ```
222
+
223
+ **Check:**
224
+ - [ ] Atomic database operations?
225
+ - [ ] Optimistic locking?
226
+ - [ ] Idempotency keys?
227
+ - [ ] Debouncing/throttling?
228
+
229
+ #### State Mutations
230
+
231
+ ```typescript
232
+ // ❌ Shared mutable state
233
+ const state = { count: 0 };
234
+ function increment() {
235
+ state.count++; // Race condition
236
+ }
237
+
238
+ // ✅ Immutable updates
239
+ function increment(state) {
240
+ return { ...state, count: state.count + 1 };
241
+ }
242
+ ```
243
+
244
+ **Check:**
245
+ - [ ] Immutable data structures?
246
+ - [ ] Pure functions?
247
+ - [ ] State management (Redux/Zustand)?
248
+ - [ ] React concurrent mode safe?
249
+
250
+ ### 5. Integration Points
251
+
252
+ #### API Contract Changes
253
+
254
+ ```typescript
255
+ // Old API response
256
+ { "user": { "name": "John" } }
257
+
258
+ // New API response (breaking change!)
259
+ { "data": { "user": { "name": "John" } } }
260
+ ```
261
+
262
+ **Check:**
263
+ - [ ] Backward compatibility?
264
+ - [ ] API versioning?
265
+ - [ ] Response schema validation?
266
+ - [ ] Graceful degradation?
267
+
268
+ #### Event Handlers
269
+
270
+ ```typescript
271
+ // ❌ Missing cleanup
272
+ useEffect(() => {
273
+ window.addEventListener('resize', handleResize);
274
+ }, []);
275
+
276
+ // ✅ Cleanup on unmount
277
+ useEffect(() => {
278
+ window.addEventListener('resize', handleResize);
279
+ return () => window.removeEventListener('resize', handleResize);
280
+ }, []);
281
+ ```
282
+
283
+ **Check:**
284
+ - [ ] Event listener cleanup?
285
+ - [ ] useEffect dependencies correct?
286
+ - [ ] Memory leaks prevented?
287
+ - [ ] Stale closures avoided?
288
+
289
+ ## Edge Case Checklist
290
+
291
+ ### For Every Change
292
+
293
+ ```markdown
294
+ ## Data Flow
295
+ - [ ] Null/undefined handling
296
+ - [ ] Empty collections
297
+ - [ ] Type coercion issues
298
+
299
+ ## Boundaries
300
+ - [ ] String length (empty, very long, unicode)
301
+ - [ ] Numeric ranges (0, negative, max, float)
302
+ - [ ] Date/time (timezone, leap year, DST)
303
+
304
+ ## Errors
305
+ - [ ] Network failures
306
+ - [ ] Database errors
307
+ - [ ] Permission denied
308
+ - [ ] Validation errors
309
+
310
+ ## Concurrency
311
+ - [ ] Race conditions
312
+ - [ ] State mutations
313
+ - [ ] Atomic operations
314
+
315
+ ## Integration
316
+ - [ ] API contract changes
317
+ - [ ] Event handler cleanup
318
+ - [ ] Dependency updates
319
+ ```
320
+
321
+ ## Automated Detection
322
+
323
+ ### Static Analysis
324
+
325
+ ```bash
326
+ # TypeScript strict mode
327
+ tsc --strict --noEmit
328
+
329
+ # ESLint rules
330
+ eslint --rule 'no-unsafe-optional-chaining: error'
331
+ eslint --rule 'no-unsafe-member-access: error'
332
+ ```
333
+
334
+ ### Runtime Checks
335
+
336
+ ```typescript
337
+ // Add assertions
338
+ function divide(a: number, b: number) {
339
+ if (b === 0) throw new Error('Division by zero');
340
+ if (!Number.isFinite(a) || !Number.isFinite(b)) {
341
+ throw new Error('Invalid number');
342
+ }
343
+ return a / b;
344
+ }
345
+ ```
346
+
347
+ ## Edge Case Patterns
348
+
349
+ ### Pattern 1: Guard Clauses
350
+
351
+ ```typescript
352
+ function processUser(user: User | null) {
353
+ // Early returns for edge cases
354
+ if (!user) return null;
355
+ if (!user.email) return null;
356
+ if (user.deleted) return null;
357
+
358
+ // Main logic
359
+ return transformUser(user);
360
+ }
361
+ ```
362
+
363
+ ### Pattern 2: Default Values
364
+
365
+ ```typescript
366
+ function getConfig(options: Partial<Config> = {}) {
367
+ return {
368
+ timeout: options.timeout ?? 5000,
369
+ retries: options.retries ?? 3,
370
+ cache: options.cache ?? true,
371
+ };
372
+ }
373
+ ```
374
+
375
+ ### Pattern 3: Validation Layer
376
+
377
+ ```typescript
378
+ function createUser(data: unknown) {
379
+ // Validate at boundary
380
+ const validated = UserSchema.parse(data);
381
+
382
+ // Safe to use
383
+ return prisma.user.create({ data: validated });
384
+ }
385
+ ```
386
+
387
+ ## Output Format
388
+
389
+ ```json
390
+ {
391
+ "edgeCases": [
392
+ {
393
+ "category": "data-flow",
394
+ "issue": "Null pointer in user.profile.avatar",
395
+ "location": "src/components/Avatar.tsx:15",
396
+ "severity": "high",
397
+ "recommendation": "Add optional chaining: user?.profile?.avatar"
398
+ },
399
+ {
400
+ "category": "boundary",
401
+ "issue": "No max length validation on bio field",
402
+ "location": "src/api/users.ts:45",
403
+ "severity": "medium",
404
+ "recommendation": "Add validation: bio.length <= 500"
405
+ },
406
+ {
407
+ "category": "error",
408
+ "issue": "Network error not handled",
409
+ "location": "src/services/api.ts:23",
410
+ "severity": "high",
411
+ "recommendation": "Add try-catch with retry logic"
412
+ }
413
+ ]
414
+ }
415
+ ```
416
+
417
+ ## Integration with Testing
418
+
419
+ Edge cases identified → Test scenarios:
420
+
421
+ ```typescript
422
+ describe('User API', () => {
423
+ // From edge case analysis
424
+ it('handles null user gracefully', () => {
425
+ expect(getUser(null)).toBe(null);
426
+ });
427
+
428
+ it('validates email length', () => {
429
+ const longEmail = 'a'.repeat(300) + '@test.com';
430
+ expect(() => createUser({ email: longEmail }))
431
+ .toThrow('Email too long');
432
+ });
433
+
434
+ it('handles network timeout', async () => {
435
+ mockFetch.mockRejectedValue(new Error('timeout'));
436
+ await expect(fetchUsers()).rejects.toThrow();
437
+ });
438
+ });
439
+ ```