diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD index eebb8b6..305a898 100644 --- a/CONTRIBUTING.MD +++ b/CONTRIBUTING.MD @@ -70,7 +70,7 @@ Application tests are in `{{cookiecutter.__src_folder_name}}/tests/`. Application tests use [playwright](https://playwright.dev/python/) and [pytest-playwright](https://github.com/microsoft/playwright-pytest). If you're testing the generated projects, you'll need to have `playwright` installed with dependencies. ```sh -playwright install --with-deps +playwright install chromium --with-deps ``` ### Running Tests diff --git a/tools/update_info.py b/tools/update_info.py index cbeeb50..f956238 100644 --- a/tools/update_info.py +++ b/tools/update_info.py @@ -42,7 +42,7 @@ def get_azure_combinations() -> Generator[tuple[str, str], None, None]: """ Returns the base_keys and base_values for the combinations - + Yields: tuple[str, str]: The base_keys and base_values for the combinations Example: ("azure-flask-postgresql-azure-app-service", "Flask PostgreSQL Azure App Service") @@ -118,7 +118,7 @@ def create_base_folder(): base_path.mkdir(parents=True, exist_ok=True) return base_path # TODO: Get repos by pattern - + def get_repos_by_pattern(pattern:str, repos: list[str]=list(get_azure_combinations())) -> list[str]: """ @@ -128,7 +128,7 @@ def get_repos_by_pattern(pattern:str, repos: list[str]=list(get_azure_combinatio pattern = re.compile(rf".*{pattern}.*") matching_repos = [repo[0] for repo in repos if pattern.match(repo[0])] return matching_repos - + def update_repo( repo:str, path: pathlib.Path, @@ -138,14 +138,14 @@ def update_repo( **kwargs) -> None: """ Updates the repo with the provided name - + Parameters: repo (str): The name of the repo to update. It should be the same as seen on GitHub. path (pathlib.Path): The parent folder where the will be saved. branch (str): The name of the branch to create and push to. Defaults to cruft/update. - **kwargs: Additional keyword arguments to pass to cruft.update as + **kwargs: Additional keyword arguments to pass to cruft.update as """ # console = console.Console() per_file_formatter = logging.Formatter(f'%(asctime)s - {repo} - %(levelname)s - %(message)s') @@ -186,7 +186,8 @@ def update_repo( return if rejection_files:=list(pathlib.Path(path).rglob("*.rej")): - logger.error(f"Rejection files found for {path}!\nFiles: {'\n- '.join([str(x) for x in rejection_files])}") + files_str = '\n- '.join([str(x) for x in rejection_files]) + logger.error(f"Rejection files found for {path}!\nFiles: {files_str}") return None logger.info(f"adding Changes and Creating a PR for {path}") @@ -238,7 +239,8 @@ def update_repos( logger.info(f"Request updates to repos matching \"{pattern}\" requested. Attrs: \n\t{branch=}\n\t{checkout=}") path = create_base_folder() patterns = get_repos_by_pattern(pattern) - logger.info(f"Found {len(patterns)} repos matching \"{pattern}\"\n{'\n'.join(patterns)}") + patterns_str = '\n- '.join(patterns) + logger.info(f"Found {len(patterns)} repos matching \"{pattern}\"\n{patterns_str}") for repo in patterns: update_repo(repo=repo, path=path, branch=branch) diff --git a/{{cookiecutter.__src_folder_name}}/.devcontainer/devcontainer.json b/{{cookiecutter.__src_folder_name}}/.devcontainer/devcontainer.json index a4a0149..1f7d3bc 100644 --- a/{{cookiecutter.__src_folder_name}}/.devcontainer/devcontainer.json +++ b/{{cookiecutter.__src_folder_name}}/.devcontainer/devcontainer.json @@ -36,7 +36,8 @@ "files.exclude": { ".coverage": true, ".pytest_cache": true, - "__pycache__": true + "__pycache__": true, + ".ruff_cache": true }, "[python]": { "editor.formatOnSave": true, diff --git a/{{cookiecutter.__src_folder_name}}/.github/workflows/azure-dev.yml b/{{cookiecutter.__src_folder_name}}/.github/workflows/azure-dev.yml index 3013f96..f825973 100644 --- a/{{cookiecutter.__src_folder_name}}/.github/workflows/azure-dev.yml +++ b/{{cookiecutter.__src_folder_name}}/.github/workflows/azure-dev.yml @@ -103,5 +103,5 @@ jobs: run: | python3 -m pip install --upgrade pip python3 -m pip install -r requirements-dev.txt - python3 -m playwright install --with-deps + python3 -m playwright install chromium --with-deps python3 -m pytest --exitfirst src/tests/smoke/smoketests.py --live-server-url $URI diff --git a/{{cookiecutter.__src_folder_name}}/.github/workflows/tests.yml b/{{cookiecutter.__src_folder_name}}/.github/workflows/tests.yml index 181d056..9b32140 100644 --- a/{{cookiecutter.__src_folder_name}}/.github/workflows/tests.yml +++ b/{{cookiecutter.__src_folder_name}}/.github/workflows/tests.yml @@ -56,7 +56,7 @@ jobs: run: | python3 -m pip install --upgrade pip python3 -m pip install -r requirements-dev.txt - playwright install --with-deps + playwright install chromium --with-deps {% if cookiecutter.project_backend in ("flask", "fastapi") %} python3 -m pip install -e src {% endif %} diff --git a/{{cookiecutter.__src_folder_name}}/.vscode/launch.json b/{{cookiecutter.__src_folder_name}}/.vscode/launch.json index 5ff4403..d80860b 100644 --- a/{{cookiecutter.__src_folder_name}}/.vscode/launch.json +++ b/{{cookiecutter.__src_folder_name}}/.vscode/launch.json @@ -15,7 +15,7 @@ ], "django": true, "justMyCode": true - } + }, {% endif %} {% if cookiecutter.project_backend == "flask" %} { @@ -35,7 +35,7 @@ ], "jinja": true, "justMyCode": false - } + }, {% endif %} {% if cookiecutter.project_backend == "fastapi" %} { @@ -47,7 +47,16 @@ "fastapi_app:app", "--reload" ], - } + }, {% endif %} + { + "name": "Python: Debug Tests", + "type": "python", + "request": "launch", + "program": "${file}", + "purpose": ["debug-test"], + "console": "integratedTerminal", + "env": {"PYTEST_ADDOPTS": "--no-cov"} + } ] } diff --git a/{{cookiecutter.__src_folder_name}}/README.md b/{{cookiecutter.__src_folder_name}}/README.md index 3f4e0f1..fa8052d 100644 --- a/{{cookiecutter.__src_folder_name}}/README.md +++ b/{{cookiecutter.__src_folder_name}}/README.md @@ -67,7 +67,7 @@ If you're running the app inside VS Code or GitHub Codespaces, you can use the " ```sh {% if cookiecutter.project_backend == "flask" %} -python3 -m flask --app src.flaskapp run --reload --port={{cookiecutter.web_port}} +python3 -m flask --app src.flaskapp run --debug --reload --port={{cookiecutter.web_port}} {% endif %} {% if cookiecutter.project_backend == "fastapi" %} python3 -m uvicorn fastapi_app:app --reload --port={{cookiecutter.web_port}} @@ -99,7 +99,7 @@ python3 src/manage.py createsuperuser ```sh python3 -m pip install -r requirements-dev.txt - python3 -m playwright install --with-deps + python3 -m playwright install chromium --with-deps ``` 3. Run the tests: diff --git a/{{cookiecutter.__src_folder_name}}/pyproject.toml b/{{cookiecutter.__src_folder_name}}/pyproject.toml index a385def..de441ed 100644 --- a/{{cookiecutter.__src_folder_name}}/pyproject.toml +++ b/{{cookiecutter.__src_folder_name}}/pyproject.toml @@ -18,3 +18,6 @@ addopts = "-ra -vv" DJANGO_SETTINGS_MODULE = "project.settings" pythonpath = ["src"] {% endif %} + +[tool.coverage.report] +show_missing = true diff --git a/{{cookiecutter.__src_folder_name}}/requirements-dev.txt b/{{cookiecutter.__src_folder_name}}/requirements-dev.txt index 8258896..17a2939 100644 --- a/{{cookiecutter.__src_folder_name}}/requirements-dev.txt +++ b/{{cookiecutter.__src_folder_name}}/requirements-dev.txt @@ -13,6 +13,8 @@ pytest-django ephemeral-port-reserve {% endif %} pytest-playwright +coverage +pytest-cov # Linters ruff diff --git a/{{cookiecutter.__src_folder_name}}/src/entrypoint.sh b/{{cookiecutter.__src_folder_name}}/src/entrypoint.sh index 8574a8b..2790d65 100755 --- a/{{cookiecutter.__src_folder_name}}/src/entrypoint.sh +++ b/{{cookiecutter.__src_folder_name}}/src/entrypoint.sh @@ -20,6 +20,5 @@ echo "${0}: running migrations." python3 manage.py migrate python3 manage.py loaddata seed_data.json python3 manage.py collectstatic --no-input -python3 -m gunicorn project.wsgi:application \ - --name relecloud +python3 -m gunicorn project.wsgi:application {% endif %} diff --git a/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/pages.py b/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/pages.py index d582b3c..beeae54 100644 --- a/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/pages.py +++ b/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/pages.py @@ -45,13 +45,13 @@ def cruise_detail(pk: int): ) -@bp.get("/info_request/") +@bp.get("/info_request") def info_request(): {{ get_all("Cruise") }} return render_template("info_request_create.html", cruises=all_cruises, message=request.args.get("message")) -@bp.post("/info_request/") +@bp.post("/info_request") def create_info_request(): name = request.form["name"] db_info_request = models.InfoRequest( diff --git a/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/tests/local/test_pages.py b/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/tests/local/test_pages.py new file mode 100644 index 0000000..6388a4c --- /dev/null +++ b/{{cookiecutter.__src_folder_name}}/src/flask/flaskapp/tests/local/test_pages.py @@ -0,0 +1,76 @@ +import pytest + +from flaskapp import db, models + + +@pytest.fixture +def client(app_with_db): + return app_with_db.test_client() + + +def test_index(client): + response = client.get("/") + + assert response.status_code == 200 + assert b"Welcome to ReleCloud" in response.data + + +def test_about(client): + response = client.get("/about") + + assert response.status_code == 200 + assert b"About ReleCloud" in response.data + + +def test_destinations(client): + response = client.get("/destinations") + + assert response.status_code == 200 + assert b"Destinations" in response.data + assert b"The Sun" in response.data + + +def test_destination_detail(client): + response = client.get("/destination/1") + + assert response.status_code == 200 + assert b"The Sun" in response.data + + +def test_cruise_detail(client): + response = client.get("/cruise/1") + + assert response.status_code == 200 + assert b"The Sun and Earth" in response.data + + +def test_info_request(client): + response = client.get("/info_request") + + assert response.status_code == 200 + assert b"Request Info" in response.data + + +def test_create_info_request(app_with_db, client): + response = client.post( + "/info_request", + data={ + "name": "Amanda Valdez", + "email": "michellewatson@gmail.com", + "notes": "Please send me more information.", + "cruise_id": "12345", + }, + ) + + assert response.status_code == 302 + assert ( + response.headers["Location"] + == "/info_request?message=Thank+you,+Amanda+Valdez!+We+will+email+you+when+we+have+more+information!" + ) + + with app_with_db.app_context(): + info_request = db.session.query(models.InfoRequest).order_by(models.InfoRequest.id.desc()).first() + assert info_request.name == "Amanda Valdez" + assert info_request.email == "michellewatson@gmail.com" + assert info_request.notes == "Please send me more information." + assert info_request.cruise_id == 12345 diff --git a/{{cookiecutter.__src_folder_name}}/src/tests/local/test_gunicorn.py b/{{cookiecutter.__src_folder_name}}/src/tests/local/test_gunicorn.py new file mode 100644 index 0000000..d3734d2 --- /dev/null +++ b/{{cookiecutter.__src_folder_name}}/src/tests/local/test_gunicorn.py @@ -0,0 +1,16 @@ +import sys +from unittest import mock + +import pytest +from gunicorn.app.wsgiapp import run + + +def test_config_imports(): + {% set app_arg = {"flask": "flaskapp:create_app()", "fastapi": "fastapi_app.app:app", "django": "project.wsgi:application"}[cookiecutter.project_backend] %} + argv = ["gunicorn", "--check-config", {{ app_arg }}, "-c", "src/gunicorn.conf.py"] + + with mock.patch.object(sys, "argv", argv): + with pytest.raises(SystemExit) as excinfo: + run() + + assert excinfo.value.args[0] == 0