add api testing (#14)

* add fixture to make api calls with authentification

* add authentik test that checks the status of all blueprints

* add option to append any kind of data to html report

Reviewed-on: local-it-infrastructure/e2e_tests#14
Co-authored-by: Daniel <d.brummerloh@gmail.com>
Co-committed-by: Daniel <d.brummerloh@gmail.com>
This commit is contained in:
Daniel 2023-12-09 12:34:25 +01:00 committed by dan
parent d1ff1183a5
commit 873bf73ae8
8 changed files with 89 additions and 19 deletions

View file

@ -1,3 +1,10 @@
# RUN
Abratest has 3 required inputs, but most importantly the test configuration is done through the .env files given with the --env_paths argument. So let's say we want to run abratest with these 3 .env files: Abratest has 3 required inputs, but most importantly the test configuration is done through the .env files given with the --env_paths argument. So let's say we want to run abratest with these 3 .env files:
@ -60,11 +67,14 @@ Furthermore, some `Runner` classes can depend on others. For example, `RunnerWor
| 9. | Wordpress-2 | cleanups | | 9. | Wordpress-2 | cleanups |
# Create a custom Runner
To comprehend this process, let's examine a simplified rendition of the `RunnerWordpress` class. Within it, there exist two setup scripts and two test scripts, one of which operates conditionally. To comprehend the process of creating a new subclass of `Runner`, let's examine a simplified rendition of the `RunnerWordpress` class. Within it, there exist two setup scripts and two test scripts, one of which operates conditionally.
```python ```python
from pytest_abra import Runner, Test
class RunnerWordpress(Runner): class RunnerWordpress(Runner):
env_type = "wordpress" env_type = "wordpress"
dependencies = ["authentik"] dependencies = ["authentik"]
@ -78,3 +88,17 @@ class RunnerWordpress(Runner):
] ]
cleanups = [] cleanups = []
``` ```
The signature of condition functions can be seen below. The function takes one `NamedTuple` and returns of type `bool`. You can learn about the contents of the input by looking up the class `ConditionArgs`. Generally speaking, it provides access to all of the .env files, especially the one related to the current Runner.
```python
def condition_function(args: ConditionArgs) -> bool:
...
```
# Create custom Tests
The test files are written in the same way as any other pytest test file. The only difference is that pytest-abra provides custom fixtures that make it easy to get the configuration by the provided .env files and to deal with URLS etc.
# todo: add example

View file

@ -23,6 +23,7 @@ dependencies = [
"imbox == 0.9.8", "imbox == 0.9.8",
"hatchling == 1.18.0", "hatchling == 1.18.0",
"icecream", "icecream",
"tabulate",
] ]
[project.entry-points.pytest11] [project.entry-points.pytest11]
@ -49,6 +50,6 @@ line-length = 120
target-version = "py311" target-version = "py311"
[tool.pytest.ini_options] [tool.pytest.ini_options]
python_functions = "test_* setup_*" python_functions = "setup_* test_* cleanup_*"
norecursedirs = ".* previous-work recipes" norecursedirs = ".* previous-work recipes"
testpaths = "tests" testpaths = "tests"

View file

@ -1,5 +1,6 @@
from pytest_abra.coordinator import Coordinator from pytest_abra.coordinator import Coordinator
from pytest_abra.dir_manager import DirManager from pytest_abra.dir_manager import DirManager
from pytest_abra.env_manager import EnvFile
from pytest_abra.runner import ConditionArgs, Runner, Test from pytest_abra.runner import ConditionArgs, Runner, Test
from pytest_abra.utils import BaseUrl from pytest_abra.utils import BaseUrl
@ -10,4 +11,5 @@ __all__ = [
"Test", "Test",
"DirManager", "DirManager",
"BaseUrl", "BaseUrl",
"EnvFile",
] ]

View file

@ -5,18 +5,16 @@ import os
import re import re
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pathlib import Path from pathlib import Path
from typing import Protocol, TypedDict from typing import Generator, Protocol, TypedDict
import pytest import pytest
from dotenv import dotenv_values from dotenv import dotenv_values
from icecream import ic from icecream import ic # type: ignore
from imbox import Imbox # type: ignore from imbox import Imbox # type: ignore
from playwright.sync_api import BrowserContext, expect from playwright.sync_api import APIRequestContext, BrowserContext, Playwright, expect
from pytest import Parser from pytest import Parser
from pytest_abra.dir_manager import DirManager from pytest_abra import BaseUrl, DirManager, EnvFile
from pytest_abra.env_manager import EnvFile
from pytest_abra.utils import BaseUrl
def pytest_addoption(parser: Parser): def pytest_addoption(parser: Parser):
@ -152,3 +150,14 @@ def imap_recent_messages(imap_client: Imbox) -> list[Message]:
messages.append(message) messages.append(message)
return messages return messages
@pytest.fixture(scope="session")
def api_request_context(
playwright: Playwright,
DIR: DirManager,
) -> Generator[APIRequestContext, None, None]:
state_file = DIR.STATES / "authentik_admin_state.json"
request_context = playwright.request.new_context(storage_state=state_file)
yield request_context
request_context.dispose()

View file

@ -1,3 +1,4 @@
import os
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Callable, NamedTuple from typing import TYPE_CHECKING, Callable, NamedTuple
@ -134,7 +135,6 @@ class Runner:
# command_arguments.append("--traceconfig") # command_arguments.append("--traceconfig")
command_arguments.append("-v") command_arguments.append("-v")
# command_arguments.append("-rx")
command_arguments.append(str(full_test_path)) command_arguments.append(str(full_test_path))
command_arguments.append("--runner_index") command_arguments.append("--runner_index")
@ -158,12 +158,13 @@ class Runner:
command_arguments.append(str(self.DIR.RECORDS / "traces" / full_test_path.stem)) command_arguments.append(str(self.DIR.RECORDS / "traces" / full_test_path.stem))
# tracing # tracing
command_arguments.append("--tracing") command_arguments.append("--tracing") # "on", "off", "retain-on-failure"
command_arguments.append("retain-on-failure") command_arguments.append("retain-on-failure")
# command_arguments.append("on")
# Disable capturing. With -s set, prints will go to console as if pytest is not there. # Disable capturing. With -s set, prints will go to console as if pytest is not there.
# command_arguments.append("-s") if os.environ.get("PWDEBUG") == "1":
command_arguments.append("-s")
command_arguments.append("-s")
# headed # headed
# command_arguments.append("--headed") # command_arguments.append("--headed")

View file

@ -4,4 +4,4 @@ from pytest_abra import Runner, Test
class RunnerAuthentik(Runner): class RunnerAuthentik(Runner):
env_type = "authentik" env_type = "authentik"
setups = [Test(test_file="setup_authentik.py")] setups = [Test(test_file="setup_authentik.py")]
# tests = [Test(test_file="test_authentik_dummy.py")] tests = [Test(test_file="test_authentik_blueprint_api.py")]

View file

@ -0,0 +1,39 @@
# api testing
# https://playwright.dev/python/docs/api-testing
import pytest_html # type: ignore
from icecream import ic # type: ignore
from playwright.sync_api import APIRequestContext
from tabulate import tabulate # type: ignore
from pytest_abra import BaseUrl
def test_authentik_blueprint_status(
api_request_context: APIRequestContext,
URL: BaseUrl,
extras,
) -> None:
blueprints = api_request_context.get(URL.get("api/v3/managed/blueprints"))
assert blueprints.ok
blueprints_data = blueprints.json()
ic(blueprints_data)
# fake failed blueprint
# blueprints_data["results"][10]["status"] = "failed"
table_data_all = []
table_data_failed = []
for item in blueprints_data["results"]:
row = [item["name"], item["enabled"], item["status"]]
table_data_all.append(row)
if item["status"] != "successful":
table_data_failed.append(row)
table = tabulate(table_data_all, headers=["name", "enabled", "status"])
extras.append(pytest_html.extras.text(table, name="Authentik Blueprint Status"))
# with pytest -v (verbose) the failed blueprints will be visible in the traceback
assert (
table_data_failed == []
), "One or more blueprints were not successful. See Authentik Blueprint Status in html report"

View file

@ -1,6 +0,0 @@
def test_true():
assert 1 + 1 == 2
def test_not_true():
assert 1 + 1 == 3