Skip to content

Commit bdb9ca0

Browse files
committed
setting up the generator
1 parent 2a4675f commit bdb9ca0

File tree

9 files changed

+138
-67
lines changed

9 files changed

+138
-67
lines changed

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[tool.poetry]
2-
name = "project-backend"
2+
name = "FastSim-backend"
33
version = "0.1.0"
4-
description = "solo professional project"
4+
description = "Simulate fastapi event loop to manage resources"
55
authors = ["Gioele Botta"]
66
readme = "README.md"
77

scripts/quality-check.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Lint & format with ruff, automatic corrections applied (--fix)
5+
poetry run ruff check src tests --fix
6+
7+
# Type‐check with mypy
8+
poetry run mypy src tests
9+
10+
echo "✅ Linting e type‐checking completed SUCCESSFULLY"

src/app/api/simulation.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
""""Api to simulate the process"""
2+
3+
from fastapi import APIRouter
4+
5+
from app.schemas.simulation_input import SimulationInput
6+
from app.schemas.simulation_output import SimulationOutput
7+
8+
router = APIRouter()
9+
10+
@router.post("/simulation")
11+
async def event_loop_simulation(
12+
generator_input: SimulationInput,
13+
) -> SimulationOutput:
14+
"""Endpoint to handle the simulation."""
15+
# simple map to pass the quality assurance
16+
return SimulationOutput(
17+
metric_1=generator_input.avg_active_users.mean,
18+
metric_2=str(generator_input.avg_request_per_minute_per_user.mean),
19+
metric_n=str(generator_input.total_simulation_time),
20+
)
21+
22+

src/app/core/auth_helpers.py

Lines changed: 0 additions & 62 deletions
This file was deleted.

src/app/main.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
from fastapi import FastAPI
77

8-
98
from app.api.health_check import router as health_router
109
from app.config.settings import settings
1110
from app.db.init_db import close_engine, init_models
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Define the schemas for the simulator"""
2+
3+
from typing import Literal
4+
5+
from pydantic import BaseModel, field_validator, model_validator
6+
7+
8+
class RVConfig(BaseModel):
9+
"""class to configure random variables"""
10+
11+
mean: int | float
12+
distribution: Literal["poisson", "normal"] = "poisson"
13+
variance: float | None = None
14+
15+
@field_validator("mean", mode="before")
16+
def check_mean_is_number(
17+
cls, # noqa: N805
18+
v: object,
19+
) -> float:
20+
"""Ensure `mean` is numeric, then coerce to float."""
21+
err_msg = "mean must be a number (int or float)"
22+
if not isinstance(v, (int, float)):
23+
raise ValueError(err_msg) # noqa: TRY004
24+
return float(v)
25+
26+
@model_validator(mode="after") # type: ignore[arg-type]
27+
def default_variance(cls, model: "RVConfig") -> "RVConfig": # noqa: N805
28+
"""Set variance = mean when distribution == 'normal' and variance is missing."""
29+
if model.variance is None and model.distribution == "normal":
30+
model.variance = model.mean
31+
return model
32+
33+
class SimulationInput(BaseModel):
34+
"""Define the expected variables for the simulation"""
35+
36+
avg_active_users: RVConfig
37+
avg_request_per_minute_per_user: RVConfig
38+
total_simulation_time: int
39+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Define the output of the simulation"""
2+
3+
from pydantic import BaseModel
4+
5+
6+
class SimulationOutput(BaseModel):
7+
"""Define the output of the simulation"""
8+
9+
metric_1: str | int
10+
metric_2: str
11+
#......
12+
metric_n: str

tests/integration/db_initialization/test_init_models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
from sqlalchemy import text
3-
from sqlalchemy.ext.asyncio import create_async_engine
43
from sqlalchemy.exc import SQLAlchemyError
4+
from sqlalchemy.ext.asyncio import create_async_engine
55

66
from app.config.settings import settings
77

@@ -14,4 +14,4 @@ async def test_users_table_exists_after_migrations() -> None:
1414
await conn.execute(text("SELECT 1"))
1515
except SQLAlchemyError:
1616
pytest.fail("Database connection or Alembic setup failed.")
17-
17+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import pytest
2+
from pydantic import ValidationError
3+
4+
from app.schemas.simulation_input import RVConfig
5+
6+
# --------------------------------------------------------------------------- #
7+
# Positive cases #
8+
# --------------------------------------------------------------------------- #
9+
10+
def test_normal_sets_variance_to_mean() -> None:
11+
"""When distribution='normal' and variance is omitted, variance == mean."""
12+
cfg = RVConfig(mean=10, distribution="normal")
13+
assert cfg.variance == 10.0
14+
15+
16+
def test_poisson_keeps_variance_none() -> None:
17+
"""When distribution='poisson' and variance is omitted, variance stays None."""
18+
cfg = RVConfig(mean=5, distribution="poisson")
19+
assert cfg.variance is None
20+
21+
22+
def test_explicit_variance_is_preserved() -> None:
23+
"""If the user supplies variance explicitly, it is preserved unchanged."""
24+
cfg = RVConfig(mean=8, distribution="normal", variance=4)
25+
assert cfg.variance == 4.0
26+
27+
28+
# --------------------------------------------------------------------------- #
29+
# Validation errors #
30+
# --------------------------------------------------------------------------- #
31+
32+
def test_mean_must_be_numeric() -> None:
33+
"""A non-numeric mean raises a ValidationError with our custom message."""
34+
with pytest.raises(ValidationError) as excinfo:
35+
RVConfig(mean="not a number", distribution="poisson")
36+
37+
# Check that at least one error refers to the 'mean' field
38+
assert any(err["loc"] == ("mean",) for err in excinfo.value.errors())
39+
assert "mean must be a number" in excinfo.value.errors()[0]["msg"]
40+
41+
42+
def test_missing_mean_field() -> None:
43+
"""Omitting the mean field raises a 'field required' ValidationError."""
44+
with pytest.raises(ValidationError) as excinfo:
45+
# Using model_validate avoids the constructor signature check
46+
RVConfig.model_validate({"distribution": "normal"})
47+
48+
assert any(
49+
err["loc"] == ("mean",) and err["type"] == "missing"
50+
for err in excinfo.value.errors()
51+
)

0 commit comments

Comments
 (0)