Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
d9d8759600
|
20
README.md
20
README.md
@ -1,15 +1,19 @@
|
||||
# Simple Testdata Generator
|
||||
# Simple Test Data Generator
|
||||
|
||||
## Example Config
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["TESTKEY1", "TESTKEY2", "TESTKEY3"],
|
||||
"max-size": "1GiB",
|
||||
"max-data": "1TiB",
|
||||
"buffer-size": "12MiB",
|
||||
"database": "./db.json",
|
||||
"database-update-interval": 5.0,
|
||||
"log": "./log.jsonl"
|
||||
"binds": [
|
||||
"127.0.0.1:9250"
|
||||
],
|
||||
"log": "-",
|
||||
"buffer-size": "4KiB",
|
||||
"max-size": "2GB",
|
||||
"keys": [
|
||||
"TESTKEY"
|
||||
],
|
||||
"max-data": "10GB",
|
||||
"database": "database.json"
|
||||
}
|
||||
```
|
||||
|
8
flake.lock
generated
8
flake.lock
generated
@ -42,17 +42,17 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1733759999,
|
||||
"narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=",
|
||||
"lastModified": 1735471104,
|
||||
"narHash": "sha256-0q9NGQySwDQc7RhAV2ukfnu7Gxa5/ybJ2ANT8DQrQrs=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56",
|
||||
"rev": "88195a94f390381c6afcdaa933c2f6ff93959cb4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
|
14
flake.nix
14
flake.nix
@ -2,7 +2,7 @@
|
||||
description = "A webserver to create files for testing purposes";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?rev=a73246e2eef4c6ed172979932bc80e1404ba2d56";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
microvm = {
|
||||
url = "github:astro/microvm.nix";
|
||||
@ -174,13 +174,11 @@
|
||||
port = 1234;
|
||||
|
||||
settings = {
|
||||
keys = ["one" "two" "three"];
|
||||
max-size = "1GB";
|
||||
max-data = "100GB";
|
||||
buffer-size = "12MiB";
|
||||
database = "/root/testdata_state.json";
|
||||
database-update-interval = 5.0;
|
||||
log = "/root/log.jsonl";
|
||||
"keys" = ["one" "two" "three"];
|
||||
"max-size" = "1GB";
|
||||
"max-data" = "100GB";
|
||||
"buffer-size" = "12MiB";
|
||||
"database" = "/root/testdata_state.json";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
[project]
|
||||
name = "testdata"
|
||||
version = "1.1.0"
|
||||
requires-python = "~=3.12, <4"
|
||||
requires-python = ">=3.11, <4"
|
||||
dependencies = [
|
||||
"fastapi~=0.115.3",
|
||||
"uvicorn~=0.32.0",
|
||||
"pydantic~=2.9.2",
|
||||
"fastapi==0.115.*",
|
||||
"uvicorn==0.32.*",
|
||||
"pydantic==2.10.*",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest~=8.3.4",
|
||||
"mypy~=1.13.0",
|
||||
"pylint~=3.3.3",
|
||||
"requests~=2.32.3",
|
||||
"types-requests~=2.32.0"
|
||||
"pytest==8.3.*",
|
||||
"mypy==1.13.*",
|
||||
"pylint==3.3.*",
|
||||
"requests==2.32.*",
|
||||
"types-requests==2.32.*"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
testdata = "testdata.main:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools~=75.1.1"]
|
||||
requires = ["setuptools==75.*"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
|
42
src/testdata/testdata.py
vendored
42
src/testdata/testdata.py
vendored
@ -4,7 +4,7 @@ import asyncio
|
||||
|
||||
from typing_extensions import Annotated
|
||||
import uvicorn
|
||||
from fastapi import FastAPI, Request, status, HTTPException
|
||||
from fastapi import FastAPI, status, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel, ConfigDict, Field, BeforeValidator, ValidationError
|
||||
|
||||
@ -43,9 +43,9 @@ class Testdata:
|
||||
max_size: Annotated[int, BeforeValidator(to_bytes)] = Field(alias='max-size')
|
||||
max_data: Annotated[int, BeforeValidator(to_bytes)] = Field(alias='max-data')
|
||||
buffer_size: Annotated[int, BeforeValidator(to_bytes)] = Field(alias='buffer-size')
|
||||
database: str | None = None
|
||||
log: str | None = Field(alias='log', default=None)
|
||||
database_update_interval: float = Field(alias='database-update-interval', default=5)
|
||||
database: str
|
||||
log_path: str | None = Field(alias='log-path', default=None)
|
||||
update_database_interval: float = Field(alias='update-database-interval', default=5)
|
||||
|
||||
_config: Config
|
||||
_api: FastAPI
|
||||
@ -61,10 +61,9 @@ class Testdata:
|
||||
self._state = {'data-used': 0}
|
||||
|
||||
@self._api.get('/zeros')
|
||||
async def zeros(api_key: str, size: int | str, request: Request) -> StreamingResponse:
|
||||
async def zeros(api_key: str, size: int | str) -> StreamingResponse:
|
||||
try:
|
||||
extra = {'api_key': api_key, 'ip': request.client.host if request.client is not None else None, 'size': size}
|
||||
self._logger.debug('Initiated request.', extra=extra)
|
||||
self._logger.debug('', extra={'api_key': api_key, 'size': size})
|
||||
|
||||
if api_key not in config.authorized_keys:
|
||||
raise HTTPException(
|
||||
@ -74,7 +73,7 @@ class Testdata:
|
||||
try:
|
||||
size = convert_to_bytes(size)
|
||||
except ValueError as err:
|
||||
self._logger.warning('Invalid format for size.', extra=extra)
|
||||
self._logger.warning('Invalid format for size.', extra={'api_key': api_key, 'size': size})
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail='Invalid format for size.'
|
||||
@ -93,7 +92,6 @@ class Testdata:
|
||||
)
|
||||
self._state['data-used'] += size
|
||||
|
||||
self._logger.debug('Successfully processed request.', extra=extra)
|
||||
return StreamingResponse(
|
||||
status_code=status.HTTP_200_OK,
|
||||
content=generate_data(size, config.buffer_size),
|
||||
@ -104,13 +102,13 @@ class Testdata:
|
||||
)
|
||||
|
||||
except MinSizePerRequestError as err:
|
||||
self._logger.warning('Size if negative.', extra=extra)
|
||||
self._logger.warning('Size if negative.', extra={'api_key': api_key, 'size': size})
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE,
|
||||
detail='Size has to be non-negative.'
|
||||
) from err
|
||||
except MaxSizePerRequestError as err:
|
||||
self._logger.warning('Exceeded max size per request.', extra=extra)
|
||||
self._logger.warning('Exceeded max size per request.', extra={'api_key': api_key, 'size': size})
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE,
|
||||
detail=f'Exceeded max size per request of {config.max_size} Bytes.'
|
||||
@ -120,7 +118,10 @@ class Testdata:
|
||||
raise err
|
||||
|
||||
async def _update_state(self):
|
||||
mode = 'r+' if os.path.exists(self._config.database) else 'w+'
|
||||
if os.path.exists(self._config.database):
|
||||
mode = 'r+'
|
||||
else:
|
||||
mode = 'w+'
|
||||
|
||||
with open(self._config.database, mode, encoding='utf-8') as file:
|
||||
try:
|
||||
@ -132,21 +133,20 @@ class Testdata:
|
||||
file.seek(0)
|
||||
json.dump(self._state, file)
|
||||
file.truncate()
|
||||
await asyncio.sleep(self._config.database_update_interval)
|
||||
await asyncio.sleep(self._config.update_database_interval)
|
||||
|
||||
async def run(self, host: str, port: int) -> None:
|
||||
try:
|
||||
if self._config.log is not None:
|
||||
logger.setup_logging(self._config.log)
|
||||
if self._config.log_path is not None:
|
||||
logger.setup_logging(self._config.log_path)
|
||||
|
||||
self._logger = logger.getLogger('testdata')
|
||||
# self._logger = logger.getLogger('testdata')
|
||||
self._logger.info('Server started.')
|
||||
|
||||
coroutines = [asyncio.create_task(uvicorn.Server(uvicorn.Config(self._api, host, port)).serve())]
|
||||
if self._config.database is not None:
|
||||
coroutines.append(self._update_state())
|
||||
|
||||
await asyncio.gather(*coroutines)
|
||||
await asyncio.gather(
|
||||
asyncio.create_task(uvicorn.Server(uvicorn.Config(self._api, host, port)).serve()),
|
||||
self._update_state()
|
||||
)
|
||||
except asyncio.exceptions.CancelledError:
|
||||
self._logger.info('Server stopped.')
|
||||
except Exception as err:
|
||||
|
@ -99,7 +99,7 @@ def test_invalid_api_key(_server):
|
||||
'max-size': '1KB',
|
||||
'max-data': '1KB',
|
||||
'buffer-size': '12MiB',
|
||||
'database-update-interval': 0.1
|
||||
'update-database-interval': 0.1
|
||||
})], indirect=['_server'])
|
||||
def test_check_database_update(_server):
|
||||
database = _server
|
||||
|
Reference in New Issue
Block a user