pure-md5 0.2.0 → 0.2.2
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.
- package/README.md +38 -24
- package/dist/adapters/ie11.cjs +1 -2
- package/dist/adapters/ie11.js +1 -2
- package/dist/adapters/node.cjs +1 -2
- package/dist/adapters/node.js +1 -2
- package/dist/adapters/webcrypto.cjs +1 -2
- package/dist/adapters/webcrypto.js +1 -2
- package/dist/index.cjs +1 -2
- package/dist/index.d.ts +149 -5
- package/dist/index.js +3 -2
- package/dist/md5.cjs +1 -0
- package/dist/md5.d.ts +20 -0
- package/dist/md5.js +1 -0
- package/dist/stream/md5-stream.cjs +1 -2
- package/dist/stream/md5-stream.js +1 -2
- package/dist/stream/whatwg-stream.cjs +1 -2
- package/dist/stream/whatwg-stream.js +1 -2
- package/dist/utils/detect.cjs +1 -2
- package/dist/utils/detect.js +3 -2
- package/package.json +10 -15
- package/pure-md5-0.2.1.tgz +0 -0
- package/test-tree-shake.mjs +12 -0
- package/.aliases +0 -19
- package/.bash_profile +0 -12
- package/.bash_prompt +0 -56
- package/.changeset/README.md +0 -32
- package/.changeset/config.json +0 -16
- package/.continue/mcpServers/new-mcp-server.yaml +0 -10
- package/.continue/rules +0 -29
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- package/.github/ISSUE_TEMPLATE/documentation.md +0 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -35
- package/.github/workflows/npm-publish.yml +0 -33
- package/.github/workflows/release.yml +0 -42
- package/CHANGELOG.md +0 -9
- package/CONTRIBUTING.md +0 -203
- package/MIGRATION_GUIDE_STREAMS.md +0 -374
- package/STREAM_API.md +0 -582
- package/STREAM_BENCHMARKS.md +0 -232
- package/STREAM_EXAMPLES.md +0 -669
- package/STREAM_OPTIMIZATION_REPORT.md +0 -136
- package/STREAM_TROUBLESHOOTING.md +0 -537
- package/WEB_CRYPTO_TESTS_SUMMARY.md +0 -140
- package/WHATWG_STREAMS.md +0 -191
- package/__tests__/adapters/node-crypto.test.ts +0 -167
- package/__tests__/adapters/web-crypto-node.test.ts +0 -73
- package/__tests__/adapters/web-crypto.test.ts +0 -195
- package/__tests__/add32.test.ts +0 -33
- package/__tests__/fallback.test.ts +0 -345
- package/__tests__/hex.test.ts +0 -38
- package/__tests__/hex_chr.test.ts +0 -20
- package/__tests__/index.test.ts +0 -87
- package/__tests__/integration/fixtures/test-file.txt +0 -1
- package/__tests__/integration/md5-stream-file.test.ts +0 -293
- package/__tests__/integration/node-crypto-file.test.ts +0 -86
- package/__tests__/integration/web-crypto.test.ts +0 -38
- package/__tests__/md51.test.ts +0 -73
- package/__tests__/md5block.test.ts +0 -61
- package/__tests__/md5cycle.test.ts +0 -48
- package/__tests__/round-functions.test.ts +0 -87
- package/__tests__/stream/fs-utils.test.ts +0 -209
- package/__tests__/stream/md5-stream-edge-cases.test.ts +0 -461
- package/__tests__/stream/md5-stream.test.ts +0 -418
- package/__tests__/stream/whatwg-stream.test.ts +0 -355
- package/__tests__/stream/whatwg-stream.test.ts.bak2 +0 -335
- package/benchmarks/md5-stream.bench.ts +0 -212
- package/benchmarks/whatwg-stream.bench.ts +0 -180
- package/dist/adapters/ie11.cjs.map +0 -1
- package/dist/adapters/ie11.js.map +0 -1
- package/dist/adapters/node.cjs.map +0 -1
- package/dist/adapters/node.js.map +0 -1
- package/dist/adapters/webcrypto.cjs.map +0 -1
- package/dist/adapters/webcrypto.js.map +0 -1
- package/dist/chunk-2YXXFGBV.js +0 -2
- package/dist/chunk-2YXXFGBV.js.map +0 -1
- package/dist/chunk-4KSCMS4Q.js +0 -2
- package/dist/chunk-4KSCMS4Q.js.map +0 -1
- package/dist/chunk-6P2QV5SR.js +0 -4
- package/dist/chunk-6P2QV5SR.js.map +0 -1
- package/dist/chunk-G5WHEAIQ.js +0 -2
- package/dist/chunk-G5WHEAIQ.js.map +0 -1
- package/dist/chunk-H2K353LR.js +0 -2
- package/dist/chunk-H2K353LR.js.map +0 -1
- package/dist/chunk-JKVD5LHZ.js +0 -2
- package/dist/chunk-JKVD5LHZ.js.map +0 -1
- package/dist/chunk-NWQ4N5RX.js +0 -2
- package/dist/chunk-NWQ4N5RX.js.map +0 -1
- package/dist/chunk-PHZ7FTYF.js +0 -2
- package/dist/chunk-PHZ7FTYF.js.map +0 -1
- package/dist/chunk-PNZTVQA7.js +0 -2
- package/dist/chunk-PNZTVQA7.js.map +0 -1
- package/dist/chunk-R4JB5MBR.js +0 -2
- package/dist/chunk-R4JB5MBR.js.map +0 -1
- package/dist/chunk-VFOAY6XI.js +0 -2
- package/dist/chunk-VFOAY6XI.js.map +0 -1
- package/dist/chunk-XB5BQIEX.js +0 -2
- package/dist/chunk-XB5BQIEX.js.map +0 -1
- package/dist/core/index.cjs +0 -2
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -19
- package/dist/core/index.d.ts +0 -19
- package/dist/core/index.js +0 -2
- package/dist/core/index.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -84
- package/dist/index.js.map +0 -1
- package/dist/stream/adapter.cjs +0 -2
- package/dist/stream/adapter.cjs.map +0 -1
- package/dist/stream/adapter.d.cts +0 -63
- package/dist/stream/adapter.d.ts +0 -63
- package/dist/stream/adapter.js +0 -2
- package/dist/stream/adapter.js.map +0 -1
- package/dist/stream/fs-utils.cjs +0 -2
- package/dist/stream/fs-utils.cjs.map +0 -1
- package/dist/stream/fs-utils.d.cts +0 -137
- package/dist/stream/fs-utils.d.ts +0 -137
- package/dist/stream/fs-utils.js +0 -2
- package/dist/stream/fs-utils.js.map +0 -1
- package/dist/stream/index.cjs +0 -2
- package/dist/stream/index.cjs.map +0 -1
- package/dist/stream/index.d.cts +0 -4
- package/dist/stream/index.d.ts +0 -4
- package/dist/stream/index.js +0 -2
- package/dist/stream/index.js.map +0 -1
- package/dist/stream/light/index.cjs +0 -2
- package/dist/stream/light/index.cjs.map +0 -1
- package/dist/stream/light/index.d.cts +0 -4
- package/dist/stream/light/index.d.ts +0 -4
- package/dist/stream/light/index.js +0 -2
- package/dist/stream/light/index.js.map +0 -1
- package/dist/stream/md5-stream.cjs.map +0 -1
- package/dist/stream/md5-stream.js.map +0 -1
- package/dist/stream/whatwg-stream.cjs.map +0 -1
- package/dist/stream/whatwg-stream.js.map +0 -1
- package/dist/types-edGoGJ5V.d.cts +0 -42
- package/dist/types-edGoGJ5V.d.ts +0 -42
- package/dist/utils/detect.cjs.map +0 -1
- package/dist/utils/detect.js.map +0 -1
- package/planning/03-optimization-size-tree-shaking/01-es-modules-tree-shaking.md +0 -152
- package/planning/03-optimization-size-tree-shaking/02-consolidate-modules.md +0 -65
- package/planning/03-optimization-size-tree-shaking/03-remove-duplicate-add32.md +0 -93
- package/planning/03-optimization-size-tree-shaking/04-remove-runtime-check.md +0 -102
- package/planning/03-optimization-size-tree-shaking/05-optimize-loops-performance.md +0 -107
- package/planning/03-optimization-size-tree-shaking/06-tsup-formats-configuration.md +0 -227
- package/planning/03-optimization-size-tree-shaking/07-multiple-build-formats.md +0 -228
- package/planning/03-optimization-size-tree-shaking/08-benchmarks-metrics.md +0 -34
- package/planning/03-optimization-size-tree-shaking/MIGRATION_GUIDE.md +0 -260
- package/planning/03-optimization-size-tree-shaking/README.md +0 -173
- package/planning/03-optimization-size-tree-shaking/SUMMARY.md +0 -168
- package/planning/04-adapter-backend/03-backend-web-crypto.md +0 -149
- package/planning/04-adapter-backend/04-backend-node-crypto.md +0 -181
- package/planning/04-adapter-backend/05-backend-pure-js.md +0 -174
- package/planning/04-adapter-backend/06-backend-ie11.md +0 -158
- package/planning/04-adapter-backend/07-detection-environment.md +0 -232
- package/planning/04-adapter-backend/08-detection-backend.md +0 -210
- package/planning/04-adapter-backend/09-adapter-unified.md +0 -255
- package/planning/04-adapter-backend/10-fallback-mechanism.md +0 -333
- package/planning/04-adapter-backend/11-tests-backend-web-crypto.md +0 -191
- package/planning/04-adapter-backend/12-tests-backend-node-crypto.md +0 -222
- package/planning/04-adapter-backend/README.md +0 -45
- package/planning/05-documentation-publishing/01-README-optimization.md +0 -105
- package/planning/05-documentation-publishing/02-VitePress-site-evaluation.md +0 -136
- package/planning/05-documentation-publishing/03-Changeset-setup.md +0 -192
- package/planning/05-documentation-publishing/04-GitHub-templates.md +0 -252
- package/planning/05-documentation-publishing/README.md +0 -22
- package/planning/05-documentation-publishing/STATUS.md +0 -222
- package/planning/prd.md +0 -405
- package/planning/streams/01-create-md5stream-class.md +0 -69
- package/planning/streams/02-create-factory-api.md +0 -65
- package/planning/streams/03-fs-integration.md +0 -37
- package/planning/streams/04-whatwg-streams-support.md +0 -37
- package/planning/streams/05-audit-optimization.md +0 -121
- package/planning/streams/06-comprehensive-tests-docs.md +0 -137
- package/planning/streams/07-architecture-integration.md +0 -38
- package/planning/streams/README.md +0 -98
- package/tsup.config.ts +0 -24
package/__tests__/index.test.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { md5 } from '../src/index';
|
|
2
|
-
|
|
3
|
-
describe('md5', () => {
|
|
4
|
-
// Test vectors from RFC 1321
|
|
5
|
-
test('should compute correct MD5 hash for RFC 1321 test vectors', () => {
|
|
6
|
-
// Test 1: empty string
|
|
7
|
-
expect(md5('')).toBe('d41d8cd98f00b204e9800998ecf8427e');
|
|
8
|
-
|
|
9
|
-
// Test 2: "a"
|
|
10
|
-
expect(md5('a')).toBe('0cc175b9c0f1b6a831c399e269772661');
|
|
11
|
-
|
|
12
|
-
// Test 3: "abc"
|
|
13
|
-
expect(md5('abc')).toBe('900150983cd24fb0d6963f7d28e17f72');
|
|
14
|
-
|
|
15
|
-
// Test 4: "message digest"
|
|
16
|
-
expect(md5('message digest')).toBe('f96b697d7cb7938d525a2f31aaf161d0');
|
|
17
|
-
|
|
18
|
-
// Test 5: "abcdefghijklmnopqrstuvwxyz"
|
|
19
|
-
expect(md5('abcdefghijklmnopqrstuvwxyz')).toBe(
|
|
20
|
-
'c3fcd3d76192e4007dfb496cca67e13b'
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
// Test 6: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
24
|
-
expect(
|
|
25
|
-
md5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
|
|
26
|
-
).toBe('d174ab98d277d9f5a5611c2c9f419d9f');
|
|
27
|
-
|
|
28
|
-
// Test 7: "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
|
|
29
|
-
expect(
|
|
30
|
-
md5(
|
|
31
|
-
'12345678901234567890123456789012345678901234567890123456789012345678901234567890'
|
|
32
|
-
)
|
|
33
|
-
).toBe('57edf4a22be3c955ac49da2e2107b67a');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('should compute correct MD5 hash for common strings', () => {
|
|
37
|
-
// Test from the code itself
|
|
38
|
-
expect(md5('hello')).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
39
|
-
|
|
40
|
-
// Additional common test cases
|
|
41
|
-
expect(md5('Hello, World!')).toBe('65a8e27d8879283831b664bd8b7f0ad4');
|
|
42
|
-
expect(md5('The quick brown fox jumps over the lazy dog')).toBe(
|
|
43
|
-
'9e107d9d372bb6826bd81d3542a419d6'
|
|
44
|
-
);
|
|
45
|
-
expect(md5('The quick brown fox jumps over the lazy dog.')).toBe(
|
|
46
|
-
'e4d909c290d0fb1ca068ffaddf22cbd0'
|
|
47
|
-
);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('should handle edge cases', () => {
|
|
51
|
-
// Very long string
|
|
52
|
-
const longString = 'x'.repeat(10000);
|
|
53
|
-
const hash = md5(longString);
|
|
54
|
-
expect(hash).toMatch(/^[0-9a-f]{32}$/);
|
|
55
|
-
|
|
56
|
-
// Single character
|
|
57
|
-
expect(md5('x')).toBe('9dd4e461268c8034f5c8564e155c67a6');
|
|
58
|
-
|
|
59
|
-
// String with null bytes
|
|
60
|
-
expect(md5('test\0null')).toBe('530923740caad7aad6a2cd4b894d983d');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('should produce consistent results', () => {
|
|
64
|
-
const input = 'test string';
|
|
65
|
-
const hash1 = md5(input);
|
|
66
|
-
const hash2 = md5(input);
|
|
67
|
-
const hash3 = md5(input);
|
|
68
|
-
|
|
69
|
-
expect(hash1).toBe(hash2);
|
|
70
|
-
expect(hash2).toBe(hash3);
|
|
71
|
-
expect(hash1).toMatch(/^[0-9a-f]{32}$/);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('should handle Unicode strings (using UTF-16 code units)', () => {
|
|
75
|
-
// Note: This implementation uses charCodeAt() which returns UTF-16 code units
|
|
76
|
-
// This is different from UTF-8 encoding used by some other MD5 implementations
|
|
77
|
-
expect(md5('🎉')).toBe('801c2a6dbe01c0c530a03a5f5f5b1214');
|
|
78
|
-
expect(md5('Привет мир')).toBe('5abca3326cf0cefc00efe7065b5e0cf6'); // "Hello world" in Russian
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test('should verify the built-in check works', () => {
|
|
82
|
-
// The function has a built-in check for 'hello'
|
|
83
|
-
// This ensures the runtime check doesn't break functionality
|
|
84
|
-
const result = md5('hello');
|
|
85
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Hello, World!
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for MD5Stream with real file I/O
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { MD5Stream, hashFile, hashFileSync, verifyFile, hashFileDigest } from '../../src/stream/index.js';
|
|
8
|
-
import { md5Core } from '../../src/core/index.js';
|
|
9
|
-
|
|
10
|
-
describe('MD5Stream - File I/O Integration', () => {
|
|
11
|
-
let tempDir: string;
|
|
12
|
-
|
|
13
|
-
beforeAll(() => {
|
|
14
|
-
tempDir = path.join(__dirname, 'temp');
|
|
15
|
-
if (!fs.existsSync(tempDir)) {
|
|
16
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterAll(() => {
|
|
21
|
-
// Cleanup temp files
|
|
22
|
-
if (fs.existsSync(tempDir)) {
|
|
23
|
-
const files = fs.readdirSync(tempDir);
|
|
24
|
-
files.forEach(file => {
|
|
25
|
-
const filePath = path.join(tempDir, file);
|
|
26
|
-
if (fs.existsSync(filePath)) {
|
|
27
|
-
fs.unlinkSync(filePath);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
fs.rmdirSync(tempDir);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('hashFile (async)', () => {
|
|
35
|
-
test('should hash a small text file', async () => {
|
|
36
|
-
const filePath = path.join(tempDir, 'test-small.txt');
|
|
37
|
-
const content = 'Hello, World!';
|
|
38
|
-
fs.writeFileSync(filePath, content);
|
|
39
|
-
|
|
40
|
-
const result = await hashFile(filePath);
|
|
41
|
-
|
|
42
|
-
expect(result.digest).toBe(md5Core(content));
|
|
43
|
-
expect(result.bytesProcessed).toBe(content.length);
|
|
44
|
-
expect(result.digest.length).toBe(32);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('should hash an empty file', async () => {
|
|
48
|
-
const filePath = path.join(tempDir, 'test-empty.txt');
|
|
49
|
-
fs.writeFileSync(filePath, '');
|
|
50
|
-
|
|
51
|
-
const result = await hashFile(filePath);
|
|
52
|
-
|
|
53
|
-
expect(result.digest).toBe(md5Core(''));
|
|
54
|
-
expect(result.bytesProcessed).toBe(0);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('should handle file path as string', async () => {
|
|
58
|
-
const filePath = path.join(tempDir, 'test-path.txt');
|
|
59
|
-
const content = 'Path test content';
|
|
60
|
-
fs.writeFileSync(filePath, content);
|
|
61
|
-
|
|
62
|
-
const result = await hashFile(filePath);
|
|
63
|
-
|
|
64
|
-
expect(result.digest).toBe(md5Core(content));
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('should handle large files efficiently', async () => {
|
|
68
|
-
const filePath = path.join(tempDir, 'test-large.bin');
|
|
69
|
-
const size = 10 * 1024 * 1024; // 10MB
|
|
70
|
-
const content = 'a'.repeat(size);
|
|
71
|
-
fs.writeFileSync(filePath, content);
|
|
72
|
-
|
|
73
|
-
const start = Date.now();
|
|
74
|
-
const result = await hashFile(filePath);
|
|
75
|
-
const duration = Date.now() - start;
|
|
76
|
-
|
|
77
|
-
expect(result.bytesProcessed).toBe(size);
|
|
78
|
-
expect(result.digest.length).toBe(32);
|
|
79
|
-
expect(duration).toBeLessThan(5000); // Should complete in under 5 seconds
|
|
80
|
-
}, 10000);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('hashFileSync (sync)', () => {
|
|
84
|
-
test('should hash a small file synchronously', () => {
|
|
85
|
-
const filePath = path.join(tempDir, 'test-sync.txt');
|
|
86
|
-
const content = 'Sync hash test';
|
|
87
|
-
fs.writeFileSync(filePath, content);
|
|
88
|
-
|
|
89
|
-
const digest = hashFileSync(filePath);
|
|
90
|
-
|
|
91
|
-
expect(digest).toBe(md5Core(content));
|
|
92
|
-
expect(digest.length).toBe(32);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('should hash empty file synchronously', () => {
|
|
96
|
-
const filePath = path.join(tempDir, 'test-sync-empty.txt');
|
|
97
|
-
fs.writeFileSync(filePath, '');
|
|
98
|
-
|
|
99
|
-
const digest = hashFileSync(filePath);
|
|
100
|
-
|
|
101
|
-
expect(digest).toBe(md5Core(''));
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('should be faster than async for small files', () => {
|
|
105
|
-
const filePath = path.join(tempDir, 'test-sync-performance.txt');
|
|
106
|
-
const content = 'a'.repeat(1000);
|
|
107
|
-
fs.writeFileSync(filePath, content);
|
|
108
|
-
|
|
109
|
-
const startSync = Date.now();
|
|
110
|
-
hashFileSync(filePath);
|
|
111
|
-
const syncDuration = Date.now() - startSync;
|
|
112
|
-
|
|
113
|
-
expect(syncDuration).toBeLessThan(100); // Sync should be fast for small files
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('hashFileDigest', () => {
|
|
118
|
-
test('should return only digest string', async () => {
|
|
119
|
-
const filePath = path.join(tempDir, 'test-digest.txt');
|
|
120
|
-
const content = 'Digest only test';
|
|
121
|
-
fs.writeFileSync(filePath, content);
|
|
122
|
-
|
|
123
|
-
const digest = await hashFileDigest(filePath);
|
|
124
|
-
|
|
125
|
-
expect(typeof digest).toBe('string');
|
|
126
|
-
expect(digest).toBe(md5Core(content));
|
|
127
|
-
expect(digest.length).toBe(32);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('verifyFile', () => {
|
|
132
|
-
test('should verify correct hash', async () => {
|
|
133
|
-
const filePath = path.join(tempDir, 'test-verify.txt');
|
|
134
|
-
const content = 'Verification test';
|
|
135
|
-
const correctDigest = md5Core(content);
|
|
136
|
-
fs.writeFileSync(filePath, content);
|
|
137
|
-
|
|
138
|
-
const isVerified = await verifyFile(filePath, correctDigest);
|
|
139
|
-
|
|
140
|
-
expect(isVerified).toBe(true);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test('should reject incorrect hash', async () => {
|
|
144
|
-
const filePath = path.join(tempDir, 'test-verify-wrong.txt');
|
|
145
|
-
const content = 'Verification test wrong';
|
|
146
|
-
const wrongDigest = '00000000000000000000000000000000';
|
|
147
|
-
fs.writeFileSync(filePath, content);
|
|
148
|
-
|
|
149
|
-
const isVerified = await verifyFile(filePath, wrongDigest);
|
|
150
|
-
|
|
151
|
-
expect(isVerified).toBe(false);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('should handle file not found', async () => {
|
|
155
|
-
const filePath = path.join(tempDir, 'test-verify-not-found.txt');
|
|
156
|
-
const digest = '00000000000000000000000000000000';
|
|
157
|
-
|
|
158
|
-
await expect(verifyFile(filePath, digest)).rejects.toThrow();
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
describe('MD5Stream with file streams', () => {
|
|
163
|
-
test('should hash file using fs.createReadStream', (done) => {
|
|
164
|
-
const filePath = path.join(tempDir, 'test-stream.txt');
|
|
165
|
-
const content = 'Stream file test';
|
|
166
|
-
fs.writeFileSync(filePath, content);
|
|
167
|
-
|
|
168
|
-
const stream = new MD5Stream();
|
|
169
|
-
|
|
170
|
-
stream.on('md5', (result) => {
|
|
171
|
-
expect(result.digest).toBe(md5Core(content));
|
|
172
|
-
expect(result.bytesProcessed).toBe(content.length);
|
|
173
|
-
done();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
fs.createReadStream(filePath).pipe(stream);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test('should handle file stream errors gracefully', (done) => {
|
|
180
|
-
const stream = new MD5Stream();
|
|
181
|
-
|
|
182
|
-
const source = fs.createReadStream('/nonexistent/file.txt');
|
|
183
|
-
|
|
184
|
-
// Source stream will emit error, but it should not hang
|
|
185
|
-
source.on('error', (error) => {
|
|
186
|
-
// Error is on source stream, not MD5Stream
|
|
187
|
-
// This is expected behavior - the source stream emits the error
|
|
188
|
-
expect(error).toBeDefined();
|
|
189
|
-
done();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// MD5Stream will just see end-of-stream since source fails early
|
|
193
|
-
stream.on('md5', () => {
|
|
194
|
-
// This shouldn't happen for non-existent file
|
|
195
|
-
done();
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
source.pipe(stream);
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
describe('Progress tracking', () => {
|
|
203
|
-
test('should support progress callback', (done) => {
|
|
204
|
-
const filePath = path.join(tempDir, 'test-progress.txt');
|
|
205
|
-
const content = 'a'.repeat(10000);
|
|
206
|
-
fs.writeFileSync(filePath, content);
|
|
207
|
-
|
|
208
|
-
// Note: Current implementation doesn't have built-in progress
|
|
209
|
-
// This is a placeholder for future implementation
|
|
210
|
-
// For now, we just verify the file can be hashed
|
|
211
|
-
hashFile(filePath).then((result) => {
|
|
212
|
-
expect(result.bytesProcessed).toBe(content.length);
|
|
213
|
-
done();
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('Edge Cases', () => {
|
|
219
|
-
test('should handle special characters in filename', async () => {
|
|
220
|
-
const specialFileName = 'test with spaces & special!@#.txt';
|
|
221
|
-
const filePath = path.join(tempDir, specialFileName);
|
|
222
|
-
const content = 'Special filename test';
|
|
223
|
-
fs.writeFileSync(filePath, content);
|
|
224
|
-
|
|
225
|
-
const result = await hashFile(filePath);
|
|
226
|
-
|
|
227
|
-
expect(result.digest).toBe(md5Core(content));
|
|
228
|
-
|
|
229
|
-
// Cleanup
|
|
230
|
-
if (fs.existsSync(filePath)) {
|
|
231
|
-
fs.unlinkSync(filePath);
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
test('should handle binary file content', async () => {
|
|
236
|
-
const filePath = path.join(tempDir, 'test-binary.bin');
|
|
237
|
-
const content = Buffer.alloc(100);
|
|
238
|
-
for (let i = 0; i < 100; i++) {
|
|
239
|
-
content[i] = i % 256;
|
|
240
|
-
}
|
|
241
|
-
fs.writeFileSync(filePath, content);
|
|
242
|
-
|
|
243
|
-
const result = await hashFile(filePath);
|
|
244
|
-
|
|
245
|
-
expect(result.digest.length).toBe(32);
|
|
246
|
-
expect(result.bytesProcessed).toBe(100);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('should handle unicode filename', async () => {
|
|
250
|
-
const specialFileName = 'тест unicode 文件.txt';
|
|
251
|
-
const filePath = path.join(tempDir, specialFileName);
|
|
252
|
-
const content = 'Unicode filename test';
|
|
253
|
-
fs.writeFileSync(filePath, content);
|
|
254
|
-
|
|
255
|
-
const result = await hashFile(filePath);
|
|
256
|
-
|
|
257
|
-
expect(result.digest).toBe(md5Core(content));
|
|
258
|
-
|
|
259
|
-
// Cleanup
|
|
260
|
-
if (fs.existsSync(filePath)) {
|
|
261
|
-
fs.unlinkSync(filePath);
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe('Consistency', () => {
|
|
267
|
-
test('should produce same hash for same file', async () => {
|
|
268
|
-
const filePath = path.join(tempDir, 'test-consistency.txt');
|
|
269
|
-
const content = 'Consistency test';
|
|
270
|
-
fs.writeFileSync(filePath, content);
|
|
271
|
-
|
|
272
|
-
const result1 = await hashFile(filePath);
|
|
273
|
-
const result2 = await hashFile(filePath);
|
|
274
|
-
const result3 = await hashFile(filePath);
|
|
275
|
-
|
|
276
|
-
expect(result1.digest).toBe(result2.digest);
|
|
277
|
-
expect(result2.digest).toBe(result3.digest);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test('should produce different hashes for different files', async () => {
|
|
281
|
-
const file1Path = path.join(tempDir, 'test-diff1.txt');
|
|
282
|
-
const file2Path = path.join(tempDir, 'test-diff2.txt');
|
|
283
|
-
|
|
284
|
-
fs.writeFileSync(file1Path, 'Content 1');
|
|
285
|
-
fs.writeFileSync(file2Path, 'Content 2');
|
|
286
|
-
|
|
287
|
-
const result1 = await hashFile(file1Path);
|
|
288
|
-
const result2 = await hashFile(file2Path);
|
|
289
|
-
|
|
290
|
-
expect(result1.digest).not.toBe(result2.digest);
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for Node.js Crypto Backend with files
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import fs from 'fs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { NodeCryptoBackend } from '../../src/adapters/node.js';
|
|
8
|
-
|
|
9
|
-
describe('NodeCryptoBackend with Files', () => {
|
|
10
|
-
let backend: NodeCryptoBackend;
|
|
11
|
-
|
|
12
|
-
beforeAll(() => {
|
|
13
|
-
if (!NodeCryptoBackend.isAvailable()) {
|
|
14
|
-
console.log('Node.js Crypto not available, skipping tests');
|
|
15
|
-
pending('Node.js Crypto not available');
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
backend = new NodeCryptoBackend();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should hash a file', async () => {
|
|
22
|
-
if (!backend) {
|
|
23
|
-
pending('Node.js Crypto not available');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const filePath = path.join(__dirname, 'fixtures', 'test-file.txt');
|
|
28
|
-
|
|
29
|
-
if (!fs.existsSync(filePath)) {
|
|
30
|
-
console.log('Test file not found, creating it');
|
|
31
|
-
const fixturesDir = path.dirname(filePath);
|
|
32
|
-
if (!fs.existsSync(fixturesDir)) {
|
|
33
|
-
fs.mkdirSync(fixturesDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
fs.writeFileSync(filePath, 'Hello, World!');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const content = fs.readFileSync(filePath);
|
|
39
|
-
const result = await backend.hashBinary(content);
|
|
40
|
-
expect(result.length).toBe(32);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should handle large files efficiently', async () => {
|
|
44
|
-
if (!backend) {
|
|
45
|
-
pending('Node.js Crypto not available');
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const filePath = path.join(__dirname, 'fixtures', 'large-file.bin');
|
|
50
|
-
|
|
51
|
-
if (!fs.existsSync(filePath)) {
|
|
52
|
-
console.log('Large file not found, skipping');
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const content = fs.readFileSync(filePath);
|
|
57
|
-
const start = Date.now();
|
|
58
|
-
const result = await backend.hashBinary(content);
|
|
59
|
-
const duration = Date.now() - start;
|
|
60
|
-
|
|
61
|
-
expect(result.length).toBe(32);
|
|
62
|
-
expect(duration).toBeLessThan(500);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('should produce consistent hash for the same file', async () => {
|
|
66
|
-
if (!backend) {
|
|
67
|
-
pending('Node.js Crypto not available');
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const filePath = path.join(__dirname, 'fixtures', 'test-file.txt');
|
|
72
|
-
|
|
73
|
-
if (!fs.existsSync(filePath)) {
|
|
74
|
-
const fixturesDir = path.dirname(filePath);
|
|
75
|
-
if (!fs.existsSync(fixturesDir)) {
|
|
76
|
-
fs.mkdirSync(fixturesDir, { recursive: true });
|
|
77
|
-
}
|
|
78
|
-
fs.writeFileSync(filePath, 'Consistent Test Data');
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const content = fs.readFileSync(filePath);
|
|
82
|
-
const result1 = await backend.hashBinary(content);
|
|
83
|
-
const result2 = await backend.hashBinary(content);
|
|
84
|
-
expect(result1).toBe(result2);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for Web Crypto Backend
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { md5, md5Adapter, getAllAvailableBackends } from '../../src/index';
|
|
6
|
-
|
|
7
|
-
describe('WebCrypto Backend Integration', () => {
|
|
8
|
-
describe('md5 function', () => {
|
|
9
|
-
it('should use default backend correctly', async () => {
|
|
10
|
-
const result = await md5('hello');
|
|
11
|
-
expect(result).toBe('5d41402abc4b2a76b9719d911017c592');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('should handle empty string', async () => {
|
|
15
|
-
const result = await md5('');
|
|
16
|
-
expect(result).toBe('d41d8cd98f00b204e9800998ecf8427e');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should handle unicode', async () => {
|
|
20
|
-
const result = await md5('Привет мир');
|
|
21
|
-
expect(result).toBe('5abca3326cf0cefc00efe7065b5e0cf6');
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('Backend availability', () => {
|
|
26
|
-
it('should detect available backends', async () => {
|
|
27
|
-
const available = await getAllAvailableBackends();
|
|
28
|
-
expect(Array.isArray(available)).toBe(true);
|
|
29
|
-
expect(available.length).toBeGreaterThan(0);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should use md5Adapter with webcrypto', async () => {
|
|
33
|
-
// Check if WebCrypto is available
|
|
34
|
-
const webcryptoAvailable = (md5Adapter as any).createBackendByName('webcrypto');
|
|
35
|
-
expect(webcryptoAvailable).toBeDefined();
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
});
|
package/__tests__/md51.test.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import md51 from '../src/md51';
|
|
2
|
-
|
|
3
|
-
describe('md51', () => {
|
|
4
|
-
test('should compute MD5 hash for empty string', () => {
|
|
5
|
-
const result = md51('');
|
|
6
|
-
|
|
7
|
-
// MD5 of empty string: d41d8cd98f00b204e9800998ecf8427e
|
|
8
|
-
// In little-endian words: [0xd98cd41d, 0x04b2008f, 0x980099e9, 0x7e42f8ec]
|
|
9
|
-
// But let's just verify it produces 4 numbers
|
|
10
|
-
expect(result).toHaveLength(4);
|
|
11
|
-
result.forEach((val) => {
|
|
12
|
-
expect(Number.isInteger(val)).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('should compute MD5 hash for "hello"', () => {
|
|
17
|
-
const result = md51('hello');
|
|
18
|
-
|
|
19
|
-
// MD5 of "hello": 5d41402abc4b2a76b9719d911017c592
|
|
20
|
-
// In little-endian words: [0x2a40415d, 0x762a4bbc, 0x9d71b9b9, 0x92c51701]
|
|
21
|
-
expect(result).toHaveLength(4);
|
|
22
|
-
|
|
23
|
-
// Convert to hex and verify
|
|
24
|
-
const hexResult = result
|
|
25
|
-
.map((n) => (n >>> 0).toString(16).padStart(8, '0'))
|
|
26
|
-
.join('');
|
|
27
|
-
// Note: the hex function reverses byte order, so we need to account for that
|
|
28
|
-
// For now, just verify we get consistent results
|
|
29
|
-
expect(hexResult).toBeDefined();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('should handle strings longer than 64 characters', () => {
|
|
33
|
-
const longString = 'a'.repeat(100);
|
|
34
|
-
const result = md51(longString);
|
|
35
|
-
|
|
36
|
-
expect(result).toHaveLength(4);
|
|
37
|
-
result.forEach((val) => {
|
|
38
|
-
expect(Number.isInteger(val)).toBe(true);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test('should handle strings exactly 64 characters', () => {
|
|
43
|
-
const exact64 = 'a'.repeat(64);
|
|
44
|
-
const result = md51(exact64);
|
|
45
|
-
|
|
46
|
-
expect(result).toHaveLength(4);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('should handle custom add32 functions', () => {
|
|
50
|
-
const customAdd32 = (x: number, y: number) => (x + y) & 0xffffffff;
|
|
51
|
-
const result = md51('test', customAdd32);
|
|
52
|
-
|
|
53
|
-
expect(result).toHaveLength(4);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('should handle strings with special characters', () => {
|
|
57
|
-
const testCases = [
|
|
58
|
-
'Hello, World!',
|
|
59
|
-
'1234567890',
|
|
60
|
-
'!@#$%^&*()',
|
|
61
|
-
'Привет мир', // Cyrillic text
|
|
62
|
-
'\n\t\r' // Control characters
|
|
63
|
-
];
|
|
64
|
-
|
|
65
|
-
testCases.forEach((input) => {
|
|
66
|
-
const result = md51(input);
|
|
67
|
-
expect(result).toHaveLength(4);
|
|
68
|
-
result.forEach((val) => {
|
|
69
|
-
expect(Number.isInteger(val)).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import md5blk from '../src/md5blk';
|
|
2
|
-
|
|
3
|
-
describe('md5blk', () => {
|
|
4
|
-
test('should convert string to 16-word block', () => {
|
|
5
|
-
// Test with a simple string
|
|
6
|
-
const input = 'a'.repeat(64); // 64 characters = 1 full block
|
|
7
|
-
const result = md5blk(input);
|
|
8
|
-
|
|
9
|
-
expect(result).toHaveLength(16);
|
|
10
|
-
|
|
11
|
-
// The character 'a' has code 97
|
|
12
|
-
// In little-endian, first word should be: 97 + (97 << 8) + (97 << 16) + (97 << 24)
|
|
13
|
-
const expectedFirstWord = 97 + (97 << 8) + (97 << 16) + (97 << 24);
|
|
14
|
-
expect(result[0]).toBe(expectedFirstWord);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('should handle empty string', () => {
|
|
18
|
-
const result = md5blk('');
|
|
19
|
-
expect(result).toHaveLength(16);
|
|
20
|
-
|
|
21
|
-
// All words should be 0
|
|
22
|
-
for (let i = 0; i < 16; i++) {
|
|
23
|
-
expect(result[i]).toBe(0);
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('should handle partial block', () => {
|
|
28
|
-
// Test with less than 64 characters
|
|
29
|
-
// Note: md5blk always processes 64 characters, padding with null bytes
|
|
30
|
-
const input = 'hello world';
|
|
31
|
-
const result = md5blk(input);
|
|
32
|
-
|
|
33
|
-
expect(result).toHaveLength(16);
|
|
34
|
-
|
|
35
|
-
// First word should contain 'h' (104), 'e' (101), 'l' (108), 'l' (108)
|
|
36
|
-
const expectedFirstWord = 104 + (101 << 8) + (108 << 16) + (108 << 24);
|
|
37
|
-
expect(result[0]).toBe(expectedFirstWord);
|
|
38
|
-
|
|
39
|
-
// Second word should contain 'o' (111), ' ' (32), 'w' (119), 'o' (111)
|
|
40
|
-
const expectedSecondWord = 111 + (32 << 8) + (119 << 16) + (111 << 24);
|
|
41
|
-
expect(result[1]).toBe(expectedSecondWord);
|
|
42
|
-
|
|
43
|
-
// Third word should contain 'r' (114), 'l' (108), 'd' (100), null (0)
|
|
44
|
-
const expectedThirdWord = 114 + (108 << 8) + (100 << 16) + (0 << 24);
|
|
45
|
-
expect(result[2]).toBe(expectedThirdWord);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test('should convert characters correctly', () => {
|
|
49
|
-
const input = 'abcd';
|
|
50
|
-
const result = md5blk(input);
|
|
51
|
-
|
|
52
|
-
// 'a'(97), 'b'(98), 'c'(99), 'd'(100)
|
|
53
|
-
const expectedWord = 97 + (98 << 8) + (99 << 16) + (100 << 24);
|
|
54
|
-
expect(result[0]).toBe(expectedWord);
|
|
55
|
-
|
|
56
|
-
// Remaining words should be 0
|
|
57
|
-
for (let i = 1; i < 16; i++) {
|
|
58
|
-
expect(result[i]).toBe(0);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import md5cycle from '../src/md5cycle';
|
|
2
|
-
|
|
3
|
-
describe('md5cycle', () => {
|
|
4
|
-
test('should update state correctly', () => {
|
|
5
|
-
const state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
|
6
|
-
const block = new Array(16).fill(0);
|
|
7
|
-
|
|
8
|
-
// Make a copy for comparison
|
|
9
|
-
const originalState = [...state];
|
|
10
|
-
|
|
11
|
-
md5cycle(state, block);
|
|
12
|
-
|
|
13
|
-
// After processing, state should be different
|
|
14
|
-
expect(state).not.toEqual(originalState);
|
|
15
|
-
|
|
16
|
-
// All elements should be 32-bit integers
|
|
17
|
-
state.forEach((val) => {
|
|
18
|
-
expect(Number.isInteger(val)).toBe(true);
|
|
19
|
-
expect(val).toBeGreaterThanOrEqual(-0x80000000);
|
|
20
|
-
expect(val).toBeLessThanOrEqual(0xffffffff);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('should handle custom add32 functions', () => {
|
|
25
|
-
const state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
|
26
|
-
const block = new Array(16).fill(0x12345678);
|
|
27
|
-
|
|
28
|
-
const customAdd32 = jest.fn((x: number, y: number) => (x + y) & 0xffffffff);
|
|
29
|
-
|
|
30
|
-
md5cycle(state, block, customAdd32);
|
|
31
|
-
|
|
32
|
-
// Custom add32 function should have been called
|
|
33
|
-
expect(customAdd32).toHaveBeenCalled();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('should use default add32 when fn is undefined', () => {
|
|
37
|
-
const state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
|
38
|
-
const block = new Array(16).fill(0);
|
|
39
|
-
|
|
40
|
-
// Since we can't easily mock the default import in md5cycle,
|
|
41
|
-
// we'll test the behavior by ensuring state changes
|
|
42
|
-
const originalState = [...state];
|
|
43
|
-
|
|
44
|
-
md5cycle(state, block);
|
|
45
|
-
|
|
46
|
-
expect(state).not.toEqual(originalState);
|
|
47
|
-
});
|
|
48
|
-
});
|