Repository navigation
feat(test): Implement parallel test execution and uvicorn timeout increase to 180s #1152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8327b49
3babd72
e6cc373
162d75b
263e22a
4a9682d
460907c
2d3bd1c
1415db8
5236004
763dc1b
cbf846f
3366a67
a3eddde
2d3c6fc
ed32937
074ab00
df949fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,6 @@ | |
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # Password hashing configuration | ||
| pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") | ||
|
|
||
| # JWT configuration | ||
|
|
@@ -299,7 +298,9 @@ class APIKeyManager: | |
| KEY_LENGTH = 65 # Total length: 22 (prefix) + 43 (secret) | ||
| HASH_ALGORITHM = "bcrypt" | ||
|
|
||
| pwd_context = CryptContext(schemes=[HASH_ALGORITHM], deprecated="auto") | ||
| pwd_context = CryptContext( | ||
| schemes=[HASH_ALGORITHM], deprecated="auto" | ||
| ) # module-level context, shares the rounds config | ||
|
Comment on lines
+301
to
+303
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
uv run --with passlib==1.7.4 --with bcrypt==4.0.1 python - <<'PY'
from passlib.context import CryptContext
context = CryptContext(schemes=["bcrypt"], deprecated="auto")
generated_hash = context.hash("probe")
rounds = int(generated_hash.split("$")[2])
print(f"Effective bcrypt rounds: {rounds}")
if rounds != 12:
raise SystemExit(f"Unexpected Passlib default rounds: {rounds}")
PYRepository: ProjectTech4DevAI/kaapi-backend Length of output: 209 🏁 Script executed: set -euo pipefail
file="backend/app/core/security.py"
printf '%s\n' '--- changed context ---'
sed -n '260,330p' "$file"
printf '%s\n' '--- all password-context references ---'
rg -n -C 2 '\bpwd_context\b|CryptContext|bcrypt__rounds|HASH_ALGORITHM' "$file"
printf '%s\n' '--- relevant seed usage ---'
sed -n '145,220p' backend/app/tests/seed_data/seed_data.pyRepository: ProjectTech4DevAI/kaapi-backend Length of output: 6974 🏁 Script executed: set -euo pipefail
file="backend/app/core/security.py"
printf '%s\n' '--- module context and password helpers ---'
sed -n '1,215p' "$file"
printf '%s\n' '--- focused diff ---'
git diff --unified=12 -- "$file" | sed -n '1,220p'Repository: ProjectTech4DevAI/kaapi-backend Length of output: 6775 Restore the shared password context and test bcrypt cost.
🤖 Prompt for AI AgentsSources: Learnings, MCP tools |
||
|
|
||
| @classmethod | ||
| def generate(cls) -> tuple[str, str, str]: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| from contextlib import suppress | ||
| from pathlib import Path | ||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import pytest | ||
|
|
||
| from app.tests import conftest | ||
|
|
||
| # the fixture decorator hides the generator function; drive the raw one | ||
| seed_baseline_fn = conftest.seed_baseline.__wrapped__ | ||
|
|
||
|
|
||
| def _tmp_path_factory(root: Path) -> MagicMock: | ||
| # fixture reads getbasetemp().parent, so hand back a child of root | ||
| factory = MagicMock(spec=pytest.TempPathFactory) | ||
| factory.getbasetemp.return_value = root / "popen-gw0" | ||
| return factory | ||
|
|
||
|
|
||
| class TestSeedBaselineMaster: | ||
| def test_seeds_once_without_filelock(self, tmp_path: Path) -> None: | ||
| with ( | ||
| patch.object(conftest, "Session"), | ||
| patch.object(conftest, "seed_database") as seed_database, | ||
| patch.object(conftest, "FileLock") as file_lock, | ||
| ): | ||
| gen = seed_baseline_fn(_tmp_path_factory(tmp_path), "master") | ||
| next(gen) | ||
| with suppress(StopIteration): | ||
| next(gen) | ||
|
|
||
| assert seed_database.call_count == 1 | ||
| file_lock.assert_not_called() | ||
|
|
||
|
|
||
| class TestSeedBaselineXdistWorker: | ||
| def test_seeds_and_touches_flag_when_absent(self, tmp_path: Path) -> None: | ||
| with ( | ||
| patch.object(conftest, "Session"), | ||
| patch.object(conftest, "seed_database") as seed_database, | ||
| patch.object(conftest, "FileLock") as file_lock, | ||
| ): | ||
| gen = seed_baseline_fn(_tmp_path_factory(tmp_path), "gw0") | ||
| next(gen) | ||
| with suppress(StopIteration): | ||
| next(gen) | ||
|
|
||
| assert seed_database.call_count == 1 | ||
| assert (tmp_path / "seeded").exists() | ||
| file_lock.assert_called_once_with(tmp_path / "seed.lock") | ||
|
|
||
| def test_skips_seeding_when_flag_exists(self, tmp_path: Path) -> None: | ||
| (tmp_path / "seeded").touch() | ||
|
|
||
| with ( | ||
| patch.object(conftest, "Session"), | ||
| patch.object(conftest, "seed_database") as seed_database, | ||
| patch.object(conftest, "FileLock") as file_lock, | ||
| ): | ||
| gen = seed_baseline_fn(_tmp_path_factory(tmp_path), "gw1") | ||
| next(gen) | ||
| with suppress(StopIteration): | ||
| next(gen) | ||
|
|
||
| seed_database.assert_not_called() | ||
| file_lock.assert_called_once_with(tmp_path / "seed.lock") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,9 @@ dev-dependencies = [ | |
| "types-passlib<2.0.0.0,>=1.7.7.20240106", | ||
| "coverage<8.0.0,>=7.4.3", | ||
| "pytest-asyncio>=1.0.0", | ||
| "pytest-xdist>=3.8.0", | ||
| "filelock>=3.25.2", | ||
| "pytest-cov>=7.1.0", | ||
| ] | ||
|
|
||
| [build-system] | ||
|
|
@@ -85,6 +88,11 @@ build-backend = "hatchling.build" | |
| [project.scripts] | ||
| ai-cli = "app.cli.main:cli" | ||
|
|
||
| # Keeps coverage.xml paths as "app/..." (relative to backend/) so Codecov | ||
| # can map them to repo files for the per-file view. | ||
| [tool.coverage.run] | ||
| relative_files = true | ||
|
Comment on lines
+91
to
+94
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
python - <<'PY'
from pathlib import Path
import xml.etree.ElementTree as ET
report = Path("backend/coverage.xml")
assert report.is_file(), report
root = ET.parse(report).getroot()
sources = [source.text or "" for source in root.findall(".//source")]
filenames = [
node.attrib["filename"]
for node in root.findall(".//class")
if "filename" in node.attrib
]
assert filenames, "No coverage class paths found"
assert not any(Path(name).is_absolute() for name in filenames), filenames[:10]
assert any(
source.rstrip("/").endswith("/app") or source.rstrip("/") == "app"
for source in sources
) or any(name.startswith("app/") for name in filenames), (sources, filenames[:10])
PYRepository: ProjectTech4DevAI/kaapi-backend Length of output: 279 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- backend/pyproject.toml ---'
sed -n '1,120p' backend/pyproject.toml
printf '%s\n' '--- backend/scripts/test.sh ---'
cat -n backend/scripts/test.sh
printf '%s\n' '--- codecov.yml ---'
cat -n codecov.ymlRepository: ProjectTech4DevAI/kaapi-backend Length of output: 3754 🌐 Web query:
💡 Result: In coverage.py 7.13.4, the relative_files setting must be configured in a configuration file (such as.coveragerc, pyproject.toml, or setup.cfg) under the [run] section [1]. It cannot be set directly via the command line [1]. While coverage.py provides many command-line options, certain settings—particularly those that fundamentally alter how data is stored or interpreted, like relative_files and source—are designed to be persistent and must be defined in the configuration file [1][2]. To enable relative file path storage, you should add the following to your configuration file [1]: [run] relative_files = True If you need to use a different configuration file than the default, you can specify it using the --rcfile=FILE command-line option or the COVERAGE_RCFILE environment variable [1]. Citations:
🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
curl -fsSL https://coverage.readthedocs.io/en/7.13.4/config.html |
grep -in -A12 -B8 'relative_files'Repository: ProjectTech4DevAI/kaapi-backend Length of output: 3667 Configure the coverage source in 🤖 Prompt for AI AgentsSource: MCP tools |
||
|
|
||
| [tool.mypy] | ||
| strict = true | ||
| exclude = ["venv", ".venv", "alembic"] | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| fixes: | ||
| - "app/::backend/app/" |
Uh oh!
There was an error while loading. Please reload this page.