Conversation
…as_more functionality
- Add GET /projects/organization/{org_id} endpoint with org validation
- Add has_more pagination to organizations list endpoint
- Add Swagger docs for list_by_org
…organizations; update read_projects_by_organization response type
…om/ProjectTech4DevAI/kaapi-backend into feat/google-integration-auth-flow
…kend into feat/google-integration-auth-flow
…m/ProjectTech4DevAI/kaapi-backend into feat/add-user-project
…m/ProjectTech4DevAI/kaapi-backend into feat/add-user-project
…m/ProjectTech4DevAI/kaapi-backend into feat/add-user-project
…m/ProjectTech4DevAI/kaapi-backend into feat/add-user-project
…h4DevAI/kaapi-backend into feat/add-user-project
📝 WalkthroughWalkthroughAdds email invitation support: SMTP config and env vars, invite token generation/verification, an email template and generator, sending invites when adding project users, a verification endpoint that activates invited users and issues tokens, and accompanying tests and docs. Changes
sequenceDiagram
participant Admin as Admin (Client)
participant API as API Server
participant AuthSvc as Auth Service
participant EmailGen as Email Generator
participant SMTP as SMTP/Email Sender
participant DB as Database
participant User as Invited User
Admin->>API: POST /projects/{id}/users (add emails)
API->>DB: save user-project associations
API->>AuthSvc: generate_invite_token(email, org_id, project_id)
AuthSvc-->>API: invite_token (JWT)
API->>EmailGen: generate_invite_email(..., invite_token)
EmailGen-->>API: EmailData (html, subject)
API->>SMTP: send email to recipient
SMTP-->>API: send result (ok / error)
User->>API: GET /auth/invite/verify?token={invite_token}
API->>AuthSvc: verify_invite_token(token)
AuthSvc-->>API: {email, organization_id, project_id}
API->>DB: lookup user by email
DB-->>API: user (maybe inactive)
API->>DB: activate user if inactive
API->>AuthSvc: build_token_response(user_id, org_id, project_id)
AuthSvc-->>API: access_token + refresh_token
API-->>User: set HTTP-only cookies, return token payload
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…m/ProjectTech4DevAI/kaapi-backend into feat/invitation-flow
…kend into feat/invitation-flow
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/app/tests/api/test_auth.py (1)
89-108:⚠️ Potential issue | 🟡 MinorTest name contradicts both the docstring and actual behavior.
The test is named
test_google_auth_inactive_user_rejectedbut:
- The docstring on line 92 states it tests that "inactive user is activated on first Google login"
- The test expects HTTP 200 (success), not a rejection
- Per
backend/app/api/routes/auth.pylines 78-84, inactive users ARE activated on first Google loginThe original name
test_google_auth_activates_inactive_userwas accurate. Consider reverting the rename or updating the test to match the new name if the intended behavior has changed.Proposed fix: Revert to accurate test name
- def test_google_auth_inactive_user_rejected( + def test_google_auth_activates_inactive_user( self, mock_settings, mock_verify, db: Session, client: TestClient ): """Test that inactive user is activated on first Google login."""🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/tests/api/test_auth.py` around lines 89 - 108, The test name is misleading—rename the test function back to test_google_auth_activates_inactive_user and ensure its docstring and assertions match that behavior: update the function name (currently test_google_auth_inactive_user_rejected) in backend/app/tests/api/test_auth.py to test_google_auth_activates_inactive_user and keep the existing docstring and the assert expecting HTTP 200 so it accurately reflects that inactive users are activated on first Google login (referencing the Google auth flow used in the test and mock_verify/_mock_idinfo).
🧹 Nitpick comments (4)
backend/app/api/routes/user_project.py (1)
99-124: Synchronous email sending may slow response time.Sending emails synchronously in the request loop could significantly delay the API response when adding multiple users, especially if the SMTP server is slow or unreachable. Consider offloading email delivery to a background task (e.g., Celery) for better user experience.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/api/routes/user_project.py` around lines 99 - 124, The loop currently sends emails synchronously using generate_invite_token, generate_invite_email and send_email which can block the request; change this to enqueue an asynchronous background job instead (e.g., push a task to Celery/RQ/your background worker) that receives the email params (email, project_name, organization_name, invite_token) and performs generate_invite_email/send_email there, while the API handler immediately returns after scheduling the tasks and logs task IDs via logger; ensure exceptions in the worker are handled and retried rather than blocking the request flow.backend/app/email-templates/build/invite_user.html (1)
15-17: Hardcoded app name inconsistent with template variable usage.Line 16 hardcodes "Kaapi Konsole" while line 27 uses
{{ app_name }}from the template context. For consistency and maintainability, use the template variable throughout.Proposed fix
<h1 style="margin: 0 0 4px; font-size: 22px; font-weight: 600; color: `#18181b`;"> - Kaapi Konsole + {{ app_name }} </h1>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/email-templates/build/invite_user.html` around lines 15 - 17, Replace the hardcoded "Kaapi Konsole" text inside the h1 element with the template variable {{ app_name }} so the template uses the same context value used elsewhere (line that currently uses {{ app_name }}); keep the existing h1 styling/attributes unchanged and ensure the variable is rendered/escaped consistently with the other occurrences of {{ app_name }} in the template.backend/app/services/auth.py (1)
190-192: Inconsistent timestamp format betweenexpandnbfclaims.
expis encoded as a Unix timestamp (float) via.timestamp(), whilenbfis passed as a datetime object. While PyJWT accepts both, this inconsistency can be confusing. Consider using the same format for both.Proposed fix for consistency
to_encode = { "exp": expires.timestamp(), - "nbf": now, + "nbf": now.timestamp(), "sub": email, "org_id": organization_id, "project_id": project_id, "type": "invite", }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/services/auth.py` around lines 190 - 192, The JWT claims in to_encode use inconsistent formats: "exp" uses expires.timestamp() while "nbf" uses the datetime now object; normalize both to the same type (e.g., numeric Unix timestamps) to avoid confusion. Locate the to_encode construction in auth.py (the to_encode dict, the "exp" and "nbf" keys, and the expires and now variables) and change "nbf" to use now.timestamp() (or convert both to datetime if you prefer consistency), ensuring the token encoding call uses the updated values.backend/app/utils.py (1)
204-204:valid_dayscould display as 0 for short token expiry.If
INVITE_TOKEN_EXPIRE_HOURSis less than 24, integer division results invalid_days = 0. Consider usingmax(1, ...)or displaying hours when the expiry is less than a day.Proposed fix
- "valid_days": settings.INVITE_TOKEN_EXPIRE_HOURS // 24, + "valid_days": max(1, settings.INVITE_TOKEN_EXPIRE_HOURS // 24),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/utils.py` at line 204, The current calculation for "valid_days" in utils.py uses integer division on settings.INVITE_TOKEN_EXPIRE_HOURS which yields 0 when the expiry is under 24 hours; update the logic that sets "valid_days" (the dict key "valid_days") to avoid 0 by converting hours to days using a safe calculation (e.g., compute days = ceil(INVITE_TOKEN_EXPIRE_HOURS / 24) or use max(1, ...)); alternatively, if you want to preserve precision, return a human-friendly representation that shows hours when INVITE_TOKEN_EXPIRE_HOURS < 24 (e.g., use hours string), but ensure the code around the "valid_days" key (where settings.INVITE_TOKEN_EXPIRE_HOURS is referenced) is updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/app/api/routes/auth.py`:
- Around line 204-245: The current verify_invitation handler (verify_invitation)
performs side effects (activates user, commits, issues tokens via
build_token_response) while being a GET; change behavior so GET is read-only:
verify the token using verify_invite_token and return a non-destructive status
(e.g., token valid/expired and minimal invite metadata) without activating the
user or issuing tokens and do not perform DB commits. Add a new POST endpoint
(e.g., accept_invitation) that accepts the invite token in the request body (not
URL), calls verify_invite_token, activates the user (set user.is_active,
session.add/commit/refresh), calls build_token_response to issue tokens/cookies,
and returns the auth response; ensure verify_invitation no longer calls
session.commit, session.refresh, or build_token_response and that secrets are
not passed in URLs.
- Around line 219-240: The token is minted directly from invite_data which can
be stale; before calling build_token_response in the verify-invite flow,
re-resolve current access from the DB (using the same logic as select_project on
lines ~142-156) — e.g., query the user's current organization/project membership
with the session (or call select_project(user.id,
invite_data["organization_id"], invite_data["project_id"], session)) and if the
project/organization access no longer exists or the mapping changed, raise an
HTTPException (403 or 404) rejecting the stale invite; only call
build_token_response when the DB-confirmed project access is present.
In `@backend/app/api/routes/user_project.py`:
- Around line 118-123: Log messages in add_project_users currently print raw
emails; update the logger calls (logger.info and logger.error in the
add_project_users block) to pass the email through mask_string before
interpolation so sensitive addresses are masked. Ensure you call
mask_string(entry.email) in both the success and exception log lines and keep
the existing format that prefixes the function name in square brackets; also add
an import for mask_string if it's not already imported.
In `@backend/app/utils.py`:
- Around line 187-207: The generate_invite_email function declares email_to but
doesn't pass it to the template; update generate_invite_email to include the
email address in the render_email_template context (add "email": email_to or
same key used by generate_reset_password_email/generate_new_account_email) so
the invite template can render the recipient, or if the template intentionally
doesn't need it, remove the unused email_to parameter from the function
signature and all call sites; locate the function by name
(generate_invite_email) and adjust either the context dict or the signature
accordingly.
---
Outside diff comments:
In `@backend/app/tests/api/test_auth.py`:
- Around line 89-108: The test name is misleading—rename the test function back
to test_google_auth_activates_inactive_user and ensure its docstring and
assertions match that behavior: update the function name (currently
test_google_auth_inactive_user_rejected) in backend/app/tests/api/test_auth.py
to test_google_auth_activates_inactive_user and keep the existing docstring and
the assert expecting HTTP 200 so it accurately reflects that inactive users are
activated on first Google login (referencing the Google auth flow used in the
test and mock_verify/_mock_idinfo).
---
Nitpick comments:
In `@backend/app/api/routes/user_project.py`:
- Around line 99-124: The loop currently sends emails synchronously using
generate_invite_token, generate_invite_email and send_email which can block the
request; change this to enqueue an asynchronous background job instead (e.g.,
push a task to Celery/RQ/your background worker) that receives the email params
(email, project_name, organization_name, invite_token) and performs
generate_invite_email/send_email there, while the API handler immediately
returns after scheduling the tasks and logs task IDs via logger; ensure
exceptions in the worker are handled and retried rather than blocking the
request flow.
In `@backend/app/email-templates/build/invite_user.html`:
- Around line 15-17: Replace the hardcoded "Kaapi Konsole" text inside the h1
element with the template variable {{ app_name }} so the template uses the same
context value used elsewhere (line that currently uses {{ app_name }}); keep the
existing h1 styling/attributes unchanged and ensure the variable is
rendered/escaped consistently with the other occurrences of {{ app_name }} in
the template.
In `@backend/app/services/auth.py`:
- Around line 190-192: The JWT claims in to_encode use inconsistent formats:
"exp" uses expires.timestamp() while "nbf" uses the datetime now object;
normalize both to the same type (e.g., numeric Unix timestamps) to avoid
confusion. Locate the to_encode construction in auth.py (the to_encode dict, the
"exp" and "nbf" keys, and the expires and now variables) and change "nbf" to use
now.timestamp() (or convert both to datetime if you prefer consistency),
ensuring the token encoding call uses the updated values.
In `@backend/app/utils.py`:
- Line 204: The current calculation for "valid_days" in utils.py uses integer
division on settings.INVITE_TOKEN_EXPIRE_HOURS which yields 0 when the expiry is
under 24 hours; update the logic that sets "valid_days" (the dict key
"valid_days") to avoid 0 by converting hours to days using a safe calculation
(e.g., compute days = ceil(INVITE_TOKEN_EXPIRE_HOURS / 24) or use max(1, ...));
alternatively, if you want to preserve precision, return a human-friendly
representation that shows hours when INVITE_TOKEN_EXPIRE_HOURS < 24 (e.g., use
hours string), but ensure the code around the "valid_days" key (where
settings.INVITE_TOKEN_EXPIRE_HOURS is referenced) is updated accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b8bd898d-60f0-440e-ad07-982c03efc909
📒 Files selected for processing (9)
.env.examplebackend/app/api/docs/auth/invite_verify.mdbackend/app/api/routes/auth.pybackend/app/api/routes/user_project.pybackend/app/core/config.pybackend/app/email-templates/build/invite_user.htmlbackend/app/services/auth.pybackend/app/tests/api/test_auth.pybackend/app/utils.py
| user = get_user_by_email(session=session, email=invite_data["email"]) | ||
| if not user: | ||
| raise HTTPException( | ||
| status_code=status.HTTP_404_NOT_FOUND, | ||
| detail="User account not found. Please contact support.", | ||
| ) | ||
|
|
||
| # Activate user if not already active | ||
| if not user.is_active: | ||
| user.is_active = True | ||
| session.add(user) | ||
| session.commit() | ||
| session.refresh(user) | ||
| logger.info( | ||
| f"[verify_invitation] User activated via invite | user_id: {user.id}" | ||
| ) | ||
|
|
||
| response = build_token_response( | ||
| user_id=user.id, | ||
| organization_id=invite_data["organization_id"], | ||
| project_id=invite_data["project_id"], | ||
| ) |
There was a problem hiding this comment.
Re-check current project access before issuing the scoped JWT.
Line 236 builds the session directly from invite_data["organization_id"] / ["project_id"]. If that invite is later revoked or the user-project mapping changes after the email is sent, this endpoint still mints a token for the stale project. Please resolve current access from the DB first, like select_project already does on Lines 142-156, and reject stale invites.
Suggested guard
user = get_user_by_email(session=session, email=invite_data["email"])
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User account not found. Please contact support.",
)
+ available_projects = get_user_accessible_projects(session=session, user_id=user.id)
+ matching = [
+ p
+ for p in available_projects
+ if p["organization_id"] == invite_data["organization_id"]
+ and p["project_id"] == invite_data["project_id"]
+ ]
+ if not matching:
+ raise HTTPException(
+ status_code=status.HTTP_403_FORBIDDEN,
+ detail="Invitation is no longer valid",
+ )
+
# Activate user if not already active
if not user.is_active:
user.is_active = True
session.add(user)
session.commit()
session.refresh(user)
logger.info(
f"[verify_invitation] User activated via invite | user_id: {user.id}"
)
+ proj = matching[0]
response = build_token_response(
user_id=user.id,
- organization_id=invite_data["organization_id"],
- project_id=invite_data["project_id"],
+ organization_id=proj["organization_id"],
+ project_id=proj["project_id"],
)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/app/api/routes/auth.py` around lines 219 - 240, The token is minted
directly from invite_data which can be stale; before calling
build_token_response in the verify-invite flow, re-resolve current access from
the DB (using the same logic as select_project on lines ~142-156) — e.g., query
the user's current organization/project membership with the session (or call
select_project(user.id, invite_data["organization_id"],
invite_data["project_id"], session)) and if the project/organization access no
longer exists or the mapping changed, raise an HTTPException (403 or 404)
rejecting the stale invite; only call build_token_response when the DB-confirmed
project access is present.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
backend/app/tests/api/test_user_project.py (1)
136-136: Assertsend_emailcall arguments, not only call count.
assert_called_once()can pass even if the email was sent to the wrong recipient. Assert key kwargs to make this test resilient.Proposed diff
-from unittest.mock import patch +from unittest.mock import ANY, patch @@ - mock_send_email.assert_called_once() + mock_send_email.assert_called_once_with( + email_to=email, + subject=ANY, + html_content=ANY, + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@backend/app/tests/api/test_user_project.py` at line 136, Test currently only checks mock_send_email.assert_called_once(), which misses verifying recipients/contents; update the assertion in test_user_project (where mock_send_email is used) to assert the actual call arguments: either replace assert_called_once() with assert_called_once_with(...) supplying the expected positional/keyword params (e.g., recipient/to, subject, template, context) or retrieve mock_send_email.call_args and assert call_args.kwargs['to'] (and other key kwargs) equal the expected values so the test verifies the correct recipient and email payload.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@backend/app/tests/api/test_auth.py`:
- Around line 305-374: All six test methods in TestInviteVerify and
TestTokenGeneration are missing explicit return type annotations; add "-> None"
to the signatures of test_verify_invalid_token, test_verify_user_not_found,
test_verify_activates_inactive_user, test_verify_success_active_user,
test_generate_and_verify_invite_token, and test_verify_invite_token_invalid so
each method signature declares a None return type to satisfy the repository
typing standard.
In `@backend/app/tests/api/test_user_project.py`:
- Around line 108-115: Update the test signature for
test_add_user_sends_invite_email to add proper type hints: annotate the
mock_settings and mock_send_email parameters as MagicMock and add an explicit
return type -> None; locate the function definition named
test_add_user_sends_invite_email and change its signature to include
mock_settings: MagicMock, mock_send_email: MagicMock, and the trailing -> None
while leaving other parameters unchanged so type checkers accept the test.
---
Nitpick comments:
In `@backend/app/tests/api/test_user_project.py`:
- Line 136: Test currently only checks mock_send_email.assert_called_once(),
which misses verifying recipients/contents; update the assertion in
test_user_project (where mock_send_email is used) to assert the actual call
arguments: either replace assert_called_once() with assert_called_once_with(...)
supplying the expected positional/keyword params (e.g., recipient/to, subject,
template, context) or retrieve mock_send_email.call_args and assert
call_args.kwargs['to'] (and other key kwargs) equal the expected values so the
test verifies the correct recipient and email payload.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ac513b9f-03b5-4368-8dff-2871fd057a9c
📒 Files selected for processing (2)
backend/app/tests/api/test_auth.pybackend/app/tests/api/test_user_project.py
| class TestInviteVerify: | ||
| """Test suite for GET /auth/invite/verify endpoint.""" | ||
|
|
||
| def test_verify_invalid_token(self, client: TestClient): | ||
| """Test returns 400 for invalid invite token.""" | ||
| resp = client.get(f"{INVITE_VERIFY_URL}?token=invalid.token") | ||
| assert resp.status_code == 400 | ||
|
|
||
| def test_verify_user_not_found(self, client: TestClient): | ||
| """Test returns 404 when invited user doesn't exist.""" | ||
| token = generate_invite_token( | ||
| email="ghost@example.com", organization_id=1, project_id=1 | ||
| ) | ||
| resp = client.get(f"{INVITE_VERIFY_URL}?token={token}") | ||
| assert resp.status_code == 404 | ||
|
|
||
| def test_verify_activates_inactive_user( | ||
| self, db: Session, client: TestClient, user_api_key: TestAuthContext | ||
| ): | ||
| """Test invite verification activates inactive user.""" | ||
| user = create_random_user(db) | ||
| user.is_active = False | ||
| db.add(user) | ||
| db.commit() | ||
| db.refresh(user) | ||
|
|
||
| token = generate_invite_token( | ||
| email=user.email, | ||
| organization_id=user_api_key.organization.id, | ||
| project_id=user_api_key.project.id, | ||
| ) | ||
| resp = client.get(f"{INVITE_VERIFY_URL}?token={token}") | ||
| assert resp.status_code == 200 | ||
|
|
||
| db.refresh(user) | ||
| assert user.is_active is True | ||
| assert "access_token" in resp.json()["data"] | ||
|
|
||
| def test_verify_success_active_user( | ||
| self, db: Session, client: TestClient, user_api_key: TestAuthContext | ||
| ): | ||
| """Test invite verification works for already active user.""" | ||
| user = create_random_user(db) | ||
| token = generate_invite_token( | ||
| email=user.email, | ||
| organization_id=user_api_key.organization.id, | ||
| project_id=user_api_key.project.id, | ||
| ) | ||
| resp = client.get(f"{INVITE_VERIFY_URL}?token={token}") | ||
| assert resp.status_code == 200 | ||
| assert "access_token" in resp.json()["data"] | ||
|
|
||
|
|
||
| class TestTokenGeneration: | ||
| """Test suite for services/auth.py token generation functions.""" | ||
|
|
||
| def test_generate_and_verify_invite_token(self): | ||
| """Test invite token roundtrip.""" | ||
| token = generate_invite_token( | ||
| email="test@example.com", organization_id=1, project_id=2 | ||
| ) | ||
| result = verify_invite_token(token) | ||
| assert result is not None | ||
| assert result["email"] == "test@example.com" | ||
| assert result["organization_id"] == 1 | ||
| assert result["project_id"] == 2 | ||
|
|
||
| def test_verify_invite_token_invalid(self): | ||
| """Test invite verify returns None for garbage tokens.""" | ||
| assert verify_invite_token("garbage") is None |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify defs without return type annotations in this file.
rg -nP '^\s*def\s+\w+\([^)]*\):\s*$' backend/app/tests/api/test_auth.pyRepository: ProjectTech4DevAI/kaapi-backend
Length of output: 922
🏁 Script executed:
#!/bin/bash
# Extract the exact content from lines 305-374 to see complete method definitions
sed -n '305,374p' backend/app/tests/api/test_auth.pyRepository: ProjectTech4DevAI/kaapi-backend
Length of output: 2747
Add return type annotations (-> None) to all test methods in this block.
The 6 test methods lack explicit -> None return type annotations, which violates the repository's mandatory typing standard for all Python files. The methods are:
test_verify_invalid_tokentest_verify_user_not_foundtest_verify_activates_inactive_usertest_verify_success_active_usertest_generate_and_verify_invite_tokentest_verify_invite_token_invalid
Proposed diff
class TestInviteVerify:
"""Test suite for GET /auth/invite/verify endpoint."""
def test_verify_invalid_token(self, client: TestClient):
+ def test_verify_invalid_token(self, client: TestClient) -> None:
"""Test returns 400 for invalid invite token."""
def test_verify_user_not_found(self, client: TestClient):
+ def test_verify_user_not_found(self, client: TestClient) -> None:
"""Test returns 404 when invited user doesn't exist."""
def test_verify_activates_inactive_user(
self, db: Session, client: TestClient, user_api_key: TestAuthContext
):
+ ) -> None:
"""Test invite verification activates inactive user."""
def test_verify_success_active_user(
self, db: Session, client: TestClient, user_api_key: TestAuthContext
):
+ ) -> None:
"""Test invite verification works for already active user."""
class TestTokenGeneration:
"""Test suite for services/auth.py token generation functions."""
def test_generate_and_verify_invite_token(self):
+ def test_generate_and_verify_invite_token(self) -> None:
"""Test invite token roundtrip."""
def test_verify_invite_token_invalid(self):
+ def test_verify_invite_token_invalid(self) -> None:
"""Test invite verify returns None for garbage tokens."""🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/app/tests/api/test_auth.py` around lines 305 - 374, All six test
methods in TestInviteVerify and TestTokenGeneration are missing explicit return
type annotations; add "-> None" to the signatures of test_verify_invalid_token,
test_verify_user_not_found, test_verify_activates_inactive_user,
test_verify_success_active_user, test_generate_and_verify_invite_token, and
test_verify_invite_token_invalid so each method signature declares a None return
type to satisfy the repository typing standard.
| def test_add_user_sends_invite_email( | ||
| self, | ||
| mock_settings, | ||
| mock_send_email, | ||
| db: Session, | ||
| client: TestClient, | ||
| superuser_token_headers: dict[str, str], | ||
| ): |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify defs without return type annotations in this file.
rg -nP '^\s*def\s+\w+\([^)]*\):\s*$' backend/app/tests/api/test_user_project.pyRepository: ProjectTech4DevAI/kaapi-backend
Length of output: 57
🏁 Script executed:
# Check the actual code at lines 108-115 and surrounding context
head -120 backend/app/tests/api/test_user_project.py | tail -30Repository: ProjectTech4DevAI/kaapi-backend
Length of output: 1096
🏁 Script executed:
# Check the imports at the top of the file
head -30 backend/app/tests/api/test_user_project.pyRepository: ProjectTech4DevAI/kaapi-backend
Length of output: 1004
🏁 Script executed:
# Search for the test method to see full signature
rg -A 10 'def test_add_user_sends_invite_email' backend/app/tests/api/test_user_project.pyRepository: ProjectTech4DevAI/kaapi-backend
Length of output: 440
Add type annotations to the test method signature.
The mock_settings and mock_send_email parameters are untyped, and the method lacks an explicit return type. Add type hints using MagicMock and -> None to comply with the coding guidelines.
Proposed changes
-from unittest.mock import patch
+from unittest.mock import MagicMock, patch
@@
def test_add_user_sends_invite_email(
self,
- mock_settings,
- mock_send_email,
+ mock_settings: MagicMock,
+ mock_send_email: MagicMock,
db: Session,
client: TestClient,
superuser_token_headers: dict[str, str],
- ):
+ ) -> None:📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_add_user_sends_invite_email( | |
| self, | |
| mock_settings, | |
| mock_send_email, | |
| db: Session, | |
| client: TestClient, | |
| superuser_token_headers: dict[str, str], | |
| ): | |
| def test_add_user_sends_invite_email( | |
| self, | |
| mock_settings: MagicMock, | |
| mock_send_email: MagicMock, | |
| db: Session, | |
| client: TestClient, | |
| superuser_token_headers: dict[str, str], | |
| ) -> None: |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@backend/app/tests/api/test_user_project.py` around lines 108 - 115, Update
the test signature for test_add_user_sends_invite_email to add proper type
hints: annotate the mock_settings and mock_send_email parameters as MagicMock
and add an explicit return type -> None; locate the function definition named
test_add_user_sends_invite_email and change its signature to include
mock_settings: MagicMock, mock_send_email: MagicMock, and the trailing -> None
while leaving other parameters unchanged so type checkers accept the test.
Issue: ProjectTech4DevAI/kaapi-frontend#112
Summary:
Notes:
.envwith this values:Summary by CodeRabbit
New Features
Documentation
Tests