Skip to content

Commit 4340f32

Browse files
committed
Initial commit
1 parent 6cefd21 commit 4340f32

File tree

12 files changed

+810
-0
lines changed

12 files changed

+810
-0
lines changed

.github/workflows/test.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Run Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
test:
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- name: Checkout code
11+
uses: actions/checkout@v2
12+
13+
- name: Set up Python
14+
uses: actions/setup-python@v2
15+
with:
16+
python-version-file: ".python-version"
17+
18+
- name: Install uv
19+
uses: astral-sh/setup-uv@v5
20+
with:
21+
version: "0.6.5"
22+
enable-cache: true
23+
cache-dependency-glob: "uv.lock"
24+
25+
- name: Minimize uv cache
26+
run: uv cache prune --ci
27+
28+
- name: Sync dependencies
29+
run: uv sync --frozen
30+
31+
- name: Run tests
32+
run: uv run pytest tests

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__pycache__
2+
.env

.vscode/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"flake8.args": [
3+
"--ignore=E501"
4+
]
5+
}

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# example-backend
2+
3+
## Requirements
4+
5+
- [uv](https://github.com/astral-sh/uv) 0.6.4+
6+
7+
## Installation
8+
9+
```bash
10+
git clone https://github.com/kumarstack55/example-backend.git
11+
cd ./example-backend
12+
uv sync --frozen
13+
```
14+
15+
## Running Tests
16+
17+
```bash
18+
pytest tests/test_message_factory.py
19+
```
20+
21+
## Running the Application
22+
23+
```bash
24+
uv run app.py
25+
```
26+
27+
## Running with Modified Error Rates
28+
29+
```bash
30+
ERROR_SUCCESS_COUNT=0 ERROR_FAILURE_COUNT=100 uv run app.py
31+
```
32+
33+
## Building the Container Image
34+
35+
```bash
36+
docker compose build
37+
```
38+
39+
## Running in a Container
40+
41+
```bash
42+
docker run -p 8080:8080 example-backend
43+
```
44+
45+
## Running Tests for Container
46+
47+
At first, start the service.
48+
49+
```bash
50+
docker compose up -d
51+
```
52+
53+
Then, run the tests.
54+
55+
```bash
56+
uv run pytest test_app.py
57+
```
58+
59+
## License
60+
61+
MIT License.
62+
See the [LICENSE](LICENSE) file for details.

app.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from aiohttp import web
2+
import json
3+
import random
4+
import os
5+
import logging
6+
7+
DEFAULT_ERROR_SUCCESS_COUNT = 90
8+
DEFAULT_ERROR_FAILURE_COUNT = 10
9+
DEFAULT_PORT = 8080
10+
11+
logging.basicConfig(level=logging.INFO)
12+
13+
_message_factory = None
14+
15+
16+
class Message:
17+
def __init__(self, message: str, success: bool = True):
18+
self._message = message
19+
self._success = success
20+
21+
@property
22+
def success(self) -> bool:
23+
return self._success
24+
25+
def to_json(self) -> str:
26+
return json.dumps({"message": self._message})
27+
28+
29+
class MessageFactory:
30+
def __init__(self, success_count: int, error_count: int):
31+
self._success_rate: float = success_count / (success_count + error_count)
32+
33+
def create_message(self) -> Message:
34+
if random.random() < self._success_rate:
35+
return Message("ok")
36+
else:
37+
return Message("bad", success=False)
38+
39+
40+
async def index_handler(request: web.Request) -> web.Response:
41+
message = _message_factory.create_message()
42+
if message.success:
43+
return web.Response(text=message.to_json(), content_type="application/json")
44+
else:
45+
return web.Response(text=message.to_json(), content_type="application/json", status=500)
46+
47+
48+
def main():
49+
global _message_factory
50+
51+
ERROR_SUCCESS_COUNT = int(os.getenv("ERROR_SUCCESS_COUNT", DEFAULT_ERROR_SUCCESS_COUNT))
52+
ERROR_FAILURE_COUNT = int(os.getenv("ERROR_FAILURE_COUNT", DEFAULT_ERROR_FAILURE_COUNT))
53+
_message_factory = MessageFactory(ERROR_SUCCESS_COUNT, ERROR_FAILURE_COUNT)
54+
55+
port = int(os.getenv("PORT", DEFAULT_PORT))
56+
57+
app = web.Application()
58+
app.router.add_get("/", index_handler)
59+
web.run_app(app, host="0.0.0.0", port=port)
60+
61+
62+
if __name__ == "__main__":
63+
main()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FROM python:3.13-slim-bookworm
2+
3+
# The installer requires curl (and certificates) to download the release archive
4+
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates
5+
6+
# Download the latest installer
7+
ADD https://astral.sh/uv/install.sh /uv-installer.sh
8+
9+
# Run the installer then remove it
10+
RUN sh /uv-installer.sh && rm /uv-installer.sh
11+
12+
# Ensure the installed binary is on the `PATH`
13+
ENV PATH="/root/.local/bin/:$PATH"
14+
15+
# Copy the project into the image
16+
ADD . /app
17+
18+
# Sync the project into a new environment, using the frozen lockfile
19+
WORKDIR /app
20+
21+
# Install the dependencies
22+
RUN uv sync --frozen
23+
24+
# Run the application
25+
CMD ["uv", "run", "app.py"]

docker-compose.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
backend-service:
3+
build:
4+
context: .
5+
dockerfile: container-images/example-backend/Dockerfile
6+
image: example-backend:latest
7+
container_name: backend
8+
ports:
9+
- "8080:8080"
10+
healthcheck:
11+
test: ["CMD", "curl", "-f", "http://localhost:8080"]
12+
interval: 10s
13+
timeout: 5s
14+
retries: 5

pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[project]
2+
name = "my-backend"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = [
8+
"aiohttp[speedups]>=3.11.13",
9+
]
10+
11+
[dependency-groups]
12+
dev = [
13+
"pytest>=8.3.5",
14+
"requests>=2.32.3",
15+
]

test_app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import requests
2+
3+
4+
def test_container_response():
5+
response = requests.get("http://localhost:8080")
6+
assert response.status_code == 200 or response.status_code == 500
7+
assert response.json()["message"] == "ok" or response.json()["message"] == "bad"

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)