Test rework + add account & fmi device test (#266)

* Rework tests

* Add account test

* Add Find My iPhone devices test

* Remove logger

* Working with Python 3.4

* Make test working in more setups

@patch("keyring.get_password", return_value=None)

* Fix Python 2.7 ASCII

* Pylint

* Self reviewed
This commit is contained in:
Quentame 2020-04-03 18:50:12 +02:00 committed by GitHub
parent d510b14570
commit 91ac1d956e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 1868 additions and 194 deletions

View file

@ -56,9 +56,9 @@ class PyiCloudSession(Session):
def __init__(self, service):
self.service = service
super(PyiCloudSession, self).__init__()
Session.__init__(self)
def request(self, *args, **kwargs): # pylint: disable=arguments-differ
def request(self, method, url, **kwargs): # pylint: disable=arguments-differ
# Charge logging to the right service endpoint
callee = inspect.stack()[2]
@ -67,10 +67,10 @@ class PyiCloudSession(Session):
if self.service.password_filter not in request_logger.filters:
request_logger.addFilter(self.service.password_filter)
request_logger.debug("%s %s %s", args[0], args[1], kwargs.get("data", ""))
request_logger.debug("%s %s %s", method, url, kwargs.get("data", ""))
kwargs.pop("retried", None)
response = super(PyiCloudSession, self).request(*args, **kwargs)
response = super(PyiCloudSession, self).request(method, url, **kwargs)
content_type = response.headers.get("Content-Type", "").split(";")[0]
json_mimetypes = ["application/json", "text/json"]
@ -82,7 +82,7 @@ class PyiCloudSession(Session):
)
request_logger.warn(api_error)
kwargs["retried"] = True
return self.request(*args, **kwargs)
return self.request(method, url, **kwargs)
self._raise_error(response.status_code, response.reason)
if content_type not in json_mimetypes:
@ -150,6 +150,9 @@ class PyiCloudService(object):
pyicloud.iphone.location()
"""
HOME_ENDPOINT = "https://www.icloud.com"
SETUP_ENDPOINT = "https://setup.icloud.com/setup/ws/1"
def __init__(
self,
apple_id,
@ -170,10 +173,7 @@ class PyiCloudService(object):
self.password_filter = PyiCloudPasswordFilter(password)
LOGGER.addFilter(self.password_filter)
self._home_endpoint = "https://www.icloud.com"
self._setup_endpoint = "https://setup.icloud.com/setup/ws/1"
self._base_login_url = "%s/login" % self._setup_endpoint
self._base_login_url = "%s/login" % self.SETUP_ENDPOINT
if cookie_directory:
self._cookie_directory = os.path.expanduser(
@ -186,8 +186,8 @@ class PyiCloudService(object):
self.session.verify = verify
self.session.headers.update(
{
"Origin": self._home_endpoint,
"Referer": "%s/" % self._home_endpoint,
"Origin": self.HOME_ENDPOINT,
"Referer": "%s/" % self.HOME_ENDPOINT,
"User-Agent": "Opera/9.52 (X11; Linux i686; U; en)",
}
)
@ -270,7 +270,7 @@ class PyiCloudService(object):
def trusted_devices(self):
"""Returns devices trusted for two-step authentication."""
request = self.session.get(
"%s/listDevices" % self._setup_endpoint, params=self.params
"%s/listDevices" % self.SETUP_ENDPOINT, params=self.params
)
return request.json().get("devices")
@ -278,7 +278,7 @@ class PyiCloudService(object):
"""Requests that a verification code is sent to the given device."""
data = json.dumps(device)
request = self.session.post(
"%s/sendVerificationCode" % self._setup_endpoint,
"%s/sendVerificationCode" % self.SETUP_ENDPOINT,
params=self.params,
data=data,
)
@ -291,7 +291,7 @@ class PyiCloudService(object):
try:
self.session.post(
"%s/validateVerificationCode" % self._setup_endpoint,
"%s/validateVerificationCode" % self.SETUP_ENDPOINT,
params=self.params,
data=data,
)

View file

@ -1,13 +1,87 @@
"""Library tests."""
import json
from requests import Session, Response
from pyicloud import base
from pyicloud.exceptions import PyiCloudFailedLoginException
from pyicloud.services.findmyiphone import FindMyiPhoneServiceManager, AppleDevice
from .const import (
AUTHENTICATED_USER,
REQUIRES_2SA_USER,
VALID_USERS,
VALID_PASSWORD,
)
from .const_login import (
LOGIN_WORKING,
LOGIN_2SA,
TRUSTED_DEVICES,
TRUSTED_DEVICE_1,
VERIFICATION_CODE_OK,
VERIFICATION_CODE_KO,
)
from .const_account import ACCOUNT_DEVICES_WORKING
from .const_findmyiphone import FMI_FMLY_WORKING
AUTHENTICATED_USER = "authenticated_user"
REQUIRES_2SA_USER = "requires_2sa_user"
VALID_USERS = [AUTHENTICATED_USER, REQUIRES_2SA_USER]
class ResponseMock(Response):
"""Mocked Response."""
def __init__(self, result, status_code=200):
Response.__init__(self)
self.result = result
self.status_code = status_code
@property
def text(self):
return json.dumps(self.result)
class PyiCloudSessionMock(base.PyiCloudSession):
"""Mocked PyiCloudSession."""
def request(self, method, url, **kwargs):
data = json.loads(kwargs.get("data", "{}"))
# Login
if self.service.SETUP_ENDPOINT in url:
if "login" in url and method == "POST":
if (
data.get("apple_id") not in VALID_USERS
or data.get("password") != VALID_PASSWORD
):
self._raise_error(None, "Unknown reason")
if (
data.get("apple_id") == REQUIRES_2SA_USER
and data.get("password") == VALID_PASSWORD
):
return ResponseMock(LOGIN_2SA)
return ResponseMock(LOGIN_WORKING)
if "listDevices" in url and method == "GET":
return ResponseMock(TRUSTED_DEVICES)
if "sendVerificationCode" in url and method == "POST":
if data == TRUSTED_DEVICE_1:
return ResponseMock(VERIFICATION_CODE_OK)
return ResponseMock(VERIFICATION_CODE_KO)
if "validateVerificationCode" in url and method == "POST":
TRUSTED_DEVICE_1.update({"verificationCode": "0", "trustBrowser": True})
if data == TRUSTED_DEVICE_1:
self.service.user["apple_id"] = AUTHENTICATED_USER
return ResponseMock(VERIFICATION_CODE_OK)
self._raise_error(None, "FOUND_CODE")
# Account
if "device/getDevices" in url and method == "GET":
return ResponseMock(ACCOUNT_DEVICES_WORKING)
# Find My iPhone
if "fmi" in url and method == "POST":
return ResponseMock(FMI_FMLY_WORKING)
return None
class PyiCloudServiceMock(base.PyiCloudService):
@ -22,174 +96,7 @@ class PyiCloudServiceMock(base.PyiCloudService):
client_id=None,
with_family=True,
):
base.PyiCloudSession = PyiCloudSessionMock
base.PyiCloudService.__init__(
self, apple_id, password, cookie_directory, verify, client_id, with_family
)
base.FindMyiPhoneServiceManager = FindMyiPhoneServiceManagerMock
def authenticate(self):
if (
not self.user.get("apple_id")
or self.user.get("apple_id") not in VALID_USERS
):
raise PyiCloudFailedLoginException(
"Invalid email/password combination.", None
)
if not self.user.get("password") or self.user.get("password") != "valid_pass":
raise PyiCloudFailedLoginException(
"Invalid email/password combination.", None
)
self.params.update({"dsid": "ID"})
self._webservices = {
"account": {"url": "account_url",},
"findme": {"url": "findme_url",},
"calendar": {"url": "calendar_url",},
"contacts": {"url": "contacts_url",},
"reminders": {"url": "reminders_url",},
}
@property
def requires_2sa(self):
return self.user["apple_id"] is REQUIRES_2SA_USER
@property
def trusted_devices(self):
return [
{
"deviceType": "SMS",
"areaCode": "",
"phoneNumber": "*******58",
"deviceId": "1",
}
]
def send_verification_code(self, device):
return device
def validate_verification_code(self, device, code):
if not device or code != 0:
self.user["apple_id"] = AUTHENTICATED_USER
self.authenticate()
return not self.requires_2sa
IPHONE_DEVICE_ID = "X1x/X&x="
IPHONE_DEVICE = AppleDevice(
{
"msg": {
"strobe": False,
"userText": False,
"playSound": True,
"vibrate": True,
"createTimestamp": 1568031021347,
"statusCode": "200",
},
"canWipeAfterLock": True,
"baUUID": "",
"wipeInProgress": False,
"lostModeEnabled": False,
"activationLocked": True,
"passcodeLength": 6,
"deviceStatus": "200",
"deviceColor": "1-6-0",
"features": {
"MSG": True,
"LOC": True,
"LLC": False,
"CLK": False,
"TEU": True,
"LMG": False,
"SND": True,
"CLT": False,
"LKL": False,
"SVP": False,
"LST": True,
"LKM": False,
"WMG": True,
"SPN": False,
"XRM": False,
"PIN": False,
"LCK": True,
"REM": False,
"MCS": False,
"CWP": False,
"KEY": False,
"KPD": False,
"WIP": True,
},
"lowPowerMode": True,
"rawDeviceModel": "iPhone11,8",
"id": IPHONE_DEVICE_ID,
"remoteLock": None,
"isLocating": True,
"modelDisplayName": "iPhone",
"lostTimestamp": "",
"batteryLevel": 0.47999998927116394,
"mesg": None,
"locationEnabled": True,
"lockedTimestamp": None,
"locFoundEnabled": False,
"snd": {"createTimestamp": 1568031021347, "statusCode": "200"},
"fmlyShare": False,
"lostDevice": {
"stopLostMode": False,
"emailUpdates": False,
"userText": True,
"sound": False,
"ownerNbr": "",
"text": "",
"createTimestamp": 1558383841233,
"statusCode": "2204",
},
"lostModeCapable": True,
"wipedTimestamp": None,
"deviceDisplayName": "iPhone XR",
"prsId": None,
"audioChannels": [],
"locationCapable": True,
"batteryStatus": "NotCharging",
"trackingInfo": None,
"name": "Quentin's iPhone",
"isMac": False,
"thisDevice": False,
"deviceClass": "iPhone",
"location": {
"isOld": False,
"isInaccurate": False,
"altitude": 0.0,
"positionType": "GPS",
"latitude": 46.012345678,
"floorLevel": 0,
"horizontalAccuracy": 12.012345678,
"locationType": "",
"timeStamp": 1568827039692,
"locationFinished": False,
"verticalAccuracy": 0.0,
"longitude": 5.012345678,
},
"deviceModel": "iphoneXR-1-6-0",
"maxMsgChar": 160,
"darkWake": False,
"remoteWipe": None,
},
None,
None,
None,
)
DEVICES = {
IPHONE_DEVICE_ID: IPHONE_DEVICE,
}
class FindMyiPhoneServiceManagerMock(FindMyiPhoneServiceManager):
"""Mocked FindMyiPhoneServiceManager."""
def __init__(self, service_root, session, params, with_family=False):
FindMyiPhoneServiceManager.__init__(
self, service_root, session, params, with_family
)
def refresh_client(self):
self._devices = DEVICES

10
tests/const.py Normal file
View file

@ -0,0 +1,10 @@
"""Test constants."""
from .const_login import PRIMARY_EMAIL, APPLE_ID_EMAIL, ICLOUD_ID_EMAIL
# Base
AUTHENTICATED_USER = PRIMARY_EMAIL
REQUIRES_2SA_USER = "requires_2sa_user"
VALID_USERS = [AUTHENTICATED_USER, REQUIRES_2SA_USER, APPLE_ID_EMAIL, ICLOUD_ID_EMAIL]
VALID_PASSWORD = "valid_password"
CLIENT_ID = "client_id"

77
tests/const_account.py Normal file
View file

@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""Account test constants."""
from .const_login import FIRST_NAME
# Fakers
PAYMENT_METHOD_ID_1 = "PAYMENT_METHOD_ID_1"
PAYMENT_METHOD_ID_2 = "PAYMENT_METHOD_ID_2"
PAYMENT_METHOD_ID_3 = "PAYMENT_METHOD_ID_3"
PAYMENT_METHOD_ID_4 = "PAYMENT_METHOD_ID_4"
# Data
ACCOUNT_DEVICES_WORKING = {
"devices": [
{
"serialNumber": "●●●●●●●NG123",
"osVersion": "OSX;10.15.3",
"modelLargePhotoURL2x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/MacBookPro/MacBookPro15,1-spacegray/online-infobox__2x.png",
"modelLargePhotoURL1x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/MacBookPro/MacBookPro15,1-spacegray/online-infobox.png",
"paymentMethods": [PAYMENT_METHOD_ID_3],
"name": "MacBook Pro de " + FIRST_NAME,
"imei": "",
"model": "MacBookPro15,1",
"udid": "MacBookPro15,1" + FIRST_NAME,
"modelSmallPhotoURL2x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/MacBookPro/MacBookPro15,1-spacegray/online-sourcelist__2x.png",
"modelSmallPhotoURL1x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/MacBookPro/MacBookPro15,1-spacegray/online-sourcelist.png",
"modelDisplayName": 'MacBook Pro 15"',
},
{
"serialNumber": "●●●●●●●UX123",
"osVersion": "iOS;13.3",
"modelLargePhotoURL2x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/iPhone/iPhone12,1-1-6-0/online-infobox__2x.png",
"modelLargePhotoURL1x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/iPhone/iPhone12,1-1-6-0/online-infobox.png",
"paymentMethods": [
PAYMENT_METHOD_ID_4,
PAYMENT_METHOD_ID_2,
PAYMENT_METHOD_ID_1,
],
"name": "iPhone de " + FIRST_NAME,
"imei": "●●●●●●●●●●12345",
"model": "iPhone12,1",
"udid": "iPhone12,1" + FIRST_NAME,
"modelSmallPhotoURL2x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/iPhone/iPhone12,1-1-6-0/online-sourcelist__2x.png",
"modelSmallPhotoURL1x": "https://statici.icloud.com/fmipmobile/deviceImages-4.0/iPhone/iPhone12,1-1-6-0/online-sourcelist.png",
"modelDisplayName": "iPhone 11",
},
],
"paymentMethods": [
{
"lastFourDigits": "333",
"balanceStatus": "NOTAPPLICABLE",
"suspensionReason": "ACTIVE",
"id": PAYMENT_METHOD_ID_3,
"type": "Boursorama Banque",
},
{
"lastFourDigits": "444",
"balanceStatus": "NOTAPPLICABLE",
"suspensionReason": "ACTIVE",
"id": PAYMENT_METHOD_ID_4,
"type": "Carte Crédit Agricole",
},
{
"lastFourDigits": "2222",
"balanceStatus": "NOTAPPLICABLE",
"suspensionReason": "ACTIVE",
"id": PAYMENT_METHOD_ID_2,
"type": "Lydia",
},
{
"lastFourDigits": "111",
"balanceStatus": "NOTAPPLICABLE",
"suspensionReason": "ACTIVE",
"id": PAYMENT_METHOD_ID_1,
"type": "Boursorama Banque",
},
],
}

1136
tests/const_findmyiphone.py Normal file

File diff suppressed because it is too large Load diff

413
tests/const_login.py Normal file
View file

@ -0,0 +1,413 @@
"""Login test constants."""
# Base
FIRST_NAME = "Quentin"
LAST_NAME = "TARANTINO"
FULL_NAME = FIRST_NAME + " " + LAST_NAME
PERSON_ID = (FIRST_NAME + LAST_NAME).lower()
NOTIFICATION_ID = "12345678-1234-1234-1234-123456789012" + PERSON_ID
A_DS_ID = "123456-12-12345678-1234-1234-1234-123456789012" + PERSON_ID
WIDGET_KEY = "widget_key" + PERSON_ID
PRIMARY_EMAIL = PERSON_ID + "@hotmail.fr"
APPLE_ID_EMAIL = PERSON_ID + "@me.com"
ICLOUD_ID_EMAIL = PERSON_ID + "@icloud.com"
# Data
LOGIN_WORKING = {
"dsInfo": {
"lastName": LAST_NAME,
"iCDPEnabled": False,
"tantorMigrated": True,
"dsid": PERSON_ID,
"hsaEnabled": True,
"ironcadeMigrated": True,
"locale": "fr-fr_FR",
"brZoneConsolidated": False,
"isManagedAppleID": False,
"gilligan-invited": "true",
"appleIdAliases": [APPLE_ID_EMAIL, ICLOUD_ID_EMAIL],
"hsaVersion": 2,
"isPaidDeveloper": False,
"countryCode": "FRA",
"notificationId": NOTIFICATION_ID,
"primaryEmailVerified": True,
"aDsID": A_DS_ID,
"locked": False,
"hasICloudQualifyingDevice": True,
"primaryEmail": PRIMARY_EMAIL,
"appleIdEntries": [
{"isPrimary": True, "type": "EMAIL", "value": PRIMARY_EMAIL},
{"type": "EMAIL", "value": APPLE_ID_EMAIL},
{"type": "EMAIL", "value": ICLOUD_ID_EMAIL},
],
"gilligan-enabled": "true",
"fullName": FULL_NAME,
"languageCode": "fr-fr",
"appleId": PRIMARY_EMAIL,
"firstName": FIRST_NAME,
"iCloudAppleIdAlias": ICLOUD_ID_EMAIL,
"notesMigrated": True,
"hasPaymentInfo": False,
"pcsDeleted": False,
"appleIdAlias": APPLE_ID_EMAIL,
"brMigrated": True,
"statusCode": 2,
"familyEligible": True,
},
"hasMinimumDeviceForPhotosWeb": True,
"iCDPEnabled": False,
"webservices": {
"reminders": {
"url": "https://p31-remindersws.icloud.com:443",
"status": "active",
},
"notes": {"url": "https://p38-notesws.icloud.com:443", "status": "active"},
"mail": {"url": "https://p38-mailws.icloud.com:443", "status": "active"},
"ckdatabasews": {
"pcsRequired": True,
"url": "https://p31-ckdatabasews.icloud.com:443",
"status": "active",
},
"photosupload": {
"pcsRequired": True,
"url": "https://p31-uploadphotosws.icloud.com:443",
"status": "active",
},
"photos": {
"pcsRequired": True,
"uploadUrl": "https://p31-uploadphotosws.icloud.com:443",
"url": "https://p31-photosws.icloud.com:443",
"status": "active",
},
"drivews": {
"pcsRequired": True,
"url": "https://p31-drivews.icloud.com:443",
"status": "active",
},
"uploadimagews": {
"url": "https://p31-uploadimagews.icloud.com:443",
"status": "active",
},
"schoolwork": {},
"cksharews": {"url": "https://p31-ckshare.icloud.com:443", "status": "active"},
"findme": {"url": "https://p31-fmipweb.icloud.com:443", "status": "active"},
"ckdeviceservice": {"url": "https://p31-ckdevice.icloud.com:443"},
"iworkthumbnailws": {
"url": "https://p31-iworkthumbnailws.icloud.com:443",
"status": "active",
},
"calendar": {
"url": "https://p31-calendarws.icloud.com:443",
"status": "active",
},
"docws": {
"pcsRequired": True,
"url": "https://p31-docws.icloud.com:443",
"status": "active",
},
"settings": {
"url": "https://p31-settingsws.icloud.com:443",
"status": "active",
},
"ubiquity": {
"url": "https://p31-ubiquityws.icloud.com:443",
"status": "active",
},
"streams": {"url": "https://p31-streams.icloud.com:443", "status": "active"},
"keyvalue": {
"url": "https://p31-keyvalueservice.icloud.com:443",
"status": "active",
},
"archivews": {
"url": "https://p31-archivews.icloud.com:443",
"status": "active",
},
"push": {"url": "https://p31-pushws.icloud.com:443", "status": "active"},
"iwmb": {"url": "https://p31-iwmb.icloud.com:443", "status": "active"},
"iworkexportws": {
"url": "https://p31-iworkexportws.icloud.com:443",
"status": "active",
},
"geows": {"url": "https://p31-geows.icloud.com:443", "status": "active"},
"account": {
"iCloudEnv": {"shortId": "p", "vipSuffix": "prod"},
"url": "https://p31-setup.icloud.com:443",
"status": "active",
},
"fmf": {"url": "https://p31-fmfweb.icloud.com:443", "status": "active"},
"contacts": {
"url": "https://p31-contactsws.icloud.com:443",
"status": "active",
},
},
"pcsEnabled": True,
"configBag": {
"urls": {
"accountCreateUI": "https://appleid.apple.com/widget/account/?widgetKey="
+ WIDGET_KEY
+ "#!create",
"accountLoginUI": "https://idmsa.apple.com/appleauth/auth/signin?widgetKey="
+ WIDGET_KEY,
"accountLogin": "https://setup.icloud.com/setup/ws/1/accountLogin",
"accountRepairUI": "https://appleid.apple.com/widget/account/?widgetKey="
+ WIDGET_KEY
+ "#!repair",
"downloadICloudTerms": "https://setup.icloud.com/setup/ws/1/downloadLiteTerms",
"repairDone": "https://setup.icloud.com/setup/ws/1/repairDone",
"accountAuthorizeUI": "https://idmsa.apple.com/appleauth/auth/authorize/signin?client_id="
+ WIDGET_KEY,
"vettingUrlForEmail": "https://id.apple.com/IDMSEmailVetting/vetShareEmail",
"accountCreate": "https://setup.icloud.com/setup/ws/1/createLiteAccount",
"getICloudTerms": "https://setup.icloud.com/setup/ws/1/getTerms",
"vettingUrlForPhone": "https://id.apple.com/IDMSEmailVetting/vetSharePhone",
},
"accountCreateEnabled": "true",
},
"hsaTrustedBrowser": True,
"appsOrder": [
"mail",
"contacts",
"calendar",
"photos",
"iclouddrive",
"notes3",
"reminders",
"pages",
"numbers",
"keynote",
"newspublisher",
"fmf",
"find",
"settings",
],
"version": 2,
"isExtendedLogin": False,
"pcsServiceIdentitiesIncluded": True,
"hsaChallengeRequired": False,
"requestInfo": {"country": "FR", "timeZone": "GMT+1", "region": "IDF"},
"pcsDeleted": False,
"iCloudInfo": {"SafariBookmarksHasMigratedToCloudKit": True},
"apps": {
"calendar": {},
"reminders": {},
"keynote": {"isQualifiedForBeta": True},
"settings": {"canLaunchWithOneFactor": True},
"mail": {},
"numbers": {"isQualifiedForBeta": True},
"photos": {},
"pages": {"isQualifiedForBeta": True},
"notes3": {},
"find": {"canLaunchWithOneFactor": True},
"iclouddrive": {},
"newspublisher": {"isHidden": True},
"fmf": {},
"contacts": {},
},
}
# Setup data
LOGIN_2SA = {
"dsInfo": {
"lastName": LAST_NAME,
"iCDPEnabled": False,
"tantorMigrated": True,
"dsid": PERSON_ID,
"hsaEnabled": True,
"ironcadeMigrated": True,
"locale": "fr-fr_FR",
"brZoneConsolidated": False,
"isManagedAppleID": False,
"gilligan-invited": "true",
"appleIdAliases": [APPLE_ID_EMAIL, ICLOUD_ID_EMAIL],
"hsaVersion": 2,
"isPaidDeveloper": False,
"countryCode": "FRA",
"notificationId": NOTIFICATION_ID,
"primaryEmailVerified": True,
"aDsID": A_DS_ID,
"locked": False,
"hasICloudQualifyingDevice": True,
"primaryEmail": PRIMARY_EMAIL,
"appleIdEntries": [
{"isPrimary": True, "type": "EMAIL", "value": PRIMARY_EMAIL},
{"type": "EMAIL", "value": APPLE_ID_EMAIL},
{"type": "EMAIL", "value": ICLOUD_ID_EMAIL},
],
"gilligan-enabled": "true",
"fullName": FULL_NAME,
"languageCode": "fr-fr",
"appleId": PRIMARY_EMAIL,
"firstName": FIRST_NAME,
"iCloudAppleIdAlias": ICLOUD_ID_EMAIL,
"notesMigrated": True,
"hasPaymentInfo": True,
"pcsDeleted": False,
"appleIdAlias": APPLE_ID_EMAIL,
"brMigrated": True,
"statusCode": 2,
"familyEligible": True,
},
"hasMinimumDeviceForPhotosWeb": True,
"iCDPEnabled": False,
"webservices": {
"reminders": {
"url": "https://p31-remindersws.icloud.com:443",
"status": "active",
},
"notes": {"url": "https://p38-notesws.icloud.com:443", "status": "active"},
"mail": {"url": "https://p38-mailws.icloud.com:443", "status": "active"},
"ckdatabasews": {
"pcsRequired": True,
"url": "https://p31-ckdatabasews.icloud.com:443",
"status": "active",
},
"photosupload": {
"pcsRequired": True,
"url": "https://p31-uploadphotosws.icloud.com:443",
"status": "active",
},
"photos": {
"pcsRequired": True,
"uploadUrl": "https://p31-uploadphotosws.icloud.com:443",
"url": "https://p31-photosws.icloud.com:443",
"status": "active",
},
"drivews": {
"pcsRequired": True,
"url": "https://p31-drivews.icloud.com:443",
"status": "active",
},
"uploadimagews": {
"url": "https://p31-uploadimagews.icloud.com:443",
"status": "active",
},
"schoolwork": {},
"cksharews": {"url": "https://p31-ckshare.icloud.com:443", "status": "active"},
"findme": {"url": "https://p31-fmipweb.icloud.com:443", "status": "active"},
"ckdeviceservice": {"url": "https://p31-ckdevice.icloud.com:443"},
"iworkthumbnailws": {
"url": "https://p31-iworkthumbnailws.icloud.com:443",
"status": "active",
},
"calendar": {
"url": "https://p31-calendarws.icloud.com:443",
"status": "active",
},
"docws": {
"pcsRequired": True,
"url": "https://p31-docws.icloud.com:443",
"status": "active",
},
"settings": {
"url": "https://p31-settingsws.icloud.com:443",
"status": "active",
},
"ubiquity": {
"url": "https://p31-ubiquityws.icloud.com:443",
"status": "active",
},
"streams": {"url": "https://p31-streams.icloud.com:443", "status": "active"},
"keyvalue": {
"url": "https://p31-keyvalueservice.icloud.com:443",
"status": "active",
},
"archivews": {
"url": "https://p31-archivews.icloud.com:443",
"status": "active",
},
"push": {"url": "https://p31-pushws.icloud.com:443", "status": "active"},
"iwmb": {"url": "https://p31-iwmb.icloud.com:443", "status": "active"},
"iworkexportws": {
"url": "https://p31-iworkexportws.icloud.com:443",
"status": "active",
},
"geows": {"url": "https://p31-geows.icloud.com:443", "status": "active"},
"account": {
"iCloudEnv": {"shortId": "p", "vipSuffix": "prod"},
"url": "https://p31-setup.icloud.com:443",
"status": "active",
},
"fmf": {"url": "https://p31-fmfweb.icloud.com:443", "status": "active"},
"contacts": {
"url": "https://p31-contactsws.icloud.com:443",
"status": "active",
},
},
"pcsEnabled": True,
"configBag": {
"urls": {
"accountCreateUI": "https://appleid.apple.com/widget/account/?widgetKey="
+ WIDGET_KEY
+ "#!create",
"accountLoginUI": "https://idmsa.apple.com/appleauth/auth/signin?widgetKey="
+ WIDGET_KEY,
"accountLogin": "https://setup.icloud.com/setup/ws/1/accountLogin",
"accountRepairUI": "https://appleid.apple.com/widget/account/?widgetKey="
+ WIDGET_KEY
+ "#!repair",
"downloadICloudTerms": "https://setup.icloud.com/setup/ws/1/downloadLiteTerms",
"repairDone": "https://setup.icloud.com/setup/ws/1/repairDone",
"accountAuthorizeUI": "https://idmsa.apple.com/appleauth/auth/authorize/signin?client_id="
+ WIDGET_KEY,
"vettingUrlForEmail": "https://id.apple.com/IDMSEmailVetting/vetShareEmail",
"accountCreate": "https://setup.icloud.com/setup/ws/1/createLiteAccount",
"getICloudTerms": "https://setup.icloud.com/setup/ws/1/getTerms",
"vettingUrlForPhone": "https://id.apple.com/IDMSEmailVetting/vetSharePhone",
},
"accountCreateEnabled": "true",
},
"hsaTrustedBrowser": False,
"appsOrder": [
"mail",
"contacts",
"calendar",
"photos",
"iclouddrive",
"notes3",
"reminders",
"pages",
"numbers",
"keynote",
"newspublisher",
"fmf",
"find",
"settings",
],
"version": 2,
"isExtendedLogin": False,
"pcsServiceIdentitiesIncluded": False,
"hsaChallengeRequired": True,
"requestInfo": {"country": "FR", "timeZone": "GMT+1", "region": "IDF"},
"pcsDeleted": False,
"iCloudInfo": {"SafariBookmarksHasMigratedToCloudKit": True},
"apps": {
"calendar": {},
"reminders": {},
"keynote": {"isQualifiedForBeta": True},
"settings": {"canLaunchWithOneFactor": True},
"mail": {},
"numbers": {"isQualifiedForBeta": True},
"photos": {},
"pages": {"isQualifiedForBeta": True},
"notes3": {},
"find": {"canLaunchWithOneFactor": True},
"iclouddrive": {},
"newspublisher": {"isHidden": True},
"fmf": {},
"contacts": {},
},
}
TRUSTED_DEVICE_1 = {
"deviceType": "SMS",
"areaCode": "",
"phoneNumber": "*******58",
"deviceId": "1",
}
TRUSTED_DEVICES = {"devices": [TRUSTED_DEVICE_1]}
VERIFICATION_CODE_OK = {"success": True}
VERIFICATION_CODE_KO = {"success": False}

33
tests/test_account.py Normal file
View file

@ -0,0 +1,33 @@
"""Account service tests."""
from unittest import TestCase
from . import PyiCloudServiceMock
from .const import AUTHENTICATED_USER, VALID_PASSWORD
class AccountServiceTest(TestCase):
""""Account service tests"""
service = None
def setUp(self):
self.service = PyiCloudServiceMock(AUTHENTICATED_USER, VALID_PASSWORD).account
def test_devices(self):
"""Tests devices."""
assert len(self.service.devices) == 2
for device in self.service.devices:
assert device.name
assert device.model
assert device.udid
assert device["serialNumber"]
assert device["osVersion"]
assert device["modelLargePhotoURL2x"]
assert device["modelLargePhotoURL1x"]
assert device["paymentMethods"]
assert device["name"]
assert device["model"]
assert device["udid"]
assert device["modelSmallPhotoURL2x"]
assert device["modelSmallPhotoURL1x"]
assert device["modelDisplayName"]

View file

@ -1,6 +1,8 @@
"""Cmdline tests."""
from pyicloud import cmdline
from . import PyiCloudServiceMock, AUTHENTICATED_USER, REQUIRES_2SA_USER, DEVICES
from . import PyiCloudServiceMock
from .const import AUTHENTICATED_USER, REQUIRES_2SA_USER, VALID_PASSWORD
from .const_findmyiphone import FMI_FMLY_WORKING
import os
import sys
@ -45,8 +47,11 @@ class TestCmdline(TestCase):
with pytest.raises(SystemExit, match="2"):
self.main(["--username"])
@patch("keyring.get_password", return_value=None)
@patch("getpass.getpass")
def test_username_password_invalid(self, mock_getpass):
def test_username_password_invalid(
self, mock_getpass, mock_get_password
): # pylint: disable=unused-argument
"""Test username and password commands."""
# No password supplied
mock_getpass.return_value = None
@ -66,8 +71,11 @@ class TestCmdline(TestCase):
):
self.main(["--username", "invalid_user", "--password", "invalid_pass"])
@patch("keyring.get_password", return_value=None)
@patch("pyicloud.cmdline.input")
def test_username_password_requires_2sa(self, mock_input):
def test_username_password_requires_2sa(
self, mock_input, mock_get_password
): # pylint: disable=unused-argument
"""Test username and password commands."""
# Valid connection for the first time
mock_input.return_value = "0"
@ -75,25 +83,29 @@ class TestCmdline(TestCase):
# fmt: off
self.main([
'--username', REQUIRES_2SA_USER,
'--password', 'valid_pass',
'--password', VALID_PASSWORD,
'--non-interactive',
])
# fmt: on
def test_device_outputfile(self):
@patch("keyring.get_password", return_value=None)
def test_device_outputfile(
self, mock_get_password
): # pylint: disable=unused-argument
"""Test the outputfile command."""
with pytest.raises(SystemExit, match="0"):
# fmt: off
self.main([
'--username', AUTHENTICATED_USER,
'--password', 'valid_pass',
'--password', VALID_PASSWORD,
'--non-interactive',
'--outputfile'
])
# fmt: on
for key in DEVICES:
file_name = DEVICES[key].content["name"].strip().lower() + ".fmip_snapshot"
devices = FMI_FMLY_WORKING.get("content")
for device in devices:
file_name = device.get("name").strip().lower() + ".fmip_snapshot"
pickle_file = open(file_name, "rb")
assert pickle_file
@ -105,7 +117,7 @@ class TestCmdline(TestCase):
contents.append(pickle.load(opened_file))
except EOFError:
break
assert contents == [DEVICES[key].content]
assert contents == [device]
pickle_file.close()
os.remove(file_name)

View file

@ -0,0 +1,86 @@
"""Find My iPhone service tests."""
from unittest import TestCase
from . import PyiCloudServiceMock
from .const import AUTHENTICATED_USER, VALID_PASSWORD
class FindMyiPhoneServiceTest(TestCase):
""""Find My iPhone service tests"""
service = None
def setUp(self):
self.service = PyiCloudServiceMock(AUTHENTICATED_USER, VALID_PASSWORD)
def test_devices(self):
"""Tests devices."""
assert len(list(self.service.devices)) == 13
for device in self.service.devices:
assert device["canWipeAfterLock"] is not None
assert device["baUUID"] is not None
assert device["wipeInProgress"] is not None
assert device["lostModeEnabled"] is not None
assert device["activationLocked"] is not None
assert device["passcodeLength"] is not None
assert device["deviceStatus"] is not None
assert device["features"] is not None
assert device["lowPowerMode"] is not None
assert device["rawDeviceModel"] is not None
assert device["id"] is not None
assert device["isLocating"] is not None
assert device["modelDisplayName"] is not None
assert device["lostTimestamp"] is not None
assert device["batteryLevel"] is not None
assert device["locationEnabled"] is not None
assert device["locFoundEnabled"] is not None
assert device["fmlyShare"] is not None
assert device["lostModeCapable"] is not None
assert device["wipedTimestamp"] is None
assert device["deviceDisplayName"] is not None
assert device["audioChannels"] is not None
assert device["locationCapable"] is not None
assert device["batteryStatus"] is not None
assert device["trackingInfo"] is None
assert device["name"] is not None
assert device["isMac"] is not None
assert device["thisDevice"] is not None
assert device["deviceClass"] is not None
assert device["deviceModel"] is not None
assert device["maxMsgChar"] is not None
assert device["darkWake"] is not None
assert device["remoteWipe"] is None
assert device.data["canWipeAfterLock"] is not None
assert device.data["baUUID"] is not None
assert device.data["wipeInProgress"] is not None
assert device.data["lostModeEnabled"] is not None
assert device.data["activationLocked"] is not None
assert device.data["passcodeLength"] is not None
assert device.data["deviceStatus"] is not None
assert device.data["features"] is not None
assert device.data["lowPowerMode"] is not None
assert device.data["rawDeviceModel"] is not None
assert device.data["id"] is not None
assert device.data["isLocating"] is not None
assert device.data["modelDisplayName"] is not None
assert device.data["lostTimestamp"] is not None
assert device.data["batteryLevel"] is not None
assert device.data["locationEnabled"] is not None
assert device.data["locFoundEnabled"] is not None
assert device.data["fmlyShare"] is not None
assert device.data["lostModeCapable"] is not None
assert device.data["wipedTimestamp"] is None
assert device.data["deviceDisplayName"] is not None
assert device.data["audioChannels"] is not None
assert device.data["locationCapable"] is not None
assert device.data["batteryStatus"] is not None
assert device.data["trackingInfo"] is None
assert device.data["name"] is not None
assert device.data["isMac"] is not None
assert device.data["thisDevice"] is not None
assert device.data["deviceClass"] is not None
assert device.data["deviceModel"] is not None
assert device.data["maxMsgChar"] is not None
assert device.data["darkWake"] is not None
assert device.data["remoteWipe"] is None