From 6bfa176655cd49a6559950ba646a5fac3bb7c399 Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Fri, 12 Dec 2025 23:42:02 +0700 Subject: [PATCH 1/6] chore: fix CI backend --- .github/workflows/test_backend.yaml | 5 +- backend/pixi.lock | 123 ++++++++++++++++++++++++++++ backend/pixi.toml | 5 ++ pixi.lock | 35 -------- pixi.toml | 10 --- 5 files changed, 132 insertions(+), 46 deletions(-) delete mode 100644 pixi.lock delete mode 100644 pixi.toml diff --git a/.github/workflows/test_backend.yaml b/.github/workflows/test_backend.yaml index 19c1383..a7c5f01 100644 --- a/.github/workflows/test_backend.yaml +++ b/.github/workflows/test_backend.yaml @@ -19,6 +19,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Start test docker services run: | make start-test-infra @@ -41,8 +42,10 @@ jobs: echo "127.0.0.1 river-localstack" | sudo tee -a /etc/hosts until nc -z river-localstack 4566; do sleep 1; done - - name: Run tests + - name: Setup Pixi uses: prefix-dev/setup-pixi@v0.8.1 + + - name: Run tests env: GH_USERNAME: ${{ secrets.GH_USERNAME }} GH_ACCESS_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }} diff --git a/backend/pixi.lock b/backend/pixi.lock index 5646051..507279e 100644 --- a/backend/pixi.lock +++ b/backend/pixi.lock @@ -53,6 +53,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/1b/5b/974430b5ffdb7a4f1941d13d83c64a0395114503cc357c6b9ae4ce5047ed/click_didyoumean-0.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/3d/9a/2abecb28ae875e39c8cad711eb1186d8d14eab564705325e77e4e6ab9ae5/click_plugins-1.1.1.2-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/40/9d857001228658f0d59e97ebd4c346fe73e138c6de1bce61dc568a57c7f8/click_repl-0.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/79/62/120842ab20d9150a9d3a6bdc07fe2870384e82f5266d41c53b08a3a96b34/cryptography-45.0.6-cp311-abi3-manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/65/a4/eeb5295637d5c85a50474d347cf6c610be43e45ce8a0211d4849fbc1701b/deepmerge-1.1.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/ef/4cb333825d10317a36a1154341ba37e6e9c087bac99c1990ef07ffdb376f/dictdiffer-0.9.0-py2.py3-none-any.whl @@ -66,6 +67,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6c/0c/f37b6a241f0759b7653ffa7213889d89ad49a2b76eb2ddf3b57b2738c347/iso8601-2.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl @@ -76,8 +78,11 @@ environments: - pypi: https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/15/f8/c7bd0ef12954a81a1d3cea60a13946bd9a49a0036a5927770c461eade7ae/paramiko-3.5.1-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/8c/51/2779ccdf9305981a06b21a6b27e8547c948d85c41c76ff434192784a4c93/psycopg-3.3.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e7/c3/26b8a0908a9db249de3b4169692e1c7c19048a9bc41a4d3209cee7dbb758/psycopg_pool-3.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl @@ -88,6 +93,9 @@ environments: - pypi: https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/52/bc/a5cff7f8c30d5f4c26a07dfb0bcda1176ab8b2de86dda3106c00a02ad787/pynacl-1.6.0-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/67/cf/2d47236c80d6deea85e76c86b959f0ec24369c16db691c6266f7a20ff4bd/pypika_tortoise-0.6.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl @@ -408,6 +416,13 @@ packages: - pytest>=7.2.1 ; extra == 'testing' - tox>=4.4.3 ; extra == 'testing' requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/5b/b6/51b5d1eb6fcbb9a1d5d6984e26cbe09018475c2922d554fd724dd0f056ee/coverage-7.13.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + name: coverage + version: 7.13.0 + sha256: 583221913fbc8f53b88c42e8dbb8fca1d0f2e597cb190ce45916662b8b9d9621 + requires_dist: + - tomli ; python_full_version <= '3.11' and extra == 'toml' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/79/62/120842ab20d9150a9d3a6bdc07fe2870384e82f5266d41c53b08a3a96b34/cryptography-45.0.6-cp311-abi3-manylinux_2_28_x86_64.whl name: cryptography version: 45.0.6 @@ -618,6 +633,11 @@ packages: - pytest>=8.3.2 ; extra == 'all' - flake8>=7.1.1 ; extra == 'all' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl + name: iniconfig + version: 2.3.0 + sha256: f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/6c/0c/f37b6a241f0759b7653ffa7213889d89ad49a2b76eb2ddf3b57b2738c347/iso8601-2.1.0-py3-none-any.whl name: iso8601 version: 2.1.0 @@ -912,6 +932,17 @@ packages: - pywin32>=2.1.8 ; sys_platform == 'win32' and extra == 'all' - invoke>=2.0 ; extra == 'all' requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl + name: pluggy + version: 1.6.0 + sha256: e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 + requires_dist: + - pre-commit ; extra == 'dev' + - tox ; extra == 'dev' + - pytest ; extra == 'testing' + - pytest-benchmark ; extra == 'testing' + - coverage ; extra == 'testing' + requires_python: '>=3.9' - pypi: https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl name: prompt-toolkit version: 3.0.52 @@ -950,6 +981,53 @@ packages: - pytest-xdist ; extra == 'test' - setuptools ; extra == 'test' requires_python: '>=3.6' +- pypi: https://files.pythonhosted.org/packages/8c/51/2779ccdf9305981a06b21a6b27e8547c948d85c41c76ff434192784a4c93/psycopg-3.3.2-py3-none-any.whl + name: psycopg + version: 3.3.2 + sha256: 3e94bc5f4690247d734599af56e51bae8e0db8e4311ea413f801fef82b14a99b + requires_dist: + - typing-extensions>=4.6 ; python_full_version < '3.13' + - tzdata ; sys_platform == 'win32' + - psycopg-c==3.3.2 ; implementation_name != 'pypy' and extra == 'c' + - psycopg-binary==3.3.2 ; implementation_name != 'pypy' and extra == 'binary' + - psycopg-pool ; extra == 'pool' + - anyio>=4.0 ; extra == 'test' + - mypy>=1.19.0 ; implementation_name != 'pypy' and extra == 'test' + - pproxy>=2.7 ; extra == 'test' + - pytest>=6.2.5 ; extra == 'test' + - pytest-cov>=3.0 ; extra == 'test' + - pytest-randomly>=3.5 ; extra == 'test' + - ast-comments>=1.1.2 ; extra == 'dev' + - black>=24.1.0 ; extra == 'dev' + - codespell>=2.2 ; extra == 'dev' + - cython-lint>=0.16 ; extra == 'dev' + - dnspython>=2.1 ; extra == 'dev' + - flake8>=4.0 ; extra == 'dev' + - isort[colors]>=6.0 ; extra == 'dev' + - isort-psycopg ; extra == 'dev' + - mypy>=1.19.0 ; extra == 'dev' + - pre-commit>=4.0.1 ; extra == 'dev' + - types-setuptools>=57.4 ; extra == 'dev' + - types-shapely>=2.0 ; extra == 'dev' + - wheel>=0.37 ; extra == 'dev' + - sphinx>=5.0 ; extra == 'docs' + - furo==2022.6.21 ; extra == 'docs' + - sphinx-autobuild>=2021.3.14 ; extra == 'docs' + - sphinx-autodoc-typehints>=1.12 ; extra == 'docs' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e7/c3/26b8a0908a9db249de3b4169692e1c7c19048a9bc41a4d3209cee7dbb758/psycopg_pool-3.3.0-py3-none-any.whl + name: psycopg-pool + version: 3.3.0 + sha256: 2e44329155c410b5e8666372db44276a8b1ebd8c90f1c3026ebba40d4bc81063 + requires_dist: + - typing-extensions>=4.6 + - anyio>=4.0 ; extra == 'test' + - mypy>=1.14 ; extra == 'test' + - pproxy>=2.7 ; extra == 'test' + - pytest>=6.2.5 ; extra == 'test' + - pytest-cov>=3.0 ; extra == 'test' + - pytest-randomly>=3.5 ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/c8/f1/d6a797abb14f6283c0ddff96bbdd46937f64122b8c925cab503dd37f8214/pyasn1-0.6.1-py3-none-any.whl name: pyasn1 version: 0.6.1 @@ -1042,6 +1120,51 @@ packages: version: 0.6.2 sha256: 425462b02ede0a5ed7b812ec12427419927ed6b19282c55667d1cbc9a440d3cb requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl + name: pytest + version: 9.0.2 + sha256: 711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b + requires_dist: + - colorama>=0.4 ; sys_platform == 'win32' + - exceptiongroup>=1 ; python_full_version < '3.11' + - iniconfig>=1.0.1 + - packaging>=22 + - pluggy>=1.5,<2 + - pygments>=2.7.2 + - tomli>=1 ; python_full_version < '3.11' + - argcomplete ; extra == 'dev' + - attrs>=19.2 ; extra == 'dev' + - hypothesis>=3.56 ; extra == 'dev' + - mock ; extra == 'dev' + - requests ; extra == 'dev' + - setuptools ; extra == 'dev' + - xmlschema ; extra == 'dev' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl + name: pytest-asyncio + version: 1.3.0 + sha256: 611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5 + requires_dist: + - backports-asyncio-runner>=1.1,<2 ; python_full_version < '3.11' + - pytest>=8.2,<10 + - typing-extensions>=4.12 ; python_full_version < '3.13' + - sphinx>=5.3 ; extra == 'docs' + - sphinx-rtd-theme>=1 ; extra == 'docs' + - coverage>=6.2 ; extra == 'testing' + - hypothesis>=5.7.1 ; extra == 'testing' + requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl + name: pytest-cov + version: 7.0.0 + sha256: 3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861 + requires_dist: + - coverage[toml]>=7.10.6 + - pluggy>=1.2 + - pytest>=7 + - process-tests ; extra == 'testing' + - pytest-xdist ; extra == 'testing' + - virtualenv ; extra == 'testing' + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.11-h9e4cc4f_0_cpython.conda sha256: 6cca004806ceceea9585d4d655059e951152fc774a471593d4f5138e6a54c81d md5: 94206474a5608243a10c92cefbe0908f diff --git a/backend/pixi.toml b/backend/pixi.toml index 2c4a6e0..68a77ea 100644 --- a/backend/pixi.toml +++ b/backend/pixi.toml @@ -31,3 +31,8 @@ boto3 = "==1.35.99" psutil = "==7.0.0" sshtunnel = "==0.4.0" celery = "==5.5.3" +pytest = ">=9.0.2, <10" +pytest-asyncio = ">=1.3.0, <2" +pytest-cov = ">=7.0.0, <8" +psycopg = ">=3.3.2, <4" +psycopg-pool = ">=3.3.0, <4" diff --git a/pixi.lock b/pixi.lock deleted file mode 100644 index 4fcbb37..0000000 --- a/pixi.lock +++ /dev/null @@ -1,35 +0,0 @@ -version: 6 -environments: - default: - channels: - - url: https://conda.anaconda.org/conda-forge/ - - url: https://conda.anaconda.org/bioconda/ - packages: - linux-64: - - conda: https://conda.anaconda.org/conda-forge/linux-64/act-0.2.82-ha8f183a_0.conda - osx-arm64: - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/act-0.2.82-h75b854d_0.conda - win-64: - - conda: https://conda.anaconda.org/conda-forge/win-64/act-0.2.82-hd02998f_0.conda -packages: -- conda: https://conda.anaconda.org/conda-forge/linux-64/act-0.2.82-ha8f183a_0.conda - sha256: c9cdd363a4aa2854b6e662ac172296e5e59f59e5dbf0c2bba7bcf8677b42f87a - md5: 1a5a7ad40991d0c85d48c1abe526b808 - license: MIT - license_family: MIT - size: 13379809 - timestamp: 1759314781579 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/act-0.2.82-h75b854d_0.conda - sha256: 21ca6bd1209355482a0cfcc05a05093eff99d3fc6b2ba679c81b2432132b4b49 - md5: bc7907733681310671517af3b2fe3f36 - license: MIT - license_family: MIT - size: 12996181 - timestamp: 1759315159609 -- conda: https://conda.anaconda.org/conda-forge/win-64/act-0.2.82-hd02998f_0.conda - sha256: a8a43e97b0bced09b1053f1c487ac1af26dff192fd5d528c8e04ce0b1846c8c5 - md5: 724999ac02d7045dc605d6ea445f6afd - license: MIT - license_family: MIT - size: 13617452 - timestamp: 1759315002727 diff --git a/pixi.toml b/pixi.toml deleted file mode 100644 index 03eb2cd..0000000 --- a/pixi.toml +++ /dev/null @@ -1,10 +0,0 @@ -[workspace] -channels = ["conda-forge", "bioconda"] -name = "river" -platforms = ["linux-64", "osx-arm64", "win-64"] -version = "0.1.0" - -[tasks] - -[dependencies] -act = ">=0.2.82,<0.3" From 42eea3d75329b7b71d3f63de900101e62674e8d8 Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Fri, 12 Dec 2025 23:53:06 +0700 Subject: [PATCH 2/6] fix: remove pixi github action setup --- .github/workflows/test_backend.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/test_backend.yaml b/.github/workflows/test_backend.yaml index a7c5f01..e984598 100644 --- a/.github/workflows/test_backend.yaml +++ b/.github/workflows/test_backend.yaml @@ -42,9 +42,6 @@ jobs: echo "127.0.0.1 river-localstack" | sudo tee -a /etc/hosts until nc -z river-localstack 4566; do sleep 1; done - - name: Setup Pixi - uses: prefix-dev/setup-pixi@v0.8.1 - - name: Run tests env: GH_USERNAME: ${{ secrets.GH_USERNAME }} From 861801bc1065988b8a25914ffd55348877b89f40 Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Sat, 13 Dec 2025 00:01:21 +0700 Subject: [PATCH 3/6] fix: remove pixi github action setup --- Makefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a6841c8..0d756ed 100644 --- a/Makefile +++ b/Makefile @@ -21,9 +21,7 @@ PYPY_VERSION := 3.11-v7.3.20-linux64 backend/pypy${PYPY_VERSION}/bin/pypy3: wget https://downloads.python.org/pypy/pypy${PYPY_VERSION}.tar.bz2 -O backend/pypy${PYPY_VERSION}.tar.bz2 tar xf backend/pypy${PYPY_VERSION}.tar.bz2 -C backend - -install-backend-deps: backend/pypy${PYPY_VERSION}/bin/pypy3 - cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -mpip install -r requirements.txt + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m ensurepip && ./pypy${PYPY_VERSION}/bin/pypy3 -mpip install -r requirements.txt dev-frontend: ~/.pixi/bin/pixi cd frontend && pixi run npm install -f @@ -55,9 +53,9 @@ migrate-dev-db: backend/pypy${PYPY_VERSION}/bin/pypy3 start-test-db: docker compose up test-db -d -migrate-test-db: ~/.pixi/bin/pixi - cd backend && backend/pypy${PYPY_VERSION}/bin/pypy3 -m aerich init-db || echo "DB already existed" - cd backend && backend/pypy${PYPY_VERSION}/bin/pypy3 -m aerich upgrade +migrate-test-db: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m aerich init-db || echo "DB already existed" + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m aerich upgrade format-backend: black . --line-length 120 From 70f18eaaa8e103d4a8dd31b2858a7a9b23f21dd0 Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Sat, 13 Dec 2025 00:10:23 +0700 Subject: [PATCH 4/6] chore: use pypy to test backend --- Makefile | 37 ++++++++++++++++++++----------------- backend/app/conftest.py | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 0d756ed..28a18cb 100644 --- a/Makefile +++ b/Makefile @@ -88,30 +88,33 @@ start-traefik: ~/.pixi/bin/pixi .PHONY: start-test-infra test-auth test-health test-organization test-all start-test-infra: start-test-db start-redis start-localstack start-slurm @echo "Start test infra setup" - -test-auth: - cd backend && pixi run pytest --cov=app/service_auth app/service_auth --cov-report=term-missing -vvvvvv -test-health: - cd backend && pixi run pytest --cov=app/service_health app/service_health --cov-report=term-missing -vvvvvv +install-test-deps: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -mpip install pytest pytest-cov pytest-asyncio + +test-auth: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_auth app/service_auth --cov-report=term-missing -vvvvvv + +test-health: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_health app/service_health --cov-report=term-missing -vvvvvv -test-organization: - cd backend && pixi run pytest --cov=app/service_organization app/service_organization --cov-report=term-missing -vvvvvv +test-organization: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_organization app/service_organization --cov-report=term-missing -vvvvvv -test-credential: - cd backend && pixi run pytest --cov=app/service_credential app/service_credential --cov-report=term-missing -vvvvvv -k "github" +test-credential: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_credential app/service_credential --cov-report=term-missing -vvvvvv -k "github" -test-analysis: - cd backend && pixi run pytest --cov=app/service_analysis app/service_analysis --cov-report=term-missing -vvvvvv +test-analysis: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_analysis app/service_analysis --cov-report=term-missing -vvvvvv -test-project: - cd backend && pixi run pytest --cov=app/service_project app/service_project --cov-report=term-missing -vvvvvv +test-project: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_project app/service_project --cov-report=term-missing -vvvvvv -test-storage: - cd backend && pixi run pytest --cov=app/service_storage app/service_storage --cov-report=term-missing -vvvvvv +test-storage: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_storage app/service_storage --cov-report=term-missing -vvvvvv -test-job: - cd backend && pixi run pytest --cov=app/service_job app/service_job --cov-report=term-missing -vvvvvv +test-job: backend/pypy${PYPY_VERSION}/bin/pypy3 + cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_job app/service_job --cov-report=term-missing -vvvvvv test-all: test-health test-auth test-organization test-credential test-analysis test-project echo "Running all tests..." diff --git a/backend/app/conftest.py b/backend/app/conftest.py index 9f03e78..25983dd 100644 --- a/backend/app/conftest.py +++ b/backend/app/conftest.py @@ -270,7 +270,7 @@ async def user_aws(test_client, cookies_admin, user_aws_arn): } aws = await create_instance( test_client, - f"/api/users/creds/{cred["id"]}/", + f"/api/users/creds/{cred['id']}/", aws_data, cookies_admin, 201, @@ -311,7 +311,7 @@ async def org_aws(test_client, organization, cookies_org_manager): } aws = await create_instance( test_client, - f"/api/orgs/{organization}/creds/{cred["id"]}/", + f"/api/orgs/{organization}/creds/{cred['id']}/", aws_data, cookies_org_manager, 201, From 21fc3245f5415b9bb471e3ab8095c8db516c666c Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Sat, 13 Dec 2025 00:16:39 +0700 Subject: [PATCH 5/6] chore: use pypy to test backend --- Makefile | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 28a18cb..6c9a631 100644 --- a/Makefile +++ b/Makefile @@ -92,28 +92,28 @@ start-test-infra: start-test-db start-redis start-localstack start-slurm install-test-deps: backend/pypy${PYPY_VERSION}/bin/pypy3 cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -mpip install pytest pytest-cov pytest-asyncio -test-auth: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-auth: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_auth app/service_auth --cov-report=term-missing -vvvvvv -test-health: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-health: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_health app/service_health --cov-report=term-missing -vvvvvv -test-organization: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-organization: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_organization app/service_organization --cov-report=term-missing -vvvvvv -test-credential: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-credential: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_credential app/service_credential --cov-report=term-missing -vvvvvv -k "github" -test-analysis: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-analysis: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_analysis app/service_analysis --cov-report=term-missing -vvvvvv -test-project: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-project: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_project app/service_project --cov-report=term-missing -vvvvvv -test-storage: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-storage: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_storage app/service_storage --cov-report=term-missing -vvvvvv -test-job: backend/pypy${PYPY_VERSION}/bin/pypy3 +test-job: install-test-deps cd backend && ./pypy${PYPY_VERSION}/bin/pypy3 -m pytest --cov=app/service_job app/service_job --cov-report=term-missing -vvvvvv test-all: test-health test-auth test-organization test-credential test-analysis test-project From 8a2c4ea75f2814953e91e29c8202927ba4795a35 Mon Sep 17 00:00:00 2001 From: "Thanh-Giang (River) Tan Nguyen" Date: Sat, 13 Dec 2025 00:30:57 +0700 Subject: [PATCH 6/6] fix: fstring in pypy3.11 --- backend/app/service_analysis/controller.py | 2 +- .../service_analysis/tests/test_analysis.py | 20 ++++++++-------- .../app/service_credential/tests/test_aws.py | 16 ++++++------- .../app/service_credential/tests/test_base.py | 4 ++-- backend/app/service_job/controller.py | 2 +- .../app/service_job/tests/test_api_web_job.py | 10 ++++---- .../app/service_organization/controller.py | 2 +- .../tests/test_organization.py | 18 +++++++------- backend/app/service_project/controller.py | 2 +- .../app/service_project/tests/test_project.py | 24 +++++++++---------- 10 files changed, 50 insertions(+), 50 deletions(-) diff --git a/backend/app/service_analysis/controller.py b/backend/app/service_analysis/controller.py index fd20c1b..f4858fa 100644 --- a/backend/app/service_analysis/controller.py +++ b/backend/app/service_analysis/controller.py @@ -95,7 +95,7 @@ async def get_analysis( return ok( { - "items": result, + "item": result, "page": page, "page_size": page_size, "total": total_count, diff --git a/backend/app/service_analysis/tests/test_analysis.py b/backend/app/service_analysis/tests/test_analysis.py index 764a33a..aa137ee 100644 --- a/backend/app/service_analysis/tests/test_analysis.py +++ b/backend/app/service_analysis/tests/test_analysis.py @@ -51,16 +51,16 @@ async def test_basic_analysis( # list instance as admin res_admin = await list_instance(test_client, f"{BASE_URL}/", cookies_admin, 200) - assert len(res_admin["items"]) == 1 - assert_instance_fields(res_admin["items"][0], EXPECTED_FIELDS) + assert len(res_admin["item"]) == 1 + assert_instance_fields(res_admin["item"][0], EXPECTED_FIELDS) # list instance as contributor res_contrib = await list_instance(test_client, f"{BASE_URL}/", cookies_contributor, 200) - assert len(res_contrib["items"]) == 1 - assert_instance_fields(res_contrib["items"][0], EXPECTED_FIELDS) + assert len(res_contrib["item"]) == 1 + assert_instance_fields(res_contrib["item"][0], EXPECTED_FIELDS) # delete as admin (should fail) - delete_url = f'{BASE_URL}/{res_admin["items"][0]["id"]}/' + delete_url = f"{BASE_URL}/{res_admin['item'][0]['id']}/" await delete_instance(test_client, delete_url, cookies_admin, 204) # delete as superuser (should pass) @@ -93,23 +93,23 @@ async def get_page(page, page_size): # page 1 data = await get_page(1, 10) assert data["page"] == 1 - assert len(data["items"]) == 10 + assert len(data["item"]) == 10 # page 2 data = await get_page(2, 10) assert data["page"] == 2 - assert len(data["items"]) == 10 + assert len(data["item"]) == 10 # last page data = await get_page(4, 10) assert data["page"] == 4 - assert len(data["items"]) == 0 + assert len(data["item"]) == 0 # too large page data = await get_page(99, 10) - assert data["items"] == [] + assert data["item"] == [] # search by url res = await test_client.get(f"{BASE_URL}/", query={"search": "repo-1"}, cookies=cookies_admin) data = await res.json() assert res.status == 200 - assert any("repo-1" in item["url"] for item in data["items"]) + assert any("repo-1" in item["url"] for item in data["item"]) diff --git a/backend/app/service_credential/tests/test_aws.py b/backend/app/service_credential/tests/test_aws.py index a96702a..b6ea664 100644 --- a/backend/app/service_credential/tests/test_aws.py +++ b/backend/app/service_credential/tests/test_aws.py @@ -98,7 +98,7 @@ async def test_credential_aws( # success aws_instance = await create_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", aws_data, cookies=cookies_admin, ) @@ -117,7 +117,7 @@ async def test_credential_aws( bad_data["aws"]["endpoint_url"] = "http://localhost:8081" instance = await create_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", bad_data, cookies_admin, 400, @@ -149,7 +149,7 @@ async def test_credential_aws( # success aws_instance = await create_instance( test_client, - f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws["id"]}/", + f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws['id']}/", aws_data, cookies=cookies_org_manager, ) @@ -168,7 +168,7 @@ async def test_credential_aws( bad_data["aws"]["endpoint_url"] = "http://localhost:8081" instance = await create_instance( test_client, - f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws["id"]}/", + f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws['id']}/", bad_data, cookies_org_manager, 400, @@ -254,7 +254,7 @@ async def test_credential_aws( aws_data["aws"]["endpoint_url"] = os.environ["ENDPOINT_URL"] aws_instance = await update_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", aws_data, cookies=cookies_admin, expected_status=201, @@ -286,7 +286,7 @@ async def test_credential_aws( aws_data["aws"]["endpoint_url"] = os.environ["ENDPOINT_URL"] aws_instance = await update_instance( test_client, - f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws["id"]}/", + f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws['id']}/", aws_data, cookies=cookies_org_manager, expected_status=201, @@ -303,11 +303,11 @@ async def test_credential_aws( # Delete await delete_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", cookies_admin, ) await delete_instance( test_client, - f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws["id"]}/", + f"{BASE_URL}/orgs/{organization}/creds/{cred_instance_org_aws['id']}/", cookies_org_manager, ) diff --git a/backend/app/service_credential/tests/test_base.py b/backend/app/service_credential/tests/test_base.py index 391d8c5..7c7af97 100644 --- a/backend/app/service_credential/tests/test_base.py +++ b/backend/app/service_credential/tests/test_base.py @@ -46,7 +46,7 @@ async def test_credential_base( error = await update_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", bad_data, cookies_admin, 400, @@ -65,7 +65,7 @@ async def test_credential_base( } error = await update_instance( test_client, - f"{BASE_URL}/users/creds/{cred_instance_user_aws["id"]}/", + f"{BASE_URL}/users/creds/{cred_instance_user_aws['id']}/", bad_data, cookies_admin, 400, diff --git a/backend/app/service_job/controller.py b/backend/app/service_job/controller.py index 32e5d60..3947c13 100644 --- a/backend/app/service_job/controller.py +++ b/backend/app/service_job/controller.py @@ -149,7 +149,7 @@ async def list_web_job( return ok( { - "items": result, + "item": result, "page": page, "page_size": page_size, "total": total_count, diff --git a/backend/app/service_job/tests/test_api_web_job.py b/backend/app/service_job/tests/test_api_web_job.py index 1e078ff..94a5a8b 100644 --- a/backend/app/service_job/tests/test_api_web_job.py +++ b/backend/app/service_job/tests/test_api_web_job.py @@ -80,8 +80,8 @@ async def test_create_list_job( ) assert res.status == 200 result = await res.json() - assert "items" in result - assert len(result["items"]) == 5 + assert "item" in result + assert len(result["item"]) == 5 assert result["total"] >= 15 # List jobs with pagination (page=3, page_size=5) @@ -92,8 +92,8 @@ async def test_create_list_job( ) assert res.status == 200 result = await res.json() - assert "items" in result - assert len(result["items"]) == 5 + assert "item" in result + assert len(result["item"]) == 5 # Search for a specific job by name search_name = "customer-03" @@ -104,7 +104,7 @@ async def test_create_list_job( ) assert res.status == 200 result = await res.json() - assert any(job["name"] == search_name for job in result["items"]) + assert any(job["name"] == search_name for job in result["item"]) # get log of current running job # TODO: test job log diff --git a/backend/app/service_organization/controller.py b/backend/app/service_organization/controller.py index 3842d0b..67638d6 100644 --- a/backend/app/service_organization/controller.py +++ b/backend/app/service_organization/controller.py @@ -69,7 +69,7 @@ async def list_organization( return ok( { - "items": result, + "item": result, "page": page, "page_size": page_size, "total": total_count, diff --git a/backend/app/service_organization/tests/test_organization.py b/backend/app/service_organization/tests/test_organization.py index 1339057..96a59dd 100644 --- a/backend/app/service_organization/tests/test_organization.py +++ b/backend/app/service_organization/tests/test_organization.py @@ -45,8 +45,8 @@ async def check_page(page, expected_count): res_data = await res.json() assert res.status == 200 assert res_data["page"] == page - assert len(res_data["items"]) == expected_count - for item in res_data["items"]: + assert len(res_data["item"]) == expected_count + for item in res_data["item"]: assert_instance_fields(item, expected_fields) await check_page(1, 10) @@ -58,22 +58,22 @@ async def check_page(page, expected_count): res_data = await res.json() assert res.status == 200 assert res_data["total"] == 1 - assert "29" in res_data["items"][0]["name"] + assert "29" in res_data["item"][0]["name"] # search by description res = await test_client.get(f"{BASE_URL}", query={"search": "Description-1"}, cookies=cookies_org_manager) res_data = await res.json() assert res.status == 200 assert res_data["total"] == 11 - assert len(res_data["items"]) == 11 - for item in res_data["items"]: + assert len(res_data["item"]) == 11 + for item in res_data["item"]: assert_instance_fields(item, expected_fields) # Update - update_org = res_data["items"][0] + update_org = res_data["item"][0] updated_org = await update_instance( test_client, - f"{BASE_URL}/{update_org["id"]}/", + f"{BASE_URL}/{update_org['id']}/", data={ "name": "New name", "description": "New description", @@ -90,7 +90,7 @@ async def check_page(page, expected_count): # delete 1 remains 29 await delete_instance( test_client, - f"{BASE_URL}/{update_org["id"]}/", + f"{BASE_URL}/{update_org['id']}/", cookies=cookies_org_manager, ) res = await test_client.get( @@ -99,4 +99,4 @@ async def check_page(page, expected_count): cookies=cookies_org_manager, ) res_data = await res.json() - assert len(res_data["items"]) == 29 + assert len(res_data["item"]) == 29 diff --git a/backend/app/service_project/controller.py b/backend/app/service_project/controller.py index 4d300d8..e3aac61 100644 --- a/backend/app/service_project/controller.py +++ b/backend/app/service_project/controller.py @@ -148,7 +148,7 @@ async def list_projects( return ok( { - "items": result, + "item": result, "page": page, "page_size": page_size, "total": total_count, diff --git a/backend/app/service_project/tests/test_project.py b/backend/app/service_project/tests/test_project.py index 6117db2..6d0c08a 100644 --- a/backend/app/service_project/tests/test_project.py +++ b/backend/app/service_project/tests/test_project.py @@ -184,8 +184,8 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["page"] == 1 - assert len(res_data["items"]) == 5 - for item in res_data["items"]: + assert len(res_data["item"]) == 5 + for item in res_data["item"]: assert_instance_fields(item, expected_fields) # second page @@ -193,8 +193,8 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["page"] == 2 - assert len(res_data["items"]) == 3 - for item in res_data["items"]: + assert len(res_data["item"]) == 3 + for item in res_data["item"]: assert_instance_fields(item, expected_fields) # Search name @@ -203,7 +203,7 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["total"] == 2 - assert "public project 1" in res_data["items"][0]["name"] + assert "public project 1" in res_data["item"][0]["name"] # failed due to invalid format filters res = await test_client.get( @@ -233,7 +233,7 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["total"] == 2 - assert "public project 1" in res_data["items"][0]["name"] + assert "public project 1" in res_data["item"][0]["name"] # Search by role res = await test_client.get( @@ -265,8 +265,8 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["total"] == 8 - assert len(res_data["items"]) == 5 - for item in res_data["items"]: + assert len(res_data["item"]) == 5 + for item in res_data["item"]: assert_instance_fields(item, expected_fields) # Get by desc sorting (asc by default) @@ -283,13 +283,13 @@ async def test_project_CRUD_and_search( res_data = await res.json() assert res.status == 200 assert res_data["total"] == 8 - assert len(res_data["items"]) == 5 - for item in res_data["items"]: + assert len(res_data["item"]) == 5 + for item in res_data["item"]: assert_instance_fields(item, expected_fields) - assert res_data["items"][0]["name"] == "user public project 1" # this one if not sorting will be last + assert res_data["item"][0]["name"] == "user public project 1" # this one if not sorting will be last # Test RBAC for update/delete - project_id = res_data["items"][0]["id"] + project_id = res_data["item"][0]["id"] update_data = {"name": "Edited name", "description": "Edited description"} for cookies in [cookies_contributor, cookies_viewer]: