package torrentfile import ( "crypto/sha1" "os" "path/filepath" "testing" ) func TestOpenParsesTorrentMetadata(t *testing.T) { t.Parallel() info := "d6:lengthi5e4:name8:test.txt12:piece lengthi16384e6:pieces20:aaaaaaaaaaaaaaaaaaaa7:privatei1ee" torrent := "d8:announce14:http://tracker4:info" + info + "e" path := writeTempTorrent(t, torrent) tf, err := Open(path) if err != nil { t.Fatalf("Open returned error: %v", err) } expectedHash := sha1.Sum([]byte(info)) if tf.InfoHash != expectedHash { t.Fatalf("unexpected info hash: got %x want %x", tf.InfoHash, expectedHash) } if tf.Announce != "http://tracker" { t.Fatalf("unexpected announce: got %q", tf.Announce) } if tf.Name != "test.txt" { t.Fatalf("unexpected name: got %q", tf.Name) } if tf.PieceLength != 16384 { t.Fatalf("unexpected piece length: got %d", tf.PieceLength) } if tf.Length != 5 { t.Fatalf("unexpected length: got %d", tf.Length) } if len(tf.PieceHashes) != 1 { t.Fatalf("unexpected piece count: got %d", len(tf.PieceHashes)) } if len(tf.Files) != 1 { t.Fatalf("unexpected file count: got %d", len(tf.Files)) } if got, want := tf.Files[0].Path, "test.txt"; got != want { t.Fatalf("unexpected first file path: got %q want %q", got, want) } if got, want := tf.Files[0].Length, 5; got != want { t.Fatalf("unexpected first file length: got %d want %d", got, want) } var expectedPiece [20]byte copy(expectedPiece[:], []byte("aaaaaaaaaaaaaaaaaaaa")) if tf.PieceHashes[0] != expectedPiece { t.Fatalf("unexpected piece hash: got %x want %x", tf.PieceHashes[0], expectedPiece) } } func TestOpenRejectsInvalidPieceHashes(t *testing.T) { t.Parallel() info := "d6:lengthi5e4:name8:test.txt12:piece lengthi16384e6:pieces19:aaaaaaaaaaaaaaaaaaae" torrent := "d8:announce14:http://tracker4:info" + info + "e" path := writeTempTorrent(t, torrent) _, err := Open(path) if err == nil { t.Fatal("expected Open to fail for invalid piece hash length") } } func TestOpenParsesMultiFileTorrent(t *testing.T) { t.Parallel() pieces := "aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb" info := "d5:filesld6:lengthi3e4:pathl5:a.txteed6:lengthi2e4:pathl5:b.txteee4:name4:root12:piece lengthi4e6:pieces40:" + pieces + "e" torrent := "d8:announce14:http://tracker4:info" + info + "e" path := writeTempTorrent(t, torrent) tf, err := Open(path) if err != nil { t.Fatalf("Open returned error: %v", err) } if tf.Length != 5 { t.Fatalf("unexpected total length: got %d", tf.Length) } if len(tf.Files) != 2 { t.Fatalf("unexpected file count: got %d", len(tf.Files)) } if got, want := tf.Files[0].Path, "root/a.txt"; got != want { t.Fatalf("unexpected file[0] path: got %q want %q", got, want) } if got, want := tf.Files[1].Path, "root/b.txt"; got != want { t.Fatalf("unexpected file[1] path: got %q want %q", got, want) } if len(tf.PieceHashes) != 2 { t.Fatalf("unexpected piece hash count: got %d", len(tf.PieceHashes)) } } func TestOpenRejectsPieceCountMismatch(t *testing.T) { t.Parallel() info := "d6:lengthi10e4:name8:test.txt12:piece lengthi4e6:pieces40:aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbe" torrent := "d8:announce14:http://tracker4:info" + info + "e" path := writeTempTorrent(t, torrent) _, err := Open(path) if err == nil { t.Fatal("expected Open to fail for piece count mismatch") } } func writeTempTorrent(t *testing.T, data string) string { t.Helper() dir := t.TempDir() path := filepath.Join(dir, "test.torrent") if err := os.WriteFile(path, []byte(data), 0o600); err != nil { t.Fatalf("failed to write temp torrent file: %v", err) } return path }