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

View file

@ -1,13 +1,87 @@
"""Library tests.""" """Library tests."""
import json
from requests import Session, Response
from pyicloud import base from pyicloud import base
from pyicloud.exceptions import PyiCloudFailedLoginException from pyicloud.exceptions import PyiCloudFailedLoginException
from pyicloud.services.findmyiphone import FindMyiPhoneServiceManager, AppleDevice 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" class ResponseMock(Response):
VALID_USERS = [AUTHENTICATED_USER, REQUIRES_2SA_USER] """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): class PyiCloudServiceMock(base.PyiCloudService):
@ -22,174 +96,7 @@ class PyiCloudServiceMock(base.PyiCloudService):
client_id=None, client_id=None,
with_family=True, with_family=True,
): ):
base.PyiCloudSession = PyiCloudSessionMock
base.PyiCloudService.__init__( base.PyiCloudService.__init__(
self, apple_id, password, cookie_directory, verify, client_id, with_family 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.""" """Cmdline tests."""
from pyicloud import cmdline 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 os
import sys import sys
@ -45,8 +47,11 @@ class TestCmdline(TestCase):
with pytest.raises(SystemExit, match="2"): with pytest.raises(SystemExit, match="2"):
self.main(["--username"]) self.main(["--username"])
@patch("keyring.get_password", return_value=None)
@patch("getpass.getpass") @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.""" """Test username and password commands."""
# No password supplied # No password supplied
mock_getpass.return_value = None mock_getpass.return_value = None
@ -66,8 +71,11 @@ class TestCmdline(TestCase):
): ):
self.main(["--username", "invalid_user", "--password", "invalid_pass"]) self.main(["--username", "invalid_user", "--password", "invalid_pass"])
@patch("keyring.get_password", return_value=None)
@patch("pyicloud.cmdline.input") @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.""" """Test username and password commands."""
# Valid connection for the first time # Valid connection for the first time
mock_input.return_value = "0" mock_input.return_value = "0"
@ -75,25 +83,29 @@ class TestCmdline(TestCase):
# fmt: off # fmt: off
self.main([ self.main([
'--username', REQUIRES_2SA_USER, '--username', REQUIRES_2SA_USER,
'--password', 'valid_pass', '--password', VALID_PASSWORD,
'--non-interactive', '--non-interactive',
]) ])
# fmt: on # 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.""" """Test the outputfile command."""
with pytest.raises(SystemExit, match="0"): with pytest.raises(SystemExit, match="0"):
# fmt: off # fmt: off
self.main([ self.main([
'--username', AUTHENTICATED_USER, '--username', AUTHENTICATED_USER,
'--password', 'valid_pass', '--password', VALID_PASSWORD,
'--non-interactive', '--non-interactive',
'--outputfile' '--outputfile'
]) ])
# fmt: on # fmt: on
for key in DEVICES: devices = FMI_FMLY_WORKING.get("content")
file_name = DEVICES[key].content["name"].strip().lower() + ".fmip_snapshot" for device in devices:
file_name = device.get("name").strip().lower() + ".fmip_snapshot"
pickle_file = open(file_name, "rb") pickle_file = open(file_name, "rb")
assert pickle_file assert pickle_file
@ -105,7 +117,7 @@ class TestCmdline(TestCase):
contents.append(pickle.load(opened_file)) contents.append(pickle.load(opened_file))
except EOFError: except EOFError:
break break
assert contents == [DEVICES[key].content] assert contents == [device]
pickle_file.close() pickle_file.close()
os.remove(file_name) 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