2
0
mirror of https://github.com/frappe/frappe_docker.git synced 2024-09-20 11:09:01 +00:00
frappe_docker/tests/main.py

446 lines
10 KiB
Python
Raw Normal View History

2021-12-13 16:37:03 +00:00
import os
2021-12-13 17:03:40 +00:00
import shutil
2021-12-13 16:37:03 +00:00
import ssl
import subprocess
from enum import Enum
from functools import wraps
from time import sleep
from typing import Any, Callable, Optional
2021-12-15 06:03:32 +00:00
from urllib.error import HTTPError, URLError
2021-12-13 16:37:03 +00:00
from urllib.request import Request, urlopen
CI = os.getenv("CI")
SITE_NAME = "tests"
BACKEND_SERVICES = (
"backend",
"queue-short",
"queue-default",
"queue-long",
"scheduler",
)
2021-12-15 05:30:00 +00:00
MINIO_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"
MINIO_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
2021-12-13 16:37:03 +00:00
def patch_print():
# Patch `print()` builtin to have nice logs when running GitHub Actions
if not CI:
return
global print
_old_print = print
def print(
*values: Any,
sep: Optional[str] = None,
end: Optional[str] = None,
file: Any = None,
flush: bool = False,
):
return _old_print(*values, sep=sep, end=end, file=file, flush=True)
class Color(Enum):
GREY = 30
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
MAGENTA = 35
CYAN = 36
WHITE = 37
def colored(text: str, color: Color):
return f"\033[{color.value}m{text}\033[0m"
def log(text: str):
def decorator(f: Callable[..., Any]):
@wraps(f)
def wrapper(*args: Any, **kwargs: Any):
if CI:
print(f"::group::{text}")
else:
print(colored(text, Color.YELLOW))
ret = f(*args, **kwargs)
if CI:
print("::endgroup::")
return ret
return wrapper
return decorator
def run(*cmd: str):
print(colored(f"> {' '.join(cmd)}", Color.GREEN))
return subprocess.check_call(cmd)
def docker_compose(*cmd: str):
args = [
"docker",
"compose",
"-p",
"test",
"--env-file",
2021-12-13 17:03:40 +00:00
"tests/.env",
2021-12-13 16:37:03 +00:00
"-f",
"compose.yaml",
2021-12-13 16:37:03 +00:00
]
if CI:
args.extend(("-f", "tests/compose.ci.yaml"))
2021-12-13 16:37:03 +00:00
return run(*args, *cmd)
2021-12-13 17:03:40 +00:00
@log("Setup .env")
def setup_env():
shutil.copy("example.env", "tests/.env")
if CI:
with open("tests/.env", "a") as f:
f.write(
f"""
FRAPPE_VERSION={os.getenv("FRAPPE_VERSION")}
ERPNEXT_VERSION={os.getenv("ERPNEXT_VERSION")}
"""
)
@log("Print compose configuration")
def print_compose_configuration():
docker_compose("config")
2021-12-13 16:37:03 +00:00
@log("Create containers")
def create_containers():
2021-12-15 05:30:00 +00:00
docker_compose("up", "-d", "--quiet-pull")
2021-12-13 16:37:03 +00:00
@log("Check if backend services have connections")
def ping_links_in_backends():
for service in BACKEND_SERVICES:
for _ in range(10):
try:
docker_compose("exec", service, "healthcheck.sh")
break
except subprocess.CalledProcessError:
sleep(1)
else:
raise Exception(f"Connections healthcheck failed for service {service}")
@log("Create test site")
def create_site():
docker_compose(
"exec",
"backend",
"bench",
"new-site",
SITE_NAME,
"--mariadb-root-password",
"123",
"--admin-password",
"admin",
)
docker_compose("restart", "backend")
# This is needed to check https override
_ssl_ctx = ssl.create_default_context()
_ssl_ctx.check_hostname = False
_ssl_ctx.verify_mode = ssl.CERT_NONE
def ping_and_check_content(url: str, callback: Callable[[str], Optional[str]]):
request = Request(url, headers={"Host": SITE_NAME})
print(f"Checking {url}")
for _ in range(100):
try:
response = urlopen(request, context=_ssl_ctx)
except HTTPError as exc:
if exc.code not in (404, 502):
raise
2021-12-15 06:03:32 +00:00
except URLError:
pass
2021-12-13 16:37:03 +00:00
else:
text: str = response.read().decode()
ret = callback(text)
if ret:
print(ret)
return
sleep(0.1)
raise AssertionError(f"Couldn't ping {url}")
def index_callback(text: str):
if "404 page not found" not in text:
return text[:200]
@log("Check /")
def check_index():
ping_and_check_content(url="http://127.0.0.1", callback=index_callback)
@log("Check /api/method/version")
def check_api():
ping_and_check_content(
url="http://127.0.0.1/api/method/version",
callback=lambda text: text if '"message"' in text else None,
)
@log("Check if Frappe can connect to services in backends")
def ping_frappe_connections_in_backends():
for service in BACKEND_SERVICES:
docker_compose("cp", "tests/_ping_frappe_connections.py", f"{service}:/tmp/")
2021-12-13 16:37:03 +00:00
docker_compose(
"exec",
service,
"/home/frappe/frappe-bench/env/bin/python",
f"/tmp/_ping_frappe_connections.py",
2021-12-13 16:37:03 +00:00
)
@log("Check /assets")
def check_assets():
ping_and_check_content(
url="http://127.0.0.1/assets/js/frappe-web.min.js",
callback=lambda text: text[:200] if text is not None else None,
)
@log("Check /files")
def check_files():
file_name = "testfile.txt"
docker_compose(
"cp",
f"tests/{file_name}",
f"backend:/home/frappe/frappe-bench/sites/{SITE_NAME}/public/files/",
)
ping_and_check_content(
url=f"http://127.0.0.1/files/{file_name}",
callback=lambda text: text if text == "lalala\n" else None,
)
2021-12-15 05:30:00 +00:00
@log("Prepare S3 server")
def prepare_s3_server():
run(
"docker",
"run",
"--name",
"minio",
"-d",
"-e",
f"MINIO_ACCESS_KEY={MINIO_ACCESS_KEY}",
"-e",
f"MINIO_SECRET_KEY={MINIO_SECRET_KEY}",
"--network",
"test_default",
"minio/minio",
"server",
"/data",
)
docker_compose("cp", "tests/_create_bucket.py", "backend:/tmp")
docker_compose(
"exec",
"-e",
f"MINIO_ACCESS_KEY={MINIO_ACCESS_KEY}",
"-e",
f"MINIO_SECRET_KEY={MINIO_SECRET_KEY}",
"backend",
"/home/frappe/frappe-bench/env/bin/python",
"/tmp/_create_bucket.py",
)
2021-12-15 05:30:00 +00:00
@log("Push backup to S3")
def push_backup_to_s3():
docker_compose(
"exec", "backend", "bench", "--site", SITE_NAME, "backup", "--with-files"
)
docker_compose(
"exec",
"backend",
"push-backup",
"--site",
SITE_NAME,
"--bucket",
"frappe",
"--region-name",
"us-east-1",
"--endpoint-url",
"http://minio:9000",
"--aws-access-key-id",
MINIO_ACCESS_KEY,
"--aws-secret-access-key",
MINIO_SECRET_KEY,
)
@log("Check backup in S3")
def check_backup_in_s3():
docker_compose("cp", "tests/_check_backup_files.py", "backend:/tmp")
docker_compose(
"exec",
"-e",
f"MINIO_ACCESS_KEY={MINIO_ACCESS_KEY}",
"-e",
f"MINIO_SECRET_KEY={MINIO_SECRET_KEY}",
"backend",
"/home/frappe/frappe-bench/env/bin/python",
"/tmp/_check_backup_files.py",
)
2021-12-15 05:30:00 +00:00
@log("Stop S3 container")
def stop_s3_container():
run("docker", "rm", "minio", "-f")
2021-12-13 16:37:03 +00:00
@log("Recreate with https override")
def recreate_with_https_override():
docker_compose("-f", "overrides/compose.https.yaml", "up", "-d", "--quiet-pull")
2021-12-13 16:37:03 +00:00
@log("Check / (https)")
def check_index_https():
ping_and_check_content(url="https://127.0.0.1", callback=index_callback)
@log("Stop containers")
def stop_containers():
docker_compose("down", "-v", "--remove-orphans")
@log("Recreate with ERPNext override")
def create_containers_with_erpnext_override():
args = ["-f", "overrides/compose.erpnext.yaml"]
2021-12-13 16:37:03 +00:00
if CI:
args.extend(("-f", "tests/compose.ci-erpnext.yaml"))
2021-12-15 05:30:00 +00:00
docker_compose(*args, "up", "-d", "--quiet-pull")
2021-12-13 16:37:03 +00:00
@log("Create ERPNext site")
def create_erpnext_site():
docker_compose(
"exec",
"backend",
"bench",
"new-site",
SITE_NAME,
"--mariadb-root-password",
"123",
"--admin-password",
"admin",
"--install-app",
"erpnext",
)
docker_compose("restart", "backend")
@log("Check /api/method/erpnext.templates.pages.product_search.get_product_list")
def check_erpnext_api():
ping_and_check_content(
url="http://127.0.0.1/api/method/erpnext.templates.pages.product_search.get_product_list",
callback=lambda text: text if '"message"' in text else None,
)
@log("Check /assets/erpnext/js/setup_wizard.js")
def check_erpnext_assets():
ping_and_check_content(
url="http://127.0.0.1/assets/erpnext/js/setup_wizard.js",
callback=lambda text: text[:200] if text is not None else None,
)
@log("Create containers with Postgres override")
def create_containers_with_postgres_override():
args = ["-f", "overrides/compose.postgres.yaml"]
2021-12-15 05:32:17 +00:00
if CI:
2021-12-15 06:28:53 +00:00
args.extend(("-f", "tests/compose.ci-postgres.yaml"))
2021-12-15 05:32:17 +00:00
docker_compose(*args, "up", "-d", "--quiet-pull")
2021-12-13 16:37:03 +00:00
@log("Create Postgres site")
def create_postgres_site():
docker_compose(
"exec", "backend", "bench", "set-config", "-g", "root_login", "postgres"
)
docker_compose(
"exec", "backend", "bench", "set-config", "-g", "root_password", "123"
)
docker_compose(
"exec",
"backend",
"bench",
"new-site",
SITE_NAME,
"--db-type",
"postgres",
"--admin-password",
"admin",
)
docker_compose("restart", "backend")
2021-12-13 17:03:40 +00:00
@log("Delete .env")
def delete_env():
os.remove("tests/.env")
2021-12-13 16:37:03 +00:00
@log("Show docker compose logs")
def show_docker_compose_logs():
docker_compose("logs")
def main() -> int:
try:
patch_print()
2021-12-13 17:03:40 +00:00
setup_env()
print_compose_configuration()
2021-12-13 16:37:03 +00:00
create_containers()
2021-12-13 16:37:03 +00:00
ping_links_in_backends()
create_site()
check_index()
check_api()
ping_frappe_connections_in_backends()
check_assets()
check_files()
2021-12-15 05:30:00 +00:00
prepare_s3_server()
push_backup_to_s3()
check_backup_in_s3()
stop_s3_container()
2021-12-13 16:37:03 +00:00
recreate_with_https_override()
check_index_https()
stop_containers()
create_containers_with_erpnext_override()
create_erpnext_site()
check_erpnext_api()
check_erpnext_assets()
stop_containers()
create_containers_with_postgres_override()
create_postgres_site()
ping_links_in_backends()
finally:
2021-12-13 17:03:40 +00:00
delete_env()
2021-12-13 16:37:03 +00:00
show_docker_compose_logs()
stop_containers()
print(colored("Tests successfully passed!", Color.YELLOW))
return 0
if __name__ == "__main__":
raise SystemExit(main())