Skip to content

Commit

Permalink
feat: add main unit tests for compose helper util (#12)
Browse files Browse the repository at this point in the history
* feat: add main unit tests for compose helper util

* setup ghas

* update pytest run command

* add verbose outputs to pytest commands

* feat: add in suggestions from review

* feat: add tests for _write_compose_file
  • Loading branch information
tushar5526 authored Jan 15, 2024
1 parent 245aefd commit 57d6910
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 11 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Run pytest on PR

on:
pull_request:
push:

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.11 # Choose your Python version

- name: Cache Python dependencies
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Install main dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Install development dependencies
run: pip install -r requirements-dev.txt

- name: Run pytest
run: python -m pytest -vvv
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ venv.bak/

deployments/*
nginx-confs/*
*.txt
*keys.txt
2 changes: 2 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest==7.4.4
pytest-mock==3.12.0
16 changes: 6 additions & 10 deletions server/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ class DeploymentConfig:
compose_file_location: str = "docker-compose.yml"
rest_action: str = "POST"

def __post_init__(self):
# Check if all members are specified
missing_members = [
field.name for field in fields(self) if not hasattr(self, field.name)
]
if missing_members:
raise ValueError(f"Missing members: {', '.join(missing_members)}")

def get_project_hash(self):
return get_random_stub(f"{self.project_name}:{self.branch_name}")

Expand All @@ -53,8 +45,9 @@ class ComposeHelper:

def __init__(self, compose_file_location: str, load_compose_file=True):
self._compose_file_location = compose_file_location
if load_compose_file:
self._compose = load_yaml_file(self._compose_file_location)
self._compose = (
load_yaml_file(self._compose_file_location) if load_compose_file else None
)

def start_services(
self, nginx_port: str, conf_file_path: str, deployment_namespace: str
Expand Down Expand Up @@ -110,6 +103,9 @@ def _generate_processed_compose_file(
"services"
]["nginx"]

self._write_compose_file()

def _write_compose_file(self):
with open(self._compose_file_location, "w") as yaml_file:
# Dump the data to the YAML file
yaml.dump(self._compose, yaml_file, default_flow_style=False)
Expand Down
71 changes: 71 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import pytest

from server.utils import ComposeHelper


@pytest.fixture
def compose_helper(mocker):
test_compose_file = """
# Test Docker compose to be used in tests
version: '3'
services:
webapp:
image: nginx:alpine
ports:
- "8080:80"
database:
image: postgres:latest
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpassword
redis:
image: redis:alpine
ports:
- "6379:6379"
messaging:
image: rabbitmq:management
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
api:
image: node:14
working_dir: /app
volumes:
- ./api:/app
command: npm start
ports:
- "3000:3000"
environment:
NODE_ENV: development
python_app:
image: python:3.8
volumes:
- ./python_app:/app
command: python app.py
environment:
PYTHON_ENV: testing
mongo_db:
image: mongo:latest
ports:
- "27017:27017"
environment:
MONGO_INITDB_DATABASE: testdb
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: rootpassword
"""
mocker.patch("builtins.open", mocker.mock_open(read_data=test_compose_file))
compose_helper = ComposeHelper("test-docker-compose.yml")
return compose_helper
107 changes: 107 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import pathlib

from server.utils import ComposeHelper, DeploymentConfig


def test_verify_project_hash_format():
# Given
config = DeploymentConfig(
project_name="test-project-name",
branch_name="test-branch-name",
project_git_url="https://github.com/tushar5526/test-project-name.git",
)
# When
project_hash = config.get_project_hash()

# Then
assert type(project_hash) == str
assert len(project_hash) == 16


def test_dont_load_compose_file_in_compose_helper():
compose_helper = ComposeHelper("random-compose.yml", load_compose_file=False)
assert getattr(compose_helper, "_compose") is None


def test_start_services(compose_helper, mocker):
# Given
mocked_run = mocker.patch("subprocess.run")

conf_file_path = "conf-file-path/some-nginx.conf"
deployment_namespace = "deployment-namespace"

# When
compose_helper.start_services(5000, conf_file_path, deployment_namespace)

# Then
# Processed compose file should be updated with following rules
# - No ports
# - No container_name
# - Restart should be present in each service
services = compose_helper._compose["services"]

# Deployment Proxy should be added in compose file
is_deployment_proxy_service = False

for service_name, service_config in services.items():
is_deployment_proxy_service = (
is_deployment_proxy_service
or service_name == f"nginx_{deployment_namespace}"
)

assert (
"ports" not in service_config or is_deployment_proxy_service
), f"Ports mapping should not be present in {service_name}"

assert (
"container_name" not in service_config
), f"Container Name should not be present in {service_name}"

assert "restart" in service_config, f"Restart clause missing in {service_name}"

assert (
service_config["restart"] == "always"
), f"Incorrect restart policy in {service_name}"

assert (
is_deployment_proxy_service
), "Deployment (Nginx) Proxy is missing in processed services"

mocked_run.assert_called_once_with(
["docker-compose", "up", "-d", "--build"], check=True, cwd=pathlib.Path(".")
)


def test_remove_services(compose_helper, mocker):
mocked_run = mocker.patch("subprocess.run")
compose_helper.remove_services()
mocked_run.assert_called_once_with(
["docker-compose", "down", "-v"], check=True, cwd=pathlib.Path(".")
)


def test_get_service_ports_config(compose_helper):
service_config = {
"webapp": [("8080", "80")],
"database": [],
"redis": [("6379", "6379")],
"messaging": [("5672", "5672"), ("15672", "15672")],
"api": [("3000", "3000")],
"python_app": [],
"mongo_db": [("27017", "27017")],
}
assert compose_helper.get_service_ports_config() == service_config


def test_write_compose_file(compose_helper, mocker):
# Given
conf_file_path = "conf-file-path/some-nginx.conf"
deployment_namespace = "deployment-namespace"
mocker.patch("subprocess.run")
mock_yaml_dump = mocker.patch("server.utils.yaml.dump")

# When
compose_helper.start_services(5000, conf_file_path, deployment_namespace)

# Then
assert mock_yaml_dump.called_once()

0 comments on commit 57d6910

Please sign in to comment.