ワンクリックで
md5-tdd-bug-fix-workflow
Test-Driven Development workflow for fixing MD5 hash computation bugs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Test-Driven Development workflow for fixing MD5 hash computation bugs
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | MD5 TDD Bug Fix Workflow |
| description | Test-Driven Development workflow for fixing MD5 hash computation bugs |
| source | auto-skill |
| extracted_at | 2026-06-26T10:26:00.000Z |
This skill describes the TDD approach used to fix MD5 hash computation bugs in the pure-md5 project, specifically for Unicode character handling issues like the em-dash (—) bug reported in issue #21.
The bug involves incorrect MD5 hash computation for Unicode characters. The root cause is typically one of these:
charCodeAt() & 0xff) instead of proper UTF-8 encoding\r\n) instead of LF (\n)toString('binary') instead of direct buffer hashingWrite a test that reproduces the bug before implementing the fix:
test('should handle em-dash character correctly (issue #21)', () => {
// Test with em-dash (—) character
// Expected hash verified against Node.js crypto implementation
expect(md5('—')).toBe('26aeabd0c99c77002e55825fbcd435af');
// Test with string containing em-dash
expect(md5('test—string')).toBe('d25ba991ad3d3deddd0652747ec22a5b');
});
Key points:
crypto.createHash('md5').update().digest('hex') to verify expected valuesCheck these areas in src/md51.ts:
charCodeAt() & 0xff patterns - this is the most common bugs.substring(i - 64, i) instead of working with UTF-8 bytesThe correct implementation should:
function strToUTF8Bytes(s: string): number[] {
const bytes: number[] = [];
for (let i = 0; i < s.length; i++) {
let code = s.charCodeAt(i);
// Handle surrogate pairs for characters outside BMP
if (code >= 0xd800 && code <= 0xdbff && i + 1 < s.length) {
const next = s.charCodeAt(i + 1);
if (next >= 0xdc00 && next <= 0xdfff) {
code = ((code - 0xd800) << 10) + (next - 0xdc00) + 0x10000;
i++; // Skip next char (surrogate pair)
}
}
if (code <= 0x7f) {
// 1 byte (ASCII)
bytes.push(code);
} else if (code <= 0x7ff) {
// 2 bytes
bytes.push(0xc0 | (code >> 6));
bytes.push(0x80 | (code & 0x3f));
} else if (code <= 0xffff) {
// 3 bytes
bytes.push(0xe0 | (code >> 12));
bytes.push(0x80 | ((code >> 6) & 0x3f));
bytes.push(0x80 | (code & 0x3f));
} else {
// 4 bytes (surrogate pair)
bytes.push(0xf0 | (code >> 18));
bytes.push(0x80 | ((code >> 12) & 0x3f));
bytes.push(0x80 | ((code >> 6) & 0x3f));
bytes.push(0x80 | (code & 0x3f));
}
}
return bytes;
}
Process UTF-8 bytes, not string code units
Convert byte arrays to number blocks for md5cycle:
function uint8ArrayToNumberArray(bytes: Uint8Array | number[]): number[] {
const result: number[] = [];
for (let i = 0; i < 16; i++) {
result[i] =
bytes[i * 4] +
(bytes[i * 4 + 1] << 8) +
(bytes[i * 4 + 2] << 16) +
(bytes[i * 4 + 3] << 24);
}
return result;
}
When fixing Unicode handling, update all related tests:
\r\n vs \n)After fixing the core implementation:
npm test to verify all tests passcrypto moduleWhen tests involve hashing fixture files, never hardcode expected hashes from string literals — line endings differ across platforms (CRLF on Windows, LF on Linux/macOS).
import crypto from 'crypto';
import fs from 'fs';
/** Compute expected MD5 hash from file bytes (platform-independent) */
function fileMD5(filePath: string): string {
return crypto.createHash('md5').update(fs.readFileSync(filePath)).digest('hex');
}
// In tests:
test('should hash a text file correctly', async () => {
const result = await hashFile(testFile);
const expected = fileMD5(testFile);
const fileSize = fs.statSync(testFile).size;
expect(result.digest).toBe(expected);
expect(result.bytesProcessed).toBe(fileSize);
});
.gitattributes for fixture normalization# Ensure test files use Unix line endings
__tests__/integration/fixtures/* text eol=lf
test-file.txt with Hello, World!\n has 14 bytes on Linux → MD5 bea8252ff4e80f41719ea13cdf007273Hello, World!\r\n has 15 bytes on Windows → MD5 29b933a8d9a0fcef0af75f1713f4940ecore.autocrlf can silently convert line endings on checkout& 0xff mask - this is wrong for UnicodefileMD5() helper instead of hardcoded stringstoString('binary') produces different results than direct buffer hashingnpm run build after changes if testing the built versionfs.statSync(file).size instead of hardcoded byte counts for fixture filessrc/md51.ts - Core MD5 implementation (primary fix target)src/md5cycle.ts - MD5 cycle function (may need block conversion helper)src/md5blk.ts - Block processing (legacy, may need updates)src/hex.ts - Hex conversion (may need updates)__tests__/index.test.ts - Main MD5 tests__tests__/adapters/*.test.ts - Backend adapter tests__tests__/stream/*.test.ts - Stream tests__tests__/integration/fixtures/ - Test fixture files