fix: support v2 and hybrid qBittorrent identities
This commit is contained in:
+5
-4
@@ -48,7 +48,6 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
sparse_supported=True,
|
||||
free_space_reserve_bytes=100,
|
||||
)
|
||||
before = {path.name for path in root.iterdir()}
|
||||
with patch(
|
||||
"archive_clients.jobs.shutil.disk_usage",
|
||||
return_value=Mock(free=109),
|
||||
@@ -57,9 +56,9 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
"109 bytes available, 110 bytes required including reserve",
|
||||
):
|
||||
executor._require_space(root, 10)
|
||||
self.assertEqual(
|
||||
self.assertLessEqual(
|
||||
{path.name for path in root.iterdir()},
|
||||
before,
|
||||
{"client.db", "client.db-wal", "client.db-shm"},
|
||||
)
|
||||
|
||||
def test_archive_and_unarchive_five_step_execution(self):
|
||||
@@ -135,7 +134,9 @@ class ClientJobHappyPathTests(unittest.TestCase):
|
||||
source_qb = Mock()
|
||||
source_qb.get_resource.return_value = resource
|
||||
target_qb = Mock()
|
||||
target_qb.get_resource.side_effect = [None, None, resource]
|
||||
target_qb.get_resource.side_effect = [
|
||||
None, None, resource, resource,
|
||||
]
|
||||
syncthing = CompleteSyncthing()
|
||||
source = ClientJobExecutor(
|
||||
client_id=source_id,
|
||||
|
||||
+108
-1
@@ -76,7 +76,114 @@ class QBittorrentReaderTests(unittest.TestCase):
|
||||
resource = QBittorrentReader(config).get_resource(torrent_hash)
|
||||
self.assertIsNotNone(resource)
|
||||
self.assertEqual(resource.summary.resource_id.info_hash_v1_hex, torrent_hash)
|
||||
self.assertIn("hashes=", opener.calls[1])
|
||||
lookup_url = getattr(
|
||||
opener.calls[1], "full_url", opener.calls[1]
|
||||
)
|
||||
self.assertTrue(lookup_url.endswith("/api/v2/torrents/info"))
|
||||
|
||||
def test_full_v2_lookup_accepts_qbittorrent_truncated_hash(self):
|
||||
info = {
|
||||
b"file tree": {
|
||||
b"a.txt": {
|
||||
b"": {
|
||||
b"length": 3,
|
||||
b"pieces root": hashlib.sha256(b"abc").digest(),
|
||||
}
|
||||
}
|
||||
},
|
||||
b"meta version": 2,
|
||||
b"name": b"a.txt",
|
||||
b"piece length": 16384,
|
||||
}
|
||||
torrent_bytes = encode({b"info": info})
|
||||
full_hash = hashlib.sha256(encode(info)).hexdigest()
|
||||
qb_hash = full_hash[:40]
|
||||
responses = [
|
||||
b"Ok.",
|
||||
json.dumps([{
|
||||
"hash": qb_hash, "name": "a.txt", "state": "uploading",
|
||||
}]).encode(),
|
||||
b'[{"index":0,"name":"a.txt","size":3,"progress":1,"priority":1}]',
|
||||
torrent_bytes,
|
||||
]
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
password = root / "password"
|
||||
password.write_text("secret", encoding="utf-8")
|
||||
os.chmod(password, 0o600)
|
||||
config = ServiceConfig(
|
||||
"http://qb", PurePosixPath("/downloads"), root,
|
||||
username="admin", password_file=password,
|
||||
)
|
||||
opener = _Opener(responses)
|
||||
with patch(
|
||||
"archive_clients.qbittorrent.request.build_opener",
|
||||
return_value=opener,
|
||||
):
|
||||
resource = QBittorrentReader(config).get_resource(full_hash)
|
||||
self.assertIsNotNone(resource)
|
||||
self.assertEqual(resource.summary.qb_torrent_id, qb_hash)
|
||||
self.assertEqual(
|
||||
resource.summary.resource_id.info_hash_v2_hex, full_hash
|
||||
)
|
||||
self.assertNotIn("hashes=", opener.calls[1])
|
||||
|
||||
def test_hybrid_lookup_accepts_v1_alias_of_v2_primary_hash(self):
|
||||
info = {
|
||||
b"file tree": {
|
||||
b"a.txt": {
|
||||
b"": {
|
||||
b"length": 3,
|
||||
b"pieces root": hashlib.sha256(b"abc").digest(),
|
||||
}
|
||||
}
|
||||
},
|
||||
b"length": 3,
|
||||
b"meta version": 2,
|
||||
b"name": b"a.txt",
|
||||
b"piece length": 16384,
|
||||
b"pieces": hashlib.sha1(b"abc").digest(),
|
||||
}
|
||||
torrent_bytes = encode({b"info": info})
|
||||
encoded_info = encode(info)
|
||||
v1_hash = hashlib.sha1(encoded_info).hexdigest()
|
||||
v2_hash = hashlib.sha256(encoded_info).hexdigest()
|
||||
qb_hash = v2_hash[:40]
|
||||
responses = [
|
||||
b"Ok.",
|
||||
json.dumps([{
|
||||
"hash": qb_hash,
|
||||
"infohash_v1": v1_hash,
|
||||
"infohash_v2": v2_hash,
|
||||
"name": "a.txt",
|
||||
"state": "uploading",
|
||||
}]).encode(),
|
||||
b'[{"index":0,"name":"a.txt","size":3,"progress":1,"priority":1}]',
|
||||
torrent_bytes,
|
||||
]
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
password = root / "password"
|
||||
password.write_text("secret", encoding="utf-8")
|
||||
os.chmod(password, 0o600)
|
||||
config = ServiceConfig(
|
||||
"http://qb", PurePosixPath("/downloads"), root,
|
||||
username="admin", password_file=password,
|
||||
)
|
||||
opener = _Opener(responses)
|
||||
with patch(
|
||||
"archive_clients.qbittorrent.request.build_opener",
|
||||
return_value=opener,
|
||||
):
|
||||
resource = QBittorrentReader(config).get_resource(v1_hash)
|
||||
self.assertIsNotNone(resource)
|
||||
self.assertEqual(resource.summary.qb_torrent_id, qb_hash)
|
||||
self.assertEqual(
|
||||
resource.summary.resource_id.info_hash_v1_hex, v1_hash
|
||||
)
|
||||
self.assertEqual(
|
||||
resource.summary.resource_id.info_hash_v2_hex, v2_hash
|
||||
)
|
||||
|
||||
def test_current_empty_204_login_is_accepted(self):
|
||||
responses = [(b"", 204), b"[]"]
|
||||
|
||||
@@ -15,9 +15,11 @@ class ResourceTests(unittest.TestCase):
|
||||
def test_pure_v2_identity_and_single_file_tree_normalization(self):
|
||||
info = {
|
||||
b"file tree": {
|
||||
b"": {
|
||||
b"length": 3,
|
||||
b"pieces root": b"x" * 32,
|
||||
b"v2.bin": {
|
||||
b"": {
|
||||
b"length": 3,
|
||||
b"pieces root": b"x" * 32,
|
||||
},
|
||||
},
|
||||
},
|
||||
b"meta version": 2,
|
||||
@@ -57,8 +59,10 @@ class ResourceTests(unittest.TestCase):
|
||||
def test_hybrid_identity_and_selection_normalization(self):
|
||||
info = {
|
||||
b"file tree": {
|
||||
b"a.txt": {b"": {b"length": 3}},
|
||||
b"b.bin": {b"": {b"attr": b"p", b"length": 5}},
|
||||
b"resource": {
|
||||
b"a.txt": {b"": {b"length": 3}},
|
||||
b"b.bin": {b"": {b"attr": b"p", b"length": 5}},
|
||||
},
|
||||
},
|
||||
b"files": [
|
||||
{b"length": 3, b"path": [b"a.txt"]},
|
||||
|
||||
Reference in New Issue
Block a user