Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
23be4b7
first taf draft + tests
aleksei-sobolev-epam Aug 18, 2025
2d86d0d
removed an excessive file
aleksei-sobolev-epam Aug 18, 2025
ab06e3e
menu and search tests
aleksei-sobolev-epam Aug 20, 2025
673a7d3
added selection-deselection test
aleksei-sobolev-epam Aug 20, 2025
909f65f
added datasets creation/deletion tests
aleksei-sobolev-epam Aug 21, 2025
3f2b230
added files creation/deletion tests
aleksei-sobolev-epam Aug 22, 2025
22575f6
added files moving test
aleksei-sobolev-epam Aug 22, 2025
8a9a236
refactoring and jobs creation test
aleksei-sobolev-epam Aug 25, 2025
a2e7bed
small fix for jobs creation test
aleksei-sobolev-epam Aug 26, 2025
01751b2
moved some constants from tests
aleksei-sobolev-epam Aug 26, 2025
a857a40
added categories page test
aleksei-sobolev-epam Aug 26, 2025
2b88871
added users helper, removed uuid from constants
aleksei-sobolev-epam Aug 26, 2025
fce1501
Merge branch 'EPMGPTBADG-260-first-taf-draft' into EPMGPTBADG-261-cat…
aleksei-sobolev-epam Aug 27, 2025
bd6de0c
reports page test
aleksei-sobolev-epam Aug 27, 2025
3ad3e1a
added plugin tests
aleksei-sobolev-epam Aug 28, 2025
7fded78
Merge branch 'EPMGPTBADG-260-first-taf-draft' into EPMGPTBADG-261-cat…
aleksei-sobolev-epam Aug 28, 2025
9391644
added more tests for datasets and files
aleksei-sobolev-epam Aug 28, 2025
8ed84b3
Merge branch 'EPMGPTBADG-260-first-taf-draft' into EPMGPTBADG-261-cat…
aleksei-sobolev-epam Aug 28, 2025
2b1da03
added more jobs tests
aleksei-sobolev-epam Aug 29, 2025
1723cb5
added more jobs tests
aleksei-sobolev-epam Aug 29, 2025
fed15c0
added file download tests
aleksei-sobolev-epam Sep 2, 2025
4d6fd34
Merge branch 'EPMGPTBADG-260-first-taf-draft' into EPMGPTBADG-261-cat…
aleksei-sobolev-epam Sep 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions test_automation_framework/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
max-line-length = 120
ignore = E203, W503
exclude = .git,__pycache__,build,dist
5 changes: 5 additions & 0 deletions test_automation_framework/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
__pycache__/
*.pyc
.vscode/
.idea/
14 changes: 14 additions & 0 deletions test_automation_framework/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
repos:
- repo: https://github.com/psf/black
rev: 25.1.0
hooks:
- id: black
args: [--line-length=120]
language_version: python3

- repo: https://github.com/pycqa/flake8
rev: 7.3.0
hooks:
- id: flake8
args: [--config=test_automation_framework/.flake8]
additional_dependencies: []
47 changes: 47 additions & 0 deletions test_automation_framework/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# BadgerDoc Test Automation Framework

This project is a Python-based **test automation framework** built with [pytest](https://docs.pytest.org/).

## Getting Started

### 1. Install PDM
Make sure you have [PDM](https://pdm-project.org/latest/#installation) installed:

```bash
brew install pdm # macOS
# or
pip install pdm
```

Verify installation:

```bash
pdm --version
```

### 2. Clone the repository

```bash
git clone https://github.com/epam/badgerdoc.git
cd badgerdoc
```

### 3. Install dependencies

```bash
pdm install
```

### 4. Pre-commit hooks

Enable pre-commit to enforce style and linting:
```bash
pre-commit install
```
Now hooks will run automatically before each commit.

### 5. Run tests

```bash
pdm run pytest
```
7 changes: 7 additions & 0 deletions test_automation_framework/config/defaults.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
BASE_URL: "http://demo.badgerdoc.com:8080"
TIMEOUT_SECONDS: 30
MAX_WORKERS: 4
USE_MOCK_LLM: true
LOG_LEVEL: "INFO"
API_USER: "user@example.com"
API_PASS: "changeme"
150 changes: 150 additions & 0 deletions test_automation_framework/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import logging
from logging import getLogger
from typing import Tuple

import pytest

from settings import load_settings
from helpers.auth.auth_service import AuthService
from helpers.base_client.base_client import BaseClient
from helpers.datasets.dataset_client import DatasetClient
from helpers.files.file_client import FileClient
from helpers.jobs.jobs_client import JobsClient
from helpers.menu.menu_client import MenuClient
from helpers.category.categories import CategoriesClient
from helpers.users.users import UsersClient
from helpers.reports.reports_client import ReportsClient
from helpers.plugins.plugins_client import PluginsClient

logger = getLogger(__name__)


def pytest_configure():
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s")


@pytest.fixture(scope="session")
def settings():
return load_settings()


@pytest.fixture(scope="session")
def tenant(settings) -> str:
return getattr(settings, "TENANT", "demo-badgerdoc")


@pytest.fixture(scope="session")
def base_client(settings) -> BaseClient:
client = BaseClient(settings.BASE_URL, timeout=10)
yield client
client.close()


@pytest.fixture(scope="session")
def auth_service(base_client) -> AuthService:
return AuthService(base_client)


@pytest.fixture(scope="session")
def auth_token(auth_service, settings) -> Tuple[str, str]:
return auth_service.get_token(settings.API_USER, settings.API_PASS.get_secret_value())


@pytest.fixture
def access_token(auth_token) -> str:
return auth_token[0]


@pytest.fixture
def menu_client(settings, access_token, tenant) -> MenuClient:
return MenuClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def dataset_client(settings, access_token, tenant) -> DatasetClient:
return DatasetClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def file_client(settings, access_token, tenant) -> FileClient:
return FileClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def jobs_client(settings, access_token, tenant) -> JobsClient:
return JobsClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def reports_client(settings, access_token, tenant) -> ReportsClient:
return ReportsClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def plugins_client(settings, access_token, tenant) -> PluginsClient:
return PluginsClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def user_uuid(settings, access_token, tenant) -> str:
users_client = UsersClient(settings.BASE_URL, access_token, tenant)
users = users_client.search_users()
return next((u.id for u in users if u.username == "admin"), None)


@pytest.fixture
def categories_client(settings, access_token, tenant) -> CategoriesClient:
return CategoriesClient(settings.BASE_URL, access_token, tenant)


@pytest.fixture
def dataset_tracker(dataset_client):
created: list[str] = []
yield created, dataset_client
for name in created:
try:
resp = dataset_client.delete_dataset(name=name)
logger.info(f"[dataset_tracker] Deleted dataset {name}: {resp.get('detail')}")
except Exception as e:
logger.warning(f"[dataset_tracker] Failed to delete dataset {name}: {e}")


@pytest.fixture
def file_tracker(file_client):
created_files: list[dict] = []
yield created_files, file_client
if created_files:
ids = [f["id"] for f in created_files if f.get("id") is not None]
if ids:
try:
result = file_client.delete_files(ids)
logger.info(f"[file_tracker] Deleted files: {ids}, response={result}")
except Exception as e:
logger.warning(f"[file_tracker] Failed to cleanup files {ids}: {e}")


@pytest.fixture
def job_tracker(jobs_client):
created: list[dict] = []
yield created, jobs_client
for job in created:
job_id = job.get("id") or job.get("job_id") or (job.get("job") or {}).get("id")
if not job_id:
continue
try:
jobs_client.post("/jobs/jobs/cancel", json={"id": job_id}, headers=jobs_client._default_headers())
logger.info(f"[job_tracker] Cancelled job {job_id}")
except Exception as e:
logger.warning(f"[job_tracker] Could not cancel job {job_id}: {e}")


@pytest.fixture
def plugins_tracker(plugins_client):
created: list[int] = []
yield created, plugins_client
for id in created:
try:
plugins_client.delete_plugin(plugin_id=id)
logger.info(f"[plugins_tracker] Deleted plugin {id}")
except Exception as e:
logger.warning(f"[plugins_tracker] Failed to delete plugin {id}: {e}")
46 changes: 46 additions & 0 deletions test_automation_framework/helpers/auth/auth_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from __future__ import annotations
from typing import Optional
from pydantic import BaseModel

from helpers.base_client.base_client import BaseClient


class TokenResponse(BaseModel):
access_token: str
refresh_token: str
id_token: Optional[str] = None
scope: Optional[str] = None
session_state: Optional[str] = None
token_type: Optional[str] = None
expires_in: Optional[int] = None


class AuthService:
def __init__(self, client: BaseClient) -> None:
self.client = client

def get_token(self, username: str, password: str, client_id: str = "admin-cli") -> tuple[str, str]:
resp = self.client.post_json(
"/users/token",
data={
"grant_type": "password",
"username": username,
"password": password,
"client_id": client_id,
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
result = TokenResponse.model_validate(resp)
return result.access_token, result.refresh_token

def refresh_token(self, refresh_token: str, client_id: str = "admin-cli") -> tuple[str, str]:
resp = self.client.post_json(
"/users/refresh_token",
json={
"grant_type": "refresh_token",
"client_id": client_id,
"refresh_token": refresh_token,
},
)
result = TokenResponse.model_validate(resp)
return result.access_token, result.refresh_token
Loading