Files
meta-hash/src/tests/magnet-link.test.ts
T
2025-11-25 20:24:54 +01:00

176 lines
7.2 KiB
TypeScript

import {describe, expect, it} from 'vitest';
import {BtihV2Hasher, MagnetLinkGenerator} from '../lib';
import {readFile, stat} from 'fs/promises';
/**
* End-to-End Magnet Link Generation Tests
*
* Tests the complete workflow:
* 1. Compute BitTorrent v2 info hash from file
* 2. Generate magnet link
* 3. Parse and validate magnet link
*/
describe('Magnet Link Generation (End-to-End)', () => {
it('should generate valid magnet link from file', async () => {
const filePath = './src/tests/test.txt';
const fileName = 'test.txt';
// Step 1: Read file and compute info hash
const fileData = await readFile(filePath);
const fileStats = await stat(filePath);
const fileSize = fileStats.size;
const hasher = new BtihV2Hasher();
hasher.setFileName(fileName);
hasher.update(fileData);
const infoHash = hasher.digest();
// Verify info hash computed
expect(infoHash).toBeInstanceOf(Buffer);
expect(infoHash.length).toBe(32);
console.log('File:', fileName);
console.log('Size:', fileSize, 'bytes');
console.log('Info hash (hex):', infoHash.toString('hex'));
// Note: We can't test magnet link generation end-to-end without
// the CID encoding working properly through HashComputerWorker.
// This would require the worker integration to be fixed.
});
it('should generate magnet link from mock CID', () => {
// Mock CID format (this is a valid CIDv1 with our custom codec 0x10B7)
// In real usage, this would come from HashComputerWorker
const fileName = 'test-video.mp4';
const fileSize = 1024 * 1024 * 100; // 100MB
// Create a mock info hash (32 bytes of test data)
const mockInfoHash = Buffer.from(
'b04c198dd3148719db3e32fecc54ec41ec355d7f866a1c3f71bf980c3cde01bf',
'hex'
);
// For this test, we'll construct the magnet link manually
// since we can't get a real CID without the worker
const infoHashHex = mockInfoHash.toString('hex');
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHashHex}&dn=${encodeURIComponent(fileName)}&xl=${fileSize}`;
console.log('\nGenerated magnet link:');
console.log(magnetLink);
// Verify magnet link format
expect(magnetLink.startsWith('magnet:?')).toBe(true);
expect(magnetLink.includes('xt=urn:btmh:1220')).toBe(true);
expect(magnetLink.includes(infoHashHex)).toBe(true);
expect(magnetLink.includes(`dn=${encodeURIComponent(fileName)}`)).toBe(true);
expect(magnetLink.includes(`xl=${fileSize}`)).toBe(true);
});
it('should validate magnet link format', () => {
const validLink = 'magnet:?xt=urn:btmh:1220' + 'a'.repeat(64) + '&dn=file.mp4&xl=1024';
const invalidLink1 = 'http://example.com';
const invalidLink2 = 'magnet:?xt=urn:btih:' + 'a'.repeat(40); // v1 format (SHA-1)
const invalidLink3 = 'magnet:?xt=urn:btmh:1220' + 'a'.repeat(32); // Too short
expect(MagnetLinkGenerator.isValid(validLink)).toBe(true);
expect(MagnetLinkGenerator.isValid(invalidLink1)).toBe(false);
expect(MagnetLinkGenerator.isValid(invalidLink2)).toBe(false);
expect(MagnetLinkGenerator.isValid(invalidLink3)).toBe(false);
});
it('should extract info hash from magnet link', () => {
const infoHash = 'b04c198dd3148719db3e32fecc54ec41ec355d7f866a1c3f71bf980c3cde01bf';
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash}&dn=file.mp4&xl=1024`;
const extracted = MagnetLinkGenerator.extractInfoHash(magnetLink);
expect(extracted).toBe(infoHash);
});
it('should extract filename from magnet link', () => {
const fileName = 'Big Buck Bunny.mp4';
const infoHash = 'a'.repeat(64);
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash}&dn=${encodeURIComponent(fileName)}&xl=1024`;
const extracted = MagnetLinkGenerator.extractFileName(magnetLink);
expect(extracted).toBe(fileName);
});
it('should extract file size from magnet link', () => {
const fileSize = 1024000;
const infoHash = 'a'.repeat(64);
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash}&dn=file.mp4&xl=${fileSize}`;
const extracted = MagnetLinkGenerator.extractFileSize(magnetLink);
expect(extracted).toBe(fileSize);
});
it('should parse complete magnet link', () => {
const infoHash = 'b04c198dd3148719db3e32fecc54ec41ec355d7f866a1c3f71bf980c3cde01bf';
const fileName = 'test-video.mp4';
const fileSize = 1024000;
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash}&dn=${encodeURIComponent(fileName)}&xl=${fileSize}`;
const parsed = MagnetLinkGenerator.parse(magnetLink);
expect(parsed).not.toBeNull();
expect(parsed!.infoHash).toBe(infoHash);
expect(parsed!.fileName).toBe(fileName);
expect(parsed!.fileSize).toBe(fileSize);
});
it('should handle special characters in filename', () => {
const fileName = 'My Video [1080p] (2024).mp4';
const infoHash = 'a'.repeat(64);
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash}&dn=${encodeURIComponent(fileName)}&xl=1024`;
const extracted = MagnetLinkGenerator.extractFileName(magnetLink);
expect(extracted).toBe(fileName);
// Verify round-trip
const parsed = MagnetLinkGenerator.parse(magnetLink);
expect(parsed?.fileName).toBe(fileName);
});
it('should demonstrate complete workflow (conceptual)', async () => {
// This test demonstrates the intended workflow once worker integration is fixed
const filePath = './src/tests/test.txt';
const fileStats = await stat(filePath);
console.log('\n=== Complete Workflow Demo ===');
console.log('1. File:', filePath);
console.log('2. Size:', fileStats.size, 'bytes');
// Step 1: Compute info hash
const fileData = await readFile(filePath);
const hasher = new BtihV2Hasher();
hasher.setFileName('test.txt');
hasher.update(fileData);
const infoHash = hasher.digest();
console.log('3. Info hash:', infoHash.toString('hex'));
// Step 2: In production, this would be the CID from metadata
// const cid = metadata.cid_btih_v2;
// const magnetLink = MagnetLinkGenerator.generate(metadata, 'test.txt', fileStats.size);
// Step 3: For demo, construct magnet link manually
const magnetLink = `magnet:?xt=urn:btmh:1220${infoHash.toString('hex')}&dn=test.txt&xl=${fileStats.size}`;
console.log('4. Magnet link:', magnetLink);
// Step 4: Verify it's valid
const isValid = MagnetLinkGenerator.isValid(magnetLink);
console.log('5. Valid:', isValid);
expect(isValid).toBe(true);
// Step 5: Parse it back
const parsed = MagnetLinkGenerator.parse(magnetLink);
console.log('6. Parsed:', parsed);
expect(parsed).not.toBeNull();
expect(parsed!.infoHash).toBe(infoHash.toString('hex'));
expect(parsed!.fileName).toBe('test.txt');
expect(parsed!.fileSize).toBe(fileStats.size);
});
});