diff --git a/go.mod b/go.mod index ed7d0a2..2b294b6 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,13 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) +require ( + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + golang.org/x/sync v0.7.0 // indirect +) + require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.11.9 // indirect @@ -33,6 +40,7 @@ require ( github.com/goccy/go-json v0.10.3 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect + github.com/jackc/pgx/v5 v5.6.0 github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect diff --git a/go.sum b/go.sum index b380dea..eced6c0 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,16 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -124,6 +132,8 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/load_tests/api.py b/load_tests/api.py new file mode 100644 index 0000000..b080479 --- /dev/null +++ b/load_tests/api.py @@ -0,0 +1,68 @@ +import random +import string +from locust import HttpUser, FastHttpUser + +class Auth(): + token: string + + def __init__(self, token): + self.token = token + +class User(): + email: string + name: string + password: string + + def __init__(self, email, password, name, token = ""): + self.email = email + self.password = password + self.name = name + self.token = token + + +class BackendApi(): + http: FastHttpUser + + def __init__(self, http: FastHttpUser): + self.http = http + + def user_create(self) -> User: + email = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) + '@test.test' + name = ''.join(random.choices(string.ascii_letters, k=10)) + password = 'Abcdef1!!1' + + response = self.http.client.post( + "/user/create", + json={ + "email": email, + "password": password, + "name": name, + }, + ) + if response.status_code != 200: + raise AssertionError('can not create user') + + return User(email, password, name) + + def user_login(self, user: User) -> Auth: + response = self.http.client.post( + "/user/login", + json={ + "email": user.email, + "password": user.password, + }, + ) + if response.status_code != 200: + raise AssertionError('can not login user') + + token = response.json()['token'] + if token == '': + raise AssertionError('empty user token') + + return Auth(token) + + def dummy_get(self, auth: Auth): + headers = {"X-Auth": auth.token} + response = self.http.client.get("/dummy", headers=headers) + if response.status_code != 200: + raise AssertionError('something wrong') \ No newline at end of file diff --git a/load_tests/loads/low_load.py b/load_tests/loads/low_load.py new file mode 100644 index 0000000..c84d101 --- /dev/null +++ b/load_tests/loads/low_load.py @@ -0,0 +1,13 @@ +from locust import LoadTestShape + +class LowLoad(LoadTestShape): + time_limit = 600 + spawn_rate = 5 + max_users = 100 + + def tick(self) -> (tuple[float, int] | None): + user_count = self.spawn_rate * self.get_run_time() + if user_count > self.max_users: + user_count = self.max_users + + return (user_count, self.spawn_rate) \ No newline at end of file diff --git a/load_tests/locustfile.py b/load_tests/locustfile.py deleted file mode 100644 index aaeaedb..0000000 --- a/load_tests/locustfile.py +++ /dev/null @@ -1,40 +0,0 @@ -import random -import string -from locust import HttpUser, FastHttpUser, task - -class DummyRoute(FastHttpUser): - @task - def dummy_test(self): - self.client.get("/dummy", headers=self.headers) - - def on_start(self): - randEmail = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) + '@test.test' - randName = ''.join(random.choices(string.ascii_letters, k=10)) - password = 'Abcdef1!!1' - - response = self.client.post( - "/user/create", - json={ - "email": randEmail, - "password": password, - "name": randName, - }, - ) - if response.status_code != 200: - raise AssertionError('can not create user') - - response = self.client.post( - "/user/login", - json={ - "email": randEmail, - "password": password, - }, - ) - if response.status_code != 200: - raise AssertionError('can not login user') - - token = response.json()['token'] - if token == '': - raise AssertionError('empty user token') - - self.headers = {"X-Auth": token} \ No newline at end of file diff --git a/load_tests/makefile b/load_tests/makefile index 9608615..efc9b49 100644 --- a/load_tests/makefile +++ b/load_tests/makefile @@ -16,4 +16,6 @@ requirements: pip freeze > requirements.txt run-web: - locust --host http://localhost:8080 \ No newline at end of file + locust -f tests,loads --class-picker --host http://localhost:8080 --processes 16 + + diff --git a/load_tests/tests/dummy.py b/load_tests/tests/dummy.py new file mode 100644 index 0000000..bf84587 --- /dev/null +++ b/load_tests/tests/dummy.py @@ -0,0 +1,17 @@ +from locust import FastHttpUser, task + +from api import BackendApi, Auth + +class DummyGet(FastHttpUser): + api: BackendApi + auth: Auth + + @task + def dummy_test(self): + self.api.dummy_get(self.auth) + + def on_start(self): + self.api = BackendApi(self) + + user = self.api.user_create() + self.auth = self.api.user_login(user) \ No newline at end of file diff --git a/load_tests/tests/user.py b/load_tests/tests/user.py new file mode 100644 index 0000000..d9f3881 --- /dev/null +++ b/load_tests/tests/user.py @@ -0,0 +1,25 @@ +from locust import FastHttpUser, task + +from api import BackendApi, User + +class UserCreate(FastHttpUser): + api: BackendApi + + @task + def user_create_test(self): + self.api.user_create() + + def on_start(self): + self.api = BackendApi(self) + +class UserLogin(FastHttpUser): + api: BackendApi + user: User + + @task + def user_create_test(self): + self.api.user_login(self.user) + + def on_start(self): + self.api = BackendApi(self) + self.user = self.api.user_create() \ No newline at end of file