update repository references and improve script handling
This commit is contained in:
427
hassio-google-drive-backup/tests/conftest.py
Normal file
427
hassio-google-drive-backup/tests/conftest.py
Normal file
@@ -0,0 +1,427 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import asyncio
|
||||
import platform
|
||||
import aiohttp
|
||||
from yarl import URL
|
||||
|
||||
import pytest
|
||||
from aiohttp import ClientSession
|
||||
from injector import (ClassAssistedBuilder, Injector, Module, inject, provider,
|
||||
singleton)
|
||||
|
||||
from backup.config import Config, Setting
|
||||
from backup.model import Coordinator
|
||||
from dev.simulationserver import SimulationServer
|
||||
from backup.drive import DriveRequests, DriveSource, FolderFinder, AuthCodeQuery
|
||||
from backup.util import GlobalInfo, Estimator, Resolver, DataCache
|
||||
from backup.ha import HaRequests, HaSource, HaUpdater
|
||||
from backup.logger import reset
|
||||
from backup.model import DummyBackup, DestinationPrecache, Model
|
||||
from backup.time import Time
|
||||
from backup.module import BaseModule
|
||||
from backup.debugworker import DebugWorker
|
||||
from backup.creds import Creds, DriveRequester
|
||||
from backup.server import ErrorStore
|
||||
from backup.ha import AddonStopper
|
||||
from backup.ui import UiServer
|
||||
from backup.watcher import Watcher
|
||||
from .faketime import FakeTime
|
||||
from .helpers import Uploader, createBackupTar
|
||||
from dev.ports import Ports
|
||||
from dev.simulated_google import SimulatedGoogle
|
||||
from dev.request_interceptor import RequestInterceptor
|
||||
from dev.simulated_supervisor import SimulatedSupervisor
|
||||
|
||||
|
||||
@singleton
|
||||
class FsFaker():
|
||||
@inject
|
||||
def __init__(self):
|
||||
self.bytes_free = 1024 * 1024 * 1024
|
||||
self.bytes_total = 1024 * 1024 * 1024
|
||||
self.old_method = None
|
||||
|
||||
def start(self):
|
||||
if platform.system() != "Windows":
|
||||
self.old_method = os.statvfs
|
||||
os.statvfs = self._hijack
|
||||
|
||||
def stop(self):
|
||||
if platform.system() != "Windows":
|
||||
os.statvfs = self.old_method
|
||||
|
||||
def _hijack(self, path):
|
||||
return os.statvfs_result((0, 1, int(self.bytes_total), int(self.bytes_free), int(self.bytes_free), 0, 0, 0, 0, 255))
|
||||
|
||||
def setFreeBytes(self, bytes_free, bytes_total=1):
|
||||
self.bytes_free = bytes_free
|
||||
self.bytes_total = bytes_total
|
||||
if self.bytes_free > self.bytes_total:
|
||||
self.bytes_total = self.bytes_free
|
||||
|
||||
|
||||
class ReaderHelper:
|
||||
def __init__(self, session, ui_port, ingress_port):
|
||||
self.session = session
|
||||
self.ui_port = ui_port
|
||||
self.ingress_port = ingress_port
|
||||
self.timeout = aiohttp.ClientTimeout(total=20)
|
||||
|
||||
def getUrl(self, ingress=True, ssl=False):
|
||||
if ssl:
|
||||
protocol = "https"
|
||||
else:
|
||||
protocol = "http"
|
||||
if ingress:
|
||||
return protocol + "://localhost:" + str(self.ingress_port) + "/"
|
||||
else:
|
||||
return protocol + "://localhost:" + str(self.ui_port) + "/"
|
||||
|
||||
async def getjson(self, path, status=200, json=None, auth=None, ingress=True, ssl=False, sslcontext=None):
|
||||
async with self.session.get(self.getUrl(ingress, ssl) + path, json=json, auth=auth, ssl=sslcontext, timeout=self.timeout) as resp:
|
||||
assert resp.status == status
|
||||
return await resp.json()
|
||||
|
||||
async def get(self, path, status=200, json=None, auth=None, ingress=True, ssl=False):
|
||||
async with self.session.get(self.getUrl(ingress, ssl) + path, json=json, auth=auth, timeout=self.timeout) as resp:
|
||||
if resp.status != status:
|
||||
import logging
|
||||
logging.getLogger().error(resp.text())
|
||||
assert resp.status == status
|
||||
return await resp.text()
|
||||
|
||||
async def postjson(self, path, status=200, json=None, ingress=True):
|
||||
async with self.session.post(self.getUrl(ingress) + path, json=json, timeout=self.timeout) as resp:
|
||||
assert resp.status == status
|
||||
return await resp.json()
|
||||
|
||||
async def assertError(self, path, error_type="generic_error", status=500, ingress=True, json=None):
|
||||
logging.getLogger().info("Requesting " + path)
|
||||
data = await self.getjson(path, status=status, ingress=ingress, json=json)
|
||||
assert data['error_type'] == error_type
|
||||
|
||||
|
||||
# This module should onyl ever have bindings that can also be satisfied by MainModule
|
||||
class TestModule(Module):
|
||||
def __init__(self, config: Config, ports: Ports):
|
||||
self.ports = ports
|
||||
self.config = config
|
||||
|
||||
@provider
|
||||
@singleton
|
||||
def getDriveCreds(self, time: Time) -> Creds:
|
||||
return Creds(time, "test_client_id", time.now(), "test_access_token", "test_refresh_token", "test_client_secret")
|
||||
|
||||
@provider
|
||||
@singleton
|
||||
def getTime(self) -> Time:
|
||||
return FakeTime()
|
||||
|
||||
@provider
|
||||
@singleton
|
||||
def getPorts(self) -> Ports:
|
||||
return self.ports
|
||||
|
||||
@provider
|
||||
@singleton
|
||||
def getConfig(self) -> Config:
|
||||
return self.config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def event_loop():
|
||||
if platform.system() == "Windows":
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
return asyncio.new_event_loop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def generate_config(server_url: URL, ports, cleandir):
|
||||
return Config.withOverrides({
|
||||
Setting.DRIVE_URL: str(server_url),
|
||||
Setting.SUPERVISOR_URL: str(server_url) + "/",
|
||||
Setting.AUTHORIZATION_HOST: str(server_url),
|
||||
Setting.TOKEN_SERVER_HOSTS: str(server_url),
|
||||
Setting.DRIVE_REFRESH_URL: str(server_url.with_path("/oauth2/v4/token")),
|
||||
Setting.DRIVE_AUTHORIZE_URL: str(server_url.with_path("/o/oauth2/v2/auth")),
|
||||
Setting.DRIVE_TOKEN_URL: str(server_url.with_path("/token")),
|
||||
Setting.DRIVE_DEVICE_CODE_URL: str(server_url.with_path("/device/code")),
|
||||
Setting.SUPERVISOR_TOKEN: "test_header",
|
||||
Setting.SECRETS_FILE_PATH: "secrets.yaml",
|
||||
Setting.CREDENTIALS_FILE_PATH: "credentials.dat",
|
||||
Setting.FOLDER_FILE_PATH: "folder.dat",
|
||||
Setting.RETAINED_FILE_PATH: "retained.json",
|
||||
Setting.ID_FILE_PATH: "id.json",
|
||||
Setting.DATA_CACHE_FILE_PATH: "data_cache.json",
|
||||
Setting.STOP_ADDON_STATE_PATH: "stop_addon.json",
|
||||
Setting.INGRESS_TOKEN_FILE_PATH: "ingress.dat",
|
||||
Setting.DEFAULT_DRIVE_CLIENT_ID: "test_client_id",
|
||||
Setting.DEFAULT_DRIVE_CLIENT_SECRET: "test_client_secret",
|
||||
Setting.BACKUP_DIRECTORY_PATH: os.path.join(cleandir, "backups"),
|
||||
Setting.PORT: ports.ui,
|
||||
Setting.INGRESS_PORT: ports.ingress,
|
||||
Setting.BACKUP_STARTUP_DELAY_MINUTES: 0,
|
||||
Setting.PING_TIMEOUT: 0.1,
|
||||
})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def injector(cleandir, ports, generate_config):
|
||||
drive_creds = Creds(FakeTime(), "test_client_id", None, "test_access_token", "test_refresh_token")
|
||||
|
||||
os.mkdir(os.path.join(cleandir, "backups"))
|
||||
with open(os.path.join(cleandir, "secrets.yaml"), "w") as f:
|
||||
f.write("for_unit_tests: \"password value\"\n")
|
||||
|
||||
with open(os.path.join(cleandir, "credentials.dat"), "w") as f:
|
||||
f.write(json.dumps(drive_creds.serialize()))
|
||||
|
||||
return Injector([BaseModule(), TestModule(generate_config, ports)])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ui_server(injector, server):
|
||||
os.mkdir("static")
|
||||
server = injector.get(UiServer)
|
||||
await server.run()
|
||||
yield server
|
||||
await server.shutdown()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def reader(server, ui_server, session, ui_port, ingress_port):
|
||||
return ReaderHelper(session, ui_port, ingress_port)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def uploader(injector: Injector, server_url):
|
||||
return injector.get(ClassAssistedBuilder[Uploader]).build(host=str(server_url))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def google(injector: Injector):
|
||||
return injector.get(SimulatedGoogle)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def interceptor(injector: Injector):
|
||||
return injector.get(RequestInterceptor)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def supervisor(injector: Injector, server, session):
|
||||
return injector.get(SimulatedSupervisor)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def addon_stopper(injector: Injector):
|
||||
return injector.get(AddonStopper)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def server(injector, port, drive_creds: Creds, session):
|
||||
server = injector.get(SimulationServer)
|
||||
|
||||
# start the server
|
||||
logging.getLogger().info("Starting SimulationServer on port " + str(port))
|
||||
await server.start(port)
|
||||
yield server
|
||||
await server.stop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def data_cache(injector):
|
||||
return injector.get(DataCache)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def session(injector):
|
||||
async with injector.get(ClientSession) as session:
|
||||
yield session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def precache(injector):
|
||||
return injector.get(DestinationPrecache)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def backup(coord, source, dest):
|
||||
await coord.sync()
|
||||
assert len(coord.backups()) == 1
|
||||
return coord.backups()[0]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def fs(injector):
|
||||
faker = injector.get(FsFaker)
|
||||
faker.start()
|
||||
yield faker
|
||||
faker.stop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def estimator(injector, fs):
|
||||
return injector.get(Estimator)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def device_code(injector):
|
||||
return injector.get(AuthCodeQuery)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def error_store(injector):
|
||||
return injector.get(ErrorStore)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def model(injector):
|
||||
return injector.get(Model)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def global_info(injector):
|
||||
return injector.get(GlobalInfo)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def server_url(port):
|
||||
return URL("http://localhost:").with_port(port)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ports(unused_tcp_port_factory):
|
||||
return Ports(unused_tcp_port_factory(), unused_tcp_port_factory(), unused_tcp_port_factory())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def port(ports: Ports):
|
||||
return ports.server
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ui_url(ports: Ports):
|
||||
return URL("http://localhost").with_port(ports.ingress)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ui_port(ports: Ports):
|
||||
return ports.ui
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ingress_port(ports: Ports):
|
||||
return ports.ingress
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def coord(injector):
|
||||
return injector.get(Coordinator)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def updater(injector):
|
||||
return injector.get(HaUpdater)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def cleandir():
|
||||
newpath = tempfile.mkdtemp()
|
||||
os.chdir(newpath)
|
||||
return newpath
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def time(injector):
|
||||
reset()
|
||||
return injector.get(Time)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def config(injector):
|
||||
return injector.get(Config)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def drive_creds(injector):
|
||||
return injector.get(Creds)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def drive(injector, server, session):
|
||||
return injector.get(DriveSource)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ha(injector, server, session):
|
||||
return injector.get(HaSource)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def ha_requests(injector, server):
|
||||
return injector.get(HaRequests)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def drive_requests(injector, server):
|
||||
return injector.get(DriveRequests)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def drive_requester(injector, server):
|
||||
return injector.get(DriveRequester)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def verify_closed_responses(drive_requester: DriveRequester):
|
||||
yield "unused"
|
||||
for resp in drive_requester.all_resposnes:
|
||||
assert resp.closed
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def resolver(injector):
|
||||
return injector.get(Resolver)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client_identifier(injector):
|
||||
return injector.get(Config).clientIdentifier()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def debug_worker(injector):
|
||||
return injector.get(DebugWorker)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def folder_finder(injector):
|
||||
return injector.get(FolderFinder)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def watcher(injector):
|
||||
watcher = injector.get(Watcher)
|
||||
yield watcher
|
||||
await watcher.stop()
|
||||
|
||||
|
||||
class BackupHelper():
|
||||
def __init__(self, uploader, time):
|
||||
self.time = time
|
||||
self.uploader = uploader
|
||||
|
||||
async def createFile(self, size=1024 * 1024 * 2, slug="testslug", name="Test Name"):
|
||||
from_backup: DummyBackup = DummyBackup(
|
||||
name, self.time.toUtc(self.time.local(1985, 12, 6)), "fake source", slug)
|
||||
data = await self.uploader.upload(createBackupTar(slug, name, self.time.now(), size))
|
||||
return from_backup, data
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def backup_helper(uploader, time):
|
||||
return BackupHelper(uploader, time)
|
||||
Reference in New Issue
Block a user