Skip to content

Commit

Permalink
dockerized backend, tests and lint checks
Browse files Browse the repository at this point in the history
  • Loading branch information
FedirAlifirenko committed Oct 19, 2024
1 parent a0da475 commit d722fac
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 33 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Application that allows authenticated users to upload files of any size (hardware resources permitting)

## BE
- Implement an API endpoint that allows users to upload files of any size (hardware resources permitting).
- Efficiently handle large file uploads, make sure to prevent memory issues.
- Implement a way to authenticate users before allowing them to upload files.
(For simplicity, you can hard-code a set of valid usernames and passwords.)
- Dockerize the application.
- Write test cases covering critical backend functionalities. Focus on testing file upload endpoint.

## FE
TODO


## Notes
- Used a single docker image for both api and test containers
22 changes: 22 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Exclude Python bytecode files
*.pyc

# Exclude system-specific files
__pycache__

# Exclude development files
.DS_Store
.idea/
*.log
*.bak

# Exclude version control files
.git
.gitignore

.venv
venv

# Exclude pytest cache directories
__pycache__/
.pytest_cache/
33 changes: 33 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM python:3.12-slim

ENV PYTHONFAULTHANDLER=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100

RUN groupadd user && useradd --create-home --home-dir /home/user -g user user

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /home/user/app/
ENV PYTHONPATH=/home/user/app/

COPY pyproject.toml poetry.lock /home/user/app/
RUN pip install 'uvicorn[standard]'
RUN pip install --no-cache-dir poetry

RUN poetry config virtualenvs.create false
RUN poetry install --with dev --no-root --no-interaction --no-ansi


RUN mkdir -p /home/user/app/uploaded_files/ && chown -R user:user /home/user/app/uploaded_files/

USER user

COPY --chown=user:user . /home/user/app/

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
16 changes: 13 additions & 3 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,22 @@ poetry install --with dev
```

### Testing
#### Run lint checks
#### Locally
Run lint checks
```commandline
poetry run task lint
```

#### Run tests locally
Run tests
```commandline
poetry run task test
```

#### Docker
Run tests in docker using poetry task
```commandline
poetry run task compose-test
```
Or using plain `docker-compose`
```commandline
docker compose run --rm test
```
10 changes: 4 additions & 6 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from fastapi import FastAPI, Response

app = FastAPI()
from fastapi import FastAPI

from app.routers import router

@app.get("/")
def index() -> Response:
return Response(content='{"message": "OK"}', media_type="application/json")
app = FastAPI()
app.include_router(router)
11 changes: 11 additions & 0 deletions backend/app/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import APIRouter

from app.routers.health import router as health_router
from app.routers.index import router as index_router

__all__ = ["router"]

router = APIRouter(prefix="/api")

router.include_router(health_router)
router.include_router(index_router)
10 changes: 10 additions & 0 deletions backend/app/routers/health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from http import HTTPStatus

from fastapi import APIRouter, Response

router = APIRouter(prefix="/health")


@router.get("", name="health", description="Health check")
async def health() -> Response:
return Response(status_code=HTTPStatus.OK, content="OK")
8 changes: 8 additions & 0 deletions backend/app/routers/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from fastapi import APIRouter, Response

router = APIRouter()


@router.get("/", name="index", description="Index page")
def index() -> Response:
return Response(content="index page")
53 changes: 52 additions & 1 deletion backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.115.2"
uvloop = "^0.21.0"


[tool.poetry.group.dev.dependencies]
Expand All @@ -24,6 +25,12 @@ lint = "task lint_black && task lint_mypy"
lint_black = "black --check app tests"
lint_mypy = "mypy app tests"
test = "pytest"
format = "black app tests"
ci = "task lint && task test"

compose-clean = "docker compose down --volumes --remove-orphans"
compose-up = "docker compose up"
compose-test = "docker compose run --rm test"


[tool.mypy]
Expand Down
Empty file removed backend/tests/__init__.py
Empty file.
23 changes: 0 additions & 23 deletions backend/tests/test_api.py

This file was deleted.

21 changes: 21 additions & 0 deletions backend/tests/test_api/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pytest
from fastapi import FastAPI, APIRouter
from starlette.testclient import TestClient

from app.main import app as api_app
from app.routers import router as api_router


@pytest.fixture
def app() -> FastAPI:
return api_app


@pytest.fixture
def router() -> APIRouter:
return api_router


@pytest.fixture
def client(app: FastAPI) -> TestClient:
return TestClient(app)
11 changes: 11 additions & 0 deletions backend/tests/test_api/test_health.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import APIRouter
from starlette.testclient import TestClient
from http import HTTPStatus


def test_health(client: TestClient, router: APIRouter) -> None:
url = router.url_path_for("health")
response = client.get(url)

assert response.status_code == HTTPStatus.OK
assert response.text == "OK"
11 changes: 11 additions & 0 deletions backend/tests/test_api/test_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import APIRouter
from starlette.testclient import TestClient
from http import HTTPStatus


def test_index(client: TestClient, router: APIRouter) -> None:
url = router.url_path_for("index")
response = client.get(url)

assert response.status_code == HTTPStatus.OK
assert response.text == "index page"
20 changes: 20 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

services:
api: &api
container_name: virtuo-api
build:
context: ./backend
ports:
- "8000:8000"
volumes:
- uploaded-files-data:/home/user/app/uploaded_files

test:
<<: *api
container_name: virtuo-api-test
profiles: ["test"]
command: poetry run task ci


volumes:
uploaded-files-data:

0 comments on commit d722fac

Please sign in to comment.