Adds export method, fixes login for v2 accounts, rm get_contacts() method
This commit is contained in:
parent
d9a77e5fd9
commit
93f3e6d376
6 changed files with 301 additions and 153 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,7 +5,7 @@
|
||||||
#################
|
#################
|
||||||
## Eclipse
|
## Eclipse
|
||||||
#################
|
#################
|
||||||
|
.env.fish
|
||||||
*.pydevproject
|
*.pydevproject
|
||||||
.project
|
.project
|
||||||
.metadata
|
.metadata
|
||||||
|
|
|
@ -6,7 +6,9 @@ Release History
|
||||||
0.9.21 (unreleased)
|
0.9.21 (unreleased)
|
||||||
+++++++++++++++++++
|
+++++++++++++++++++
|
||||||
|
|
||||||
- Nothing changed yet.
|
- Removes broken method ``get_contacts()``.
|
||||||
|
- Adds support for login with a v2 Mega user account.
|
||||||
|
- Adds ``export()`` method to share a file or folder, returning public share URL with key.
|
||||||
|
|
||||||
|
|
||||||
0.9.20 (2019-10-17)
|
0.9.20 (2019-10-17)
|
||||||
|
|
|
@ -13,3 +13,4 @@ zest.releaser
|
||||||
setuptools
|
setuptools
|
||||||
twine
|
twine
|
||||||
wheel
|
wheel
|
||||||
|
rope
|
||||||
|
|
19
src/mega/example_req_to_export_folder.json
Normal file
19
src/mega/example_req_to_export_folder.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[{
|
||||||
|
"a": "log",
|
||||||
|
"e": 99635
|
||||||
|
}, {
|
||||||
|
"a": "s2",
|
||||||
|
"n": "oUchHSzJ",
|
||||||
|
"s": [{
|
||||||
|
"u": "EXP",
|
||||||
|
"r": 0
|
||||||
|
}],
|
||||||
|
"i": "q6CpLxTDNV",
|
||||||
|
"ok": "uGuwXS80VfifU1hcLlKcrQ",
|
||||||
|
"ha": "n6dI3_qVste9XPma5fMIKQ",
|
||||||
|
"cr": [
|
||||||
|
["oUchHSzJ"],
|
||||||
|
["oUchHSzJ"],
|
||||||
|
[0, 0, "ff5m7sr6LZlYyRtiEE9EZA"]
|
||||||
|
]
|
||||||
|
}]
|
209
src/mega/mega.py
209
src/mega/mega.py
|
@ -1,5 +1,7 @@
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
|
import secrets
|
||||||
|
import hashlib
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
from Crypto.Util import Counter
|
from Crypto.Util import Counter
|
||||||
|
@ -41,10 +43,27 @@ class Mega(object):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _login_user(self, email, password):
|
def _login_user(self, email, password):
|
||||||
password_aes = prepare_key(str_to_a32(password))
|
email = email.lower()
|
||||||
uh = stringhash(email, password_aes)
|
get_user_salt_resp = self._api_request({'a': 'us0', 'user': email})
|
||||||
resp = self._api_request({'a': 'us', 'user': email, 'uh': uh})
|
user_salt = None
|
||||||
# if numeric error code response
|
try:
|
||||||
|
user_salt = base64_to_a32(get_user_salt_resp['s'])
|
||||||
|
except KeyError:
|
||||||
|
# v1 user account
|
||||||
|
password_aes = prepare_key(str_to_a32(password))
|
||||||
|
user_hash = stringhash(email, password_aes)
|
||||||
|
else:
|
||||||
|
# v2 user account
|
||||||
|
pbkdf2_key = hashlib.pbkdf2_hmac(
|
||||||
|
hash_name='sha512',
|
||||||
|
password=password.encode(),
|
||||||
|
salt=a32_to_str(user_salt),
|
||||||
|
iterations=100000,
|
||||||
|
dklen=32
|
||||||
|
)
|
||||||
|
password_aes = str_to_a32(pbkdf2_key[:16])
|
||||||
|
user_hash = base64_url_encode(pbkdf2_key[-16:])
|
||||||
|
resp = self._api_request({'a': 'us', 'user': email, 'uh': user_hash})
|
||||||
if isinstance(resp, int):
|
if isinstance(resp, int):
|
||||||
raise RequestError(resp)
|
raise RequestError(resp)
|
||||||
self._login_process(resp, password_aes)
|
self._login_process(resp, password_aes)
|
||||||
|
@ -70,7 +89,6 @@ class Mega(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
resp = self._api_request({'a': 'us', 'user': user})
|
resp = self._api_request({'a': 'us', 'user': user})
|
||||||
# if numeric error code response
|
|
||||||
if isinstance(resp, int):
|
if isinstance(resp, int):
|
||||||
raise RequestError(resp)
|
raise RequestError(resp)
|
||||||
self._login_process(resp, password_key)
|
self._login_process(resp, password_key)
|
||||||
|
@ -113,7 +131,6 @@ class Mega(object):
|
||||||
self.rsa_private_key[1]
|
self.rsa_private_key[1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
sid = '%x' % rsa_decrypter.key._decrypt(encrypted_sid)
|
sid = '%x' % rsa_decrypter.key._decrypt(encrypted_sid)
|
||||||
sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid)
|
sid = binascii.unhexlify('0' + sid if len(sid) % 2 else sid)
|
||||||
self.sid = base64_url_encode(sid[:43])
|
self.sid = base64_url_encode(sid[:43])
|
||||||
|
@ -135,20 +152,8 @@ class Mega(object):
|
||||||
params=params,
|
params=params,
|
||||||
data=json.dumps(data),
|
data=json.dumps(data),
|
||||||
timeout=self.timeout,
|
timeout=self.timeout,
|
||||||
headers={
|
|
||||||
'Origin':
|
|
||||||
'https://mega.nz',
|
|
||||||
'Referer':
|
|
||||||
'https://mega.nz/login',
|
|
||||||
'User-Agent': (
|
|
||||||
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:69.0) '
|
|
||||||
'Gecko/20100101 Firefox/69.0'
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
json_resp = json.loads(req.text)
|
json_resp = json.loads(req.text)
|
||||||
|
|
||||||
# if numeric error code response
|
|
||||||
if isinstance(json_resp, int):
|
if isinstance(json_resp, int):
|
||||||
raise RequestError(json_resp)
|
raise RequestError(json_resp)
|
||||||
return json_resp[0]
|
return json_resp[0]
|
||||||
|
@ -163,9 +168,6 @@ class Mega(object):
|
||||||
raise RequestError('Url key missing')
|
raise RequestError('Url key missing')
|
||||||
|
|
||||||
def _process_file(self, file, shared_keys):
|
def _process_file(self, file, shared_keys):
|
||||||
"""
|
|
||||||
Process a file
|
|
||||||
"""
|
|
||||||
if file['t'] == 0 or file['t'] == 1:
|
if file['t'] == 0 or file['t'] == 1:
|
||||||
keys = dict(
|
keys = dict(
|
||||||
keypart.split(':', 1)
|
keypart.split(':', 1)
|
||||||
|
@ -194,6 +196,13 @@ class Mega(object):
|
||||||
key = keys[hkey]
|
key = keys[hkey]
|
||||||
key = decrypt_key(base64_to_a32(key), shared_key)
|
key = decrypt_key(base64_to_a32(key), shared_key)
|
||||||
break
|
break
|
||||||
|
if file['h'] and file['h'] in shared_keys.get('EXP', ()):
|
||||||
|
shared_key = shared_keys['EXP'][file['h']]
|
||||||
|
encrypted_key = str_to_a32(
|
||||||
|
base64_url_decode(file['k'].split(':')[-1])
|
||||||
|
)
|
||||||
|
key = decrypt_key(encrypted_key, shared_key)
|
||||||
|
file['shared_folder_key'] = shared_key
|
||||||
if key is not None:
|
if key is not None:
|
||||||
# file
|
# file
|
||||||
if file['t'] == 0:
|
if file['t'] == 0:
|
||||||
|
@ -244,9 +253,7 @@ class Mega(object):
|
||||||
shared_keys[s_item['u']] = {}
|
shared_keys[s_item['u']] = {}
|
||||||
if s_item['h'] in ok_dict:
|
if s_item['h'] in ok_dict:
|
||||||
shared_keys[s_item['u']][s_item['h']] = ok_dict[s_item['h']]
|
shared_keys[s_item['u']][s_item['h']] = ok_dict[s_item['h']]
|
||||||
|
self.shared_keys = shared_keys
|
||||||
##########################################################################
|
|
||||||
# GET
|
|
||||||
|
|
||||||
def find_path_descriptor(self, path):
|
def find_path_descriptor(self, path):
|
||||||
"""
|
"""
|
||||||
|
@ -264,8 +271,11 @@ class Mega(object):
|
||||||
for foldername in paths:
|
for foldername in paths:
|
||||||
if foldername != '':
|
if foldername != '':
|
||||||
for file in files.items():
|
for file in files.items():
|
||||||
if file[1]['a'] and file[1]['t'] and \
|
if (
|
||||||
file[1]['a']['n'] == foldername:
|
file[1]['a'] and
|
||||||
|
file[1]['t'] and
|
||||||
|
file[1]['a']['n'] == foldername
|
||||||
|
):
|
||||||
if parent_desc == file[1]['p']:
|
if parent_desc == file[1]['p']:
|
||||||
parent_desc = file[0]
|
parent_desc = file[0]
|
||||||
found = True
|
found = True
|
||||||
|
@ -275,22 +285,40 @@ class Mega(object):
|
||||||
return None
|
return None
|
||||||
return parent_desc
|
return parent_desc
|
||||||
|
|
||||||
def find(self, filename):
|
def find(self, filename=None, handle=None):
|
||||||
"""
|
"""
|
||||||
Return file object from given filename
|
Return file object from given filename
|
||||||
"""
|
"""
|
||||||
|
from pathlib import Path
|
||||||
|
path = Path(filename)
|
||||||
|
filename = path.name
|
||||||
files = self.get_files()
|
files = self.get_files()
|
||||||
|
parent_dir_name = path.parent.name
|
||||||
for file in list(files.items()):
|
for file in list(files.items()):
|
||||||
if not isinstance(file[1]['a'], dict):
|
parent_node_id = None
|
||||||
continue
|
if parent_dir_name:
|
||||||
if file[1]['a'] and file[1]['a']['n'] == filename:
|
parent_node_id = self.find_path_descriptor(parent_dir_name)
|
||||||
|
if (
|
||||||
|
filename and parent_node_id and
|
||||||
|
file[1]['a'] and file[1]['a']['n'] == filename
|
||||||
|
and parent_node_id == file[1]['p']
|
||||||
|
):
|
||||||
|
return file
|
||||||
|
# if not isinstance(file[1]['a'], dict):
|
||||||
|
# continue
|
||||||
|
if (
|
||||||
|
filename and
|
||||||
|
file[1]['a'] and file[1]['a']['n'] == filename
|
||||||
|
):
|
||||||
|
return file
|
||||||
|
if handle and file[1]['h'] == handle:
|
||||||
return file
|
return file
|
||||||
|
|
||||||
def get_files(self):
|
def get_files(self):
|
||||||
"""
|
"""
|
||||||
Get all files in account
|
Get all files in account
|
||||||
"""
|
"""
|
||||||
files = self._api_request({'a': 'f', 'c': 1})
|
files = self._api_request({'a': 'f', 'c': 1, 'r': 1})
|
||||||
files_dict = {}
|
files_dict = {}
|
||||||
shared_keys = {}
|
shared_keys = {}
|
||||||
self._init_shared_keys(files, shared_keys)
|
self._init_shared_keys(files, shared_keys)
|
||||||
|
@ -341,6 +369,31 @@ class Mega(object):
|
||||||
else:
|
else:
|
||||||
raise ValidationError('File id and key must be present')
|
raise ValidationError('File id and key must be present')
|
||||||
|
|
||||||
|
def _node_data(self, node):
|
||||||
|
try:
|
||||||
|
return node[1]
|
||||||
|
except (IndexError, KeyError):
|
||||||
|
return node
|
||||||
|
|
||||||
|
def get_folder_link(self, file):
|
||||||
|
try:
|
||||||
|
file = file[1]
|
||||||
|
except (IndexError, KeyError):
|
||||||
|
pass
|
||||||
|
if 'h' in file and 'k' in file:
|
||||||
|
public_handle = self._api_request({'a': 'l', 'n': file['h']})
|
||||||
|
if public_handle == -11:
|
||||||
|
raise RequestError(
|
||||||
|
"Can't get a public link from that file "
|
||||||
|
"(is this a shared file?)"
|
||||||
|
)
|
||||||
|
decrypted_key = a32_to_base64(file['shared_folder_key'])
|
||||||
|
return '{0}://{1}/#F!{2}!{3}'.format(
|
||||||
|
self.schema, self.domain, public_handle, decrypted_key
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValidationError('File id and key must be present')
|
||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
user_data = self._api_request({'a': 'ug'})
|
user_data = self._api_request({'a': 'ug'})
|
||||||
return user_data
|
return user_data
|
||||||
|
@ -442,8 +495,6 @@ class Mega(object):
|
||||||
if 'balance' in user_data:
|
if 'balance' in user_data:
|
||||||
return user_data['balance']
|
return user_data['balance']
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# DELETE
|
|
||||||
def delete(self, public_handle):
|
def delete(self, public_handle):
|
||||||
"""
|
"""
|
||||||
Delete a file by its public handle
|
Delete a file by its public handle
|
||||||
|
@ -491,8 +542,6 @@ class Mega(object):
|
||||||
post_list.append({"a": "d", "n": file, "i": self.request_id})
|
post_list.append({"a": "d", "n": file, "i": self.request_id})
|
||||||
return self._api_request(post_list)
|
return self._api_request(post_list)
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# DOWNLOAD
|
|
||||||
def download(self, file, dest_path=None, dest_filename=None):
|
def download(self, file, dest_path=None, dest_filename=None):
|
||||||
"""
|
"""
|
||||||
Download a file by it's file object
|
Download a file by it's file object
|
||||||
|
@ -506,6 +555,68 @@ class Mega(object):
|
||||||
is_public=False
|
is_public=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _export_file(self, node):
|
||||||
|
self._api_request([
|
||||||
|
{
|
||||||
|
'a': 'l',
|
||||||
|
'n': node[1]['h'],
|
||||||
|
'i': self.request_id
|
||||||
|
}
|
||||||
|
])
|
||||||
|
return self.get_link(node)
|
||||||
|
|
||||||
|
def export(self, path):
|
||||||
|
self.get_files()
|
||||||
|
folder = self.find(path)
|
||||||
|
if folder[1]['t'] == 0:
|
||||||
|
return self._export_file(folder)
|
||||||
|
if folder:
|
||||||
|
try:
|
||||||
|
# If already exported
|
||||||
|
return self.get_folder_link(folder)
|
||||||
|
except (RequestError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
user_id = folder[1]['u']
|
||||||
|
user_pub_key = self._api_request({'a': 'uk', 'u': user_id})['pubk']
|
||||||
|
node_key = folder[1]['k']
|
||||||
|
|
||||||
|
master_key_cipher = AES.new(a32_to_str(self.master_key), AES.MODE_ECB)
|
||||||
|
ha = base64_url_encode(
|
||||||
|
master_key_cipher.encrypt(folder[1]['h'] + folder[1]['h'])
|
||||||
|
)
|
||||||
|
|
||||||
|
share_key = secrets.token_bytes(16)
|
||||||
|
ok = base64_url_encode(master_key_cipher.encrypt(share_key))
|
||||||
|
|
||||||
|
share_key_cipher = AES.new(share_key, AES.MODE_ECB)
|
||||||
|
encrypted_node_key = base64_url_encode(
|
||||||
|
share_key_cipher.encrypt(a32_to_str(node_key))
|
||||||
|
)
|
||||||
|
|
||||||
|
node_id = folder[1]['h']
|
||||||
|
request_body = [
|
||||||
|
{
|
||||||
|
'a': 's2',
|
||||||
|
'n': node_id,
|
||||||
|
's': [{
|
||||||
|
'u': 'EXP',
|
||||||
|
'r': 0
|
||||||
|
}],
|
||||||
|
'i': self.request_id,
|
||||||
|
'ok': ok,
|
||||||
|
'ha': ha,
|
||||||
|
'cr': [
|
||||||
|
[node_id],
|
||||||
|
[node_id],
|
||||||
|
[0, 0, encrypted_node_key]
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
self._api_request(request_body)
|
||||||
|
nodes = self.get_files()
|
||||||
|
link = self.get_folder_link(nodes[node_id])
|
||||||
|
return link
|
||||||
|
|
||||||
def download_url(self, url, dest_path=None, dest_filename=None):
|
def download_url(self, url, dest_path=None, dest_filename=None):
|
||||||
"""
|
"""
|
||||||
Download a file by it's public url
|
Download a file by it's public url
|
||||||
|
@ -631,8 +742,6 @@ class Mega(object):
|
||||||
|
|
||||||
shutil.move(temp_output_file.name, dest_path + file_name)
|
shutil.move(temp_output_file.name, dest_path + file_name)
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# UPLOAD
|
|
||||||
def upload(self, filename, dest=None, dest_filename=None):
|
def upload(self, filename, dest=None, dest_filename=None):
|
||||||
# determine storage node
|
# determine storage node
|
||||||
if dest is None:
|
if dest is None:
|
||||||
|
@ -767,10 +876,8 @@ class Mega(object):
|
||||||
# update attributes
|
# update attributes
|
||||||
data = self._api_request(
|
data = self._api_request(
|
||||||
{
|
{
|
||||||
'a':
|
'a':'p',
|
||||||
'p',
|
't': dest,
|
||||||
't':
|
|
||||||
dest,
|
|
||||||
'n': [
|
'n': [
|
||||||
{
|
{
|
||||||
'h': 'xxxxxxxx',
|
'h': 'xxxxxxxx',
|
||||||
|
@ -779,8 +886,7 @@ class Mega(object):
|
||||||
'k': encrypted_key
|
'k': encrypted_key
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'i':
|
'i': self.request_id
|
||||||
self.request_id
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return data
|
return data
|
||||||
|
@ -881,21 +987,6 @@ class Mega(object):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_contacts(self):
|
|
||||||
raise NotImplementedError()
|
|
||||||
# TODO implement this
|
|
||||||
# sn param below = maxaction var with function getsc() in mega.co.nz js
|
|
||||||
# seens to be the 'sn' attrib of the previous request response...
|
|
||||||
# requests goto /sc rather than
|
|
||||||
|
|
||||||
# req = requests.post(
|
|
||||||
# '{0}://g.api.{1}/sc'.format(self.schema, self.domain),
|
|
||||||
# params={'sn': 'ZMxcQ_DmHnM', 'ssl': '1'},
|
|
||||||
# data=json.dumps(None),
|
|
||||||
# timeout=self.timeout)
|
|
||||||
# json_resp = json.loads(req.text)
|
|
||||||
# print json_resp
|
|
||||||
|
|
||||||
def get_public_url_info(self, url):
|
def get_public_url_info(self, url):
|
||||||
"""
|
"""
|
||||||
Get size and name from a public url, dict returned
|
Get size and name from a public url, dict returned
|
||||||
|
@ -917,8 +1008,6 @@ class Mega(object):
|
||||||
Get size and name of a public file
|
Get size and name of a public file
|
||||||
"""
|
"""
|
||||||
data = self._api_request({'a': 'g', 'p': file_handle, 'ssm': 1})
|
data = self._api_request({'a': 'g', 'p': file_handle, 'ssm': 1})
|
||||||
|
|
||||||
# if numeric error code response
|
|
||||||
if isinstance(data, int):
|
if isinstance(data, int):
|
||||||
raise RequestError(data)
|
raise RequestError(data)
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
"""
|
|
||||||
These unit tests will upload a test file,a test folder and a test contact,
|
|
||||||
Perform api operations on them,
|
|
||||||
And them remove them from your account.
|
|
||||||
"""
|
|
||||||
import unittest
|
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from mega import Mega
|
from mega import Mega
|
||||||
|
|
||||||
mega = Mega()
|
|
||||||
# anonymous login
|
|
||||||
m = mega.login()
|
|
||||||
# normal login
|
|
||||||
# m = mega.login(email, password)
|
|
||||||
|
|
||||||
FIND_RESP = None
|
|
||||||
TEST_CONTACT = 'test@mega.co.nz'
|
TEST_CONTACT = 'test@mega.co.nz'
|
||||||
TEST_PUBLIC_URL = (
|
TEST_PUBLIC_URL = (
|
||||||
'https://mega.nz/#!hYVmXKqL!r0d0-WRnFwulR_shhuEDwrY1Vo103-am1MyUy8oV6Ps'
|
'https://mega.nz/#!hYVmXKqL!r0d0-WRnFwulR_shhuEDwrY1Vo103-am1MyUy8oV6Ps'
|
||||||
|
@ -24,83 +13,131 @@ TEST_FILE = os.path.basename(__file__)
|
||||||
TEST_FOLDER = 'mega.py_testfolder_{0}'.format(random.random())
|
TEST_FOLDER = 'mega.py_testfolder_{0}'.format(random.random())
|
||||||
|
|
||||||
|
|
||||||
class TestMega(unittest.TestCase):
|
@pytest.fixture
|
||||||
|
def mega():
|
||||||
def test_mega(self):
|
mega_ = Mega()
|
||||||
self.assertIsInstance(mega, Mega)
|
mega_.login(email=os.environ['EMAIL'], password=os.environ['PASS'])
|
||||||
|
node = mega_.create_folder(TEST_FOLDER)
|
||||||
def test_login(self):
|
yield mega_
|
||||||
self.assertIsInstance(mega, Mega)
|
node_id = node['f'][0]['h']
|
||||||
|
mega_.destroy(node_id)
|
||||||
def test_get_user(self):
|
|
||||||
resp = m.get_user()
|
|
||||||
self.assertIsInstance(resp, dict)
|
|
||||||
|
|
||||||
def test_get_quota(self):
|
|
||||||
resp = m.get_quota()
|
|
||||||
self.assertIsInstance(int(resp), int)
|
|
||||||
|
|
||||||
def test_get_storage_space(self):
|
|
||||||
resp = m.get_storage_space(mega=True)
|
|
||||||
self.assertIsInstance(resp, dict)
|
|
||||||
|
|
||||||
def test_get_files(self):
|
|
||||||
files = m.get_files()
|
|
||||||
self.assertIsInstance(files, dict)
|
|
||||||
|
|
||||||
def test_get_link(self):
|
|
||||||
file = m.find(TEST_FILE)
|
|
||||||
if file:
|
|
||||||
link = m.get_link(file)
|
|
||||||
self.assertIsInstance(link, str)
|
|
||||||
|
|
||||||
def test_import_public_url(self):
|
|
||||||
resp = m.import_public_url(TEST_PUBLIC_URL)
|
|
||||||
file_handle = m.get_id_from_obj(resp)
|
|
||||||
resp = m.destroy(file_handle)
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_create_folder(self):
|
|
||||||
resp = m.create_folder(TEST_FOLDER)
|
|
||||||
self.assertIsInstance(resp, dict)
|
|
||||||
|
|
||||||
def test_rename(self):
|
|
||||||
file = m.find(TEST_FOLDER)
|
|
||||||
if file:
|
|
||||||
resp = m.rename(file, TEST_FOLDER)
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_delete_folder(self):
|
|
||||||
folder_node = m.find(TEST_FOLDER)[0]
|
|
||||||
resp = m.delete(folder_node)
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_delete(self):
|
|
||||||
file = m.find(TEST_FILE)
|
|
||||||
if file:
|
|
||||||
resp = m.delete(file[0])
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_destroy(self):
|
|
||||||
file = m.find(TEST_FILE)
|
|
||||||
if file:
|
|
||||||
resp = m.destroy(file[0])
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_empty_trash(self):
|
|
||||||
# resp None if already empty, else int
|
|
||||||
resp = m.empty_trash()
|
|
||||||
if resp is not None:
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_add_contact(self):
|
|
||||||
resp = m.add_contact(TEST_CONTACT)
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
def test_remove_contact(self):
|
|
||||||
resp = m.remove_contact(TEST_CONTACT)
|
|
||||||
self.assertIsInstance(resp, int)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def test_mega(mega):
|
||||||
unittest.main()
|
assert isinstance(mega, Mega)
|
||||||
|
|
||||||
|
|
||||||
|
def test_login(mega):
|
||||||
|
assert isinstance(mega, Mega)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_user(mega):
|
||||||
|
resp = mega.get_user()
|
||||||
|
assert isinstance(resp, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_quota(mega):
|
||||||
|
resp = mega.get_quota()
|
||||||
|
assert isinstance(int(resp), int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_storage_space(mega):
|
||||||
|
resp = mega.get_storage_space(mega=True)
|
||||||
|
assert isinstance(resp, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_files(mega):
|
||||||
|
files = mega.get_files()
|
||||||
|
assert isinstance(files, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_link(mega):
|
||||||
|
file = mega.find(TEST_FILE)
|
||||||
|
if file:
|
||||||
|
link = mega.get_link(file)
|
||||||
|
assert isinstance(link, str)
|
||||||
|
|
||||||
|
|
||||||
|
class TestExport:
|
||||||
|
|
||||||
|
def test_export_folder(self, mega):
|
||||||
|
public_url = None
|
||||||
|
for _ in range(2):
|
||||||
|
result_public_share_url = mega.export(TEST_FOLDER)
|
||||||
|
|
||||||
|
if not public_url:
|
||||||
|
public_url = result_public_share_url
|
||||||
|
|
||||||
|
assert result_public_share_url.startswith('https://mega.co.nz/#F!')
|
||||||
|
assert result_public_share_url == public_url
|
||||||
|
|
||||||
|
def test_export_single_file(self, mega):
|
||||||
|
# Upload a single file into a folder
|
||||||
|
folder = mega.find(TEST_FOLDER)
|
||||||
|
dest_node_id = folder[1]['h']
|
||||||
|
result = mega.upload(
|
||||||
|
__file__, dest=dest_node_id, dest_filename='test.py'
|
||||||
|
)
|
||||||
|
path = f'{TEST_FOLDER}/test.py'
|
||||||
|
assert mega.find(path)
|
||||||
|
|
||||||
|
for _ in range(2):
|
||||||
|
result_public_share_url = mega.export(path)
|
||||||
|
|
||||||
|
assert result_public_share_url.startswith('https://mega.co.nz/#!')
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_public_url(mega):
|
||||||
|
resp = mega.import_public_url(TEST_PUBLIC_URL)
|
||||||
|
file_handle = mega.get_id_from_obj(resp)
|
||||||
|
resp = mega.destroy(file_handle)
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_folder(mega):
|
||||||
|
resp = mega.create_folder(TEST_FOLDER)
|
||||||
|
assert isinstance(resp, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rename(mega):
|
||||||
|
file = mega.find(TEST_FOLDER)
|
||||||
|
if file:
|
||||||
|
resp = mega.rename(file, TEST_FOLDER)
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_folder(mega):
|
||||||
|
folder_node = mega.find(TEST_FOLDER)[0]
|
||||||
|
resp = mega.delete(folder_node)
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete(mega):
|
||||||
|
file = mega.find(TEST_FILE)
|
||||||
|
if file:
|
||||||
|
resp = mega.delete(file[0])
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_destroy(mega):
|
||||||
|
file = mega.find(TEST_FILE)
|
||||||
|
if file:
|
||||||
|
resp = mega.destroy(file[0])
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_trash(mega):
|
||||||
|
# resp None if already empty, else int
|
||||||
|
resp = mega.empty_trash()
|
||||||
|
if resp is not None:
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_contact(mega):
|
||||||
|
resp = mega.add_contact(TEST_CONTACT)
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
||||||
|
|
||||||
|
def test_remove_contact(mega):
|
||||||
|
resp = mega.remove_contact(TEST_CONTACT)
|
||||||
|
assert isinstance(resp, int)
|
||||||
|
|
Loading…
Reference in a new issue