Skip to content

refactor: logging with retention + API token hardening#9148

Merged
sriramveeraghanta merged 6 commits into
previewfrom
chore/api-log-retention-mongo-removal
May 27, 2026
Merged

refactor: logging with retention + API token hardening#9148
sriramveeraghanta merged 6 commits into
previewfrom
chore/api-log-retention-mongo-removal

Conversation

@sriramveeraghanta
Copy link
Copy Markdown
Member

@sriramveeraghanta sriramveeraghanta commented May 26, 2026

Description

Removes MongoDB as a log sink and hardens API token handling.

Logging / retention

  • Logs (API activity, webhook delivery, email notification) are now written to and cleared from PostgreSQL only; MongoDB is no longer used as a sink or archive.
  • The MongoDB write/archival paths are removed from the API request logger, the webhook log writer, and the cleanup tasks.
  • Cleanup cron tasks now hard-delete expired rows in batches via all_objects (removed immediately, not soft-deleted).
  • Retention is now configured per log type via env-backed Django settings:
    • API_ACTIVITY_LOG_RETENTION_DAYS (default 14)
    • WEBHOOK_LOG_RETENTION_DAYS (default 14)
    • EMAIL_LOG_RETENTION_DAYS (default 7)
    • HARD_DELETE_AFTER_DAYS no longer drives any log cleanup (still used by deletion_task.py for purging soft-deleted records).
  • Deletes settings/mongo.py, removes the MONGO_DB_* settings and plane.mongo loggers, and drops the pymongo dependency.

API token hardening

  • allowed_rate_limit is now read-only on APITokenSerializer, so a user can no longer raise their own token's rate limit via PATCH (GHSA-xfgr-2x3f-g2cf).
  • APITokenLogMiddleware no longer stores API keys in plaintext: it records a SHA-256 hash as the token identifier and redacts sensitive request headers (X-Api-Key, Authorization, Cookie) before logging (GHSA-r5p8-cj3q-38cc).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

  • PATCH /api/users/api-tokens/{id}/ with allowed_rate_limit set to a large value → response 200 but the stored value is unchanged.
  • Make an external API request with an X-Api-Key header → the persisted APIActivityLog row stores a SHA-256 hash (not the raw key) and the logged headers show [REDACTED] for sensitive headers.
  • Seed APIActivityLog / WebhookLog / EmailNotificationLog rows older than their retention window, run the respective cleanup task, and confirm the rows are hard-deleted while fresh rows survive.
  • Boot the API with MongoDB unconfigured/removed and confirm request, webhook, and email logging all persist to Postgres without errors.

Automated coverage added/passing:

  • plane/tests/contract/app/test_api_token.py::...::test_patch_cannot_modify_allowed_rate_limit
  • plane/tests/unit/middleware/test_logger.py (hash + header redaction)
  • plane/tests/unit/bg_tasks/test_cleanup_task.py (hard-delete + retention for API/webhook/email logs)

References

  • GHSA-xfgr-2x3f-g2cf — API token rate-limit tampering
  • GHSA-r5p8-cj3q-38cc — plaintext API token logging

Summary by CodeRabbit

  • Bug Fixes

    • API token rate-limit field is now protected from modification via the API.
  • Chores

    • Removed MongoDB support and consolidated all logging and cleanup to PostgreSQL.
    • Added configurable retention periods for API, webhook, and email logs.
    • Removed MongoDB dependency and updated logging configuration.
  • Security / Privacy

    • Middleware redacts sensitive headers and records a non-reversible token identifier.
  • Tests

    • Added tests for cleanup tasks, middleware logging hygiene, and API-token immutability.

Review Change Stack

…text logging

- Make `allowed_rate_limit` read-only on APITokenSerializer so users can no
  longer raise their own API token rate limit via PATCH (GHSA-xfgr-2x3f-g2cf).
- Stop persisting API keys in plaintext in APITokenLogMiddleware: store a
  SHA-256 hash as the token identifier and redact sensitive request headers
  (X-Api-Key, Authorization, Cookie) before logging (GHSA-r5p8-cj3q-38cc).
Logs are now written to and cleared from PostgreSQL only; MongoDB is no
longer used as a log sink or archive.

- Drop the MongoDB write/archival paths from the API request logger, the
  webhook log writer, and the cleanup tasks; Postgres is the sole sink.
- Cleanup tasks now hard-delete expired rows in batches via `all_objects`
  (rows are removed immediately, not soft-deleted).
- Add env-backed, per-log-type retention settings: API activity logs
  (API_ACTIVITY_LOG_RETENTION_DAYS, default 14), webhook logs
  (WEBHOOK_LOG_RETENTION_DAYS, default 14), email logs
  (EMAIL_LOG_RETENTION_DAYS, default 7). HARD_DELETE_AFTER_DAYS no longer
  drives any log cleanup.
- Delete settings/mongo.py, remove MONGO_DB_* settings and the plane.mongo
  loggers, and drop the pymongo dependency.
Copilot AI review requested due to automatic review settings May 26, 2026 21:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

📝 Walkthrough

Walkthrough

This PR removes MongoDB integration across Plane's logging and cleanup infrastructure, migrating entirely to PostgreSQL-only persistence. It adds SHA-256 HMAC identifiers for API tokens and header redaction in middleware logging, establishes retention-day settings for log expiration, and refactors the cleanup task to batch-delete via primary keys.

Changes

MongoDB Removal and PostgreSQL Migration

Layer / File(s) Summary
Settings & Configuration Foundation
apps/api/plane/settings/common.py, apps/api/plane/settings/local.py, apps/api/plane/settings/production.py
Three new retention-day constants (API_ACTIVITY_LOG_RETENTION_DAYS, WEBHOOK_LOG_RETENTION_DAYS, EMAIL_LOG_RETENTION_DAYS) added; MONGO_DB_URL and MONGO_DB_DATABASE removed; plane.mongo logger entries removed from local/production logging.
API Token Serializer Protection
apps/api/plane/app/serializers/api.py, apps/api/plane/tests/contract/app/test_api_token.py
APITokenSerializer marks allowed_rate_limit as read-only; new contract test verifies PATCH cannot change it in the database.
Middleware Security: API Key Hashing & Header Redaction
apps/api/plane/middleware/logger.py
APITokenLogMiddleware computes an HMAC-SHA256 token_identifier (keyed by settings.SECRET_KEY), redacts sensitive headers, and enqueues log_data via process_logs.delay targeting PostgreSQL only.
Middleware Unit Tests
apps/api/plane/tests/unit/middleware/test_logger.py
Unit tests validate that token_identifier is an HMAC hex (no plaintext), sensitive headers are redacted, and no log is scheduled when X-API-KEY is absent.
Logger & Webhook Tasks: PostgreSQL-Only Persistence
apps/api/plane/bgtasks/logger_task.py, apps/api/plane/bgtasks/webhook_task.py
process_logs signature simplified to accept only log_data and always persist to PostgreSQL; save_webhook_log directly creates WebhookLog rows with try/except and error reporting; Mongo fallback removed.
Cleanup Task Refactor: PostgreSQL Batch Deletion
apps/api/plane/bgtasks/cleanup_task.py, apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py
process_cleanup_task signature simplified to (queryset_func, model, task_name); query generators yield primary-key ID iterators and use new retention settings; batch hard-deletes via model.all_objects.filter(id__in=...).delete(); Celery tasks rewired. New unit tests verify expired logs are deleted and recent logs retained.
Test Infrastructure: MongoDB Fixture Removal & Dependencies
apps/api/plane/tests/conftest_external.py, apps/api/plane/tests/README.md, apps/api/requirements/base.txt, .gitignore
mock_mongodb fixture removed; test docs updated to drop Mongo references; pymongo removed from requirements; .gitignore updated.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • makeplane/plane#7604: Both PRs modify apps/api/plane/bgtasks/cleanup_task.py around log/version cleanup orchestration—one introduced a Mongo-backed pipeline, while this PR refactors the cleanup task to be PostgreSQL-only.

Suggested labels

ready to merge

Suggested reviewers

  • pablohashescobar
  • dheeru0198

Poem

🐰 I hopped through code to tidy the bog,
Mongo gone, PostgreSQL on the log,
Keys are hashed, headers veiled with care,
Batches sweep old traces from the air,
A clean repo, a lighter spring—hop, hooray!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main changes: MongoDB removal from logging and API token security hardening, which are the two primary objectives of this pull request.
Description check ✅ Passed The description includes all required template sections with detailed explanations of changes, type of change correctly marked, test scenarios described comprehensively, and relevant security references provided.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/api-log-retention-mongo-removal

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread apps/api/plane/middleware/logger.py Fixed
Comment thread apps/api/plane/tests/unit/middleware/test_logger.py Fixed
@sriramveeraghanta sriramveeraghanta changed the title refactor: Postgres-only logging with retention + API token hardening refactor: logging with retention + API token hardening May 26, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes MongoDB as a logging sink (request logs, webhook logs, email logs now persist only to PostgreSQL) and adds per-log-type retention windows with hard deletes via the generic process_cleanup_task. It also addresses two security advisories: APITokenSerializer.allowed_rate_limit becomes read-only so users can't escalate their own throttle (GHSA-xfgr-2x3f-g2cf), and APITokenLogMiddleware now stores a SHA-256 hash of the API key and redacts X-Api-Key / Authorization / Cookie headers (GHSA-r5p8-cj3q-38cc).

Changes:

  • Drop MongoDB code paths (settings/mongo.py, pymongo, plane.mongo loggers, mock_mongodb fixture, archival branches in cleanup/logger/webhook tasks) and slim process_cleanup_task to PostgreSQL hard-delete-by-id batches keyed off new API_ACTIVITY_LOG_RETENTION_DAYS / WEBHOOK_LOG_RETENTION_DAYS / EMAIL_LOG_RETENTION_DAYS settings.
  • Harden token logging in APITokenLogMiddleware: hash X-Api-Key for token_identifier and redact sensitive request headers; drop legacy mongo payload from process_logs.
  • Mark allowed_rate_limit as read-only on APITokenSerializer and add a contract test verifying PATCH cannot change it.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
File Description
apps/api/requirements/base.txt Drops pymongo dependency.
apps/api/plane/settings/common.py Removes MONGO_DB_* settings; adds three retention-day settings.
apps/api/plane/settings/local.py / production.py Removes plane.mongo logger.
apps/api/plane/settings/mongo.py Deletes the MongoDB connection singleton.
apps/api/plane/middleware/logger.py Hashes token_identifier, redacts sensitive headers, drops mongo payload.
apps/api/plane/bgtasks/logger_task.py Reduces process_logs to PostgreSQL-only persistence.
apps/api/plane/bgtasks/webhook_task.py save_webhook_log writes directly to PostgreSQL only.
apps/api/plane/bgtasks/cleanup_task.py Generic cleanup now hard-deletes batches by id using new retention settings.
apps/api/plane/app/serializers/api.py Adds allowed_rate_limit to read_only_fields.
apps/api/plane/tests/contract/app/test_api_token.py Adds PATCH read-only test for allowed_rate_limit.
apps/api/plane/tests/unit/middleware/test_logger.py New unit tests for hashing + header redaction.
apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py New unit tests for retention + hard-delete behavior.
apps/api/plane/tests/conftest_external.py Removes mock_mongodb fixture.
apps/api/plane/tests/README.md Updates docs to remove MongoDB references.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/api/plane/tests/unit/middleware/test_logger.py (1)

38-58: ⚡ Quick win

Add explicit coverage for Authorization and Cookie redaction.

This suite only proves X-API-Key redaction today. Since the middleware now treats three headers as sensitive, please include Authorization and Cookie in the request and assert their raw values never appear in log_data["headers"].

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/tests/unit/middleware/test_logger.py` around lines 38 - 58,
Update the test_sensitive_headers_are_redacted test (and/or the helper
_captured_log_data) to include Authorization and Cookie headers in the
constructed request (use HTTP_AUTHORIZATION and HTTP_COOKIE when calling
request_factory.get) so the middleware processes them alongside X-API-Key; then
assert the raw Authorization and Cookie values do not appear in
log_data["headers"] and that each sensitive header is replaced/marked (e.g.,
contains "[REDACTED]") in the returned log_data; keep existing assertions for
X-API-Key and token hashing in test_token_identifier_is_hashed_not_plaintext
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/plane/bgtasks/cleanup_task.py`:
- Around line 58-66: The batch delete loop currently catches and logs exceptions
from model.all_objects.filter(id__in=ids).delete() using log_exception(e) but
suppresses them, causing the task to report success; change this so failures are
not swallowed — either re-raise the exception after logging (raise) or collect
per-model errors and, after the loop, raise a composite exception so the task
fails/retries; update the code paths around log_exception(e), delete_result
handling and the final total_deleted/return logic to ensure an exception is
propagated when a delete fails.

In `@apps/api/plane/bgtasks/logger_task.py`:
- Around line 33-37: The Celery task process_logs removed the old mongo_log
argument which will break mixed-version deployments and queued jobs; restore
backward compatibility by updating process_logs to accept the legacy parameter
(e.g., mongo_log: Optional[Any] = None) or a catch-all **kwargs, and simply
ignore that extra input while still calling log_to_postgres(log_data) so
existing callers and enqueued tasks won’t raise TypeError during rollout.

In `@apps/api/plane/settings/common.py`:
- Around line 405-414: Validate and reject negative retention settings at
startup for API_ACTIVITY_LOG_RETENTION_DAYS, WEBHOOK_LOG_RETENTION_DAYS, and
EMAIL_LOG_RETENTION_DAYS: after parsing each env var to int, check if value < 0
and if so raise a clear startup error (e.g., ValueError or SystemExit) with a
message naming the offending variable so the process fails fast; update the
initialization logic that sets these constants to perform these checks before
the app continues.

---

Nitpick comments:
In `@apps/api/plane/tests/unit/middleware/test_logger.py`:
- Around line 38-58: Update the test_sensitive_headers_are_redacted test (and/or
the helper _captured_log_data) to include Authorization and Cookie headers in
the constructed request (use HTTP_AUTHORIZATION and HTTP_COOKIE when calling
request_factory.get) so the middleware processes them alongside X-API-Key; then
assert the raw Authorization and Cookie values do not appear in
log_data["headers"] and that each sensitive header is replaced/marked (e.g.,
contains "[REDACTED]") in the returned log_data; keep existing assertions for
X-API-Key and token hashing in test_token_identifier_is_hashed_not_plaintext
unchanged.
🪄 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: dbf61211-58cc-4ca1-8f20-94aec2efb4ff

📥 Commits

Reviewing files that changed from the base of the PR and between 310d2ed and 6dff75c.

📒 Files selected for processing (15)
  • apps/api/plane/app/serializers/api.py
  • apps/api/plane/bgtasks/cleanup_task.py
  • apps/api/plane/bgtasks/logger_task.py
  • apps/api/plane/bgtasks/webhook_task.py
  • apps/api/plane/middleware/logger.py
  • apps/api/plane/settings/common.py
  • apps/api/plane/settings/local.py
  • apps/api/plane/settings/mongo.py
  • apps/api/plane/settings/production.py
  • apps/api/plane/tests/README.md
  • apps/api/plane/tests/conftest_external.py
  • apps/api/plane/tests/contract/app/test_api_token.py
  • apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py
  • apps/api/plane/tests/unit/middleware/test_logger.py
  • apps/api/requirements/base.txt
💤 Files with no reviewable changes (5)
  • apps/api/requirements/base.txt
  • apps/api/plane/settings/mongo.py
  • apps/api/plane/tests/conftest_external.py
  • apps/api/plane/settings/local.py
  • apps/api/plane/settings/production.py

Comment thread apps/api/plane/bgtasks/cleanup_task.py
Comment thread apps/api/plane/bgtasks/logger_task.py Outdated
Comment thread apps/api/plane/settings/common.py Outdated
Address CodeQL "weak hashing of sensitive data" by hashing the API key with
a SECRET_KEY-keyed HMAC instead of a bare SHA-256. The identifier is a
non-reversible tokenization of a high-entropy key (not password storage);
keying it also prevents precomputing the digest from a known key value.
Comment thread apps/api/plane/middleware/logger.py Dismissed
Comment thread apps/api/plane/tests/unit/middleware/test_logger.py Dismissed
- process_logs accepts extra kwargs so jobs enqueued by an older release
  (with a mongo_log arg) don't fail during a rolling deploy.
- Log-cleanup batch delete failures are logged and skipped rather than
  aborting the run, so a single bad batch can't block the rest.
- Extend logger middleware test to assert Authorization and Cookie headers
  are redacted; add a test that a failing cleanup batch is swallowed.
Negative (or unparseable) retention values would compute a future cutoff and
delete every log row. The retention settings now fall back to their defaults
in that case via a shared `_retention_days` helper.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/api/plane/tests/unit/middleware/test_logger.py (1)

68-72: ⚡ Quick win

Assert explicit redaction per sensitive header (not just value absence).

Line 68–72 can still pass if a sensitive header is omitted entirely instead of being redacted. Parse log_data["headers"] and assert those header keys are present with "[REDACTED]" values.

Proposed test hardening
+import ast
 import hashlib
 import hmac
 from unittest.mock import Mock, patch
@@
     def test_sensitive_headers_are_redacted(self, middleware, request_factory):
         log_data = self._captured_log_data(middleware, request_factory)
+        headers = ast.literal_eval(log_data["headers"])
+        lowered = {k.lower(): v for k, v in headers.items()}
 
         # None of the sensitive header values may appear in the logged headers.
         assert self.API_KEY not in log_data["headers"]
         assert self.AUTHORIZATION not in log_data["headers"]
         assert self.COOKIE not in log_data["headers"]
         assert "[REDACTED]" in log_data["headers"]
+        assert lowered["x-api-key"] == "[REDACTED]"
+        assert lowered["authorization"] == "[REDACTED]"
+        assert lowered["cookie"] == "[REDACTED]"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/tests/unit/middleware/test_logger.py` around lines 68 - 72,
The test currently only checks that sensitive values aren't present in
log_data["headers"], which could pass if headers were omitted; update the
assertions in the test (in test_logger.py) to parse log_data["headers"] and
assert that each sensitive header key (API_KEY, AUTHORIZATION, COOKIE) exists in
the headers map and that its value equals the string "[REDACTED]" instead of
only asserting the raw values are absent; keep the check for "[REDACTED]" but
replace the value-absence assertions with explicit key-presence-and-value-equals
checks against log_data["headers"].
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py`:
- Around line 129-143: Update the test
TestProcessCleanupTaskErrorHandling.test_batch_delete_failure_is_swallowed to
also assert that the error is logged: patch or monkeypatch the log_exception
function used by process_cleanup_task (or the module-level logger) with a mock
before calling process_cleanup_task(lambda: iter([1,2,3]), _BoomModel, "Boom"),
call the function, then assert the mock was called once (or with an exception
argument) in addition to asserting no exception was raised; reference
process_cleanup_task and log_exception in the test so the logging call is
validated.

---

Nitpick comments:
In `@apps/api/plane/tests/unit/middleware/test_logger.py`:
- Around line 68-72: The test currently only checks that sensitive values aren't
present in log_data["headers"], which could pass if headers were omitted; update
the assertions in the test (in test_logger.py) to parse log_data["headers"] and
assert that each sensitive header key (API_KEY, AUTHORIZATION, COOKIE) exists in
the headers map and that its value equals the string "[REDACTED]" instead of
only asserting the raw values are absent; keep the check for "[REDACTED]" but
replace the value-absence assertions with explicit key-presence-and-value-equals
checks against log_data["headers"].
🪄 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: c61e80a3-e151-4279-af60-d20a77f9fb53

📥 Commits

Reviewing files that changed from the base of the PR and between 7276d75 and 37559e9.

📒 Files selected for processing (6)
  • apps/api/plane/bgtasks/cleanup_task.py
  • apps/api/plane/bgtasks/logger_task.py
  • apps/api/plane/settings/common.py
  • apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py
  • apps/api/plane/tests/unit/middleware/test_logger.py
  • apps/api/plane/tests/unit/settings/test_retention.py

Comment on lines +129 to +143
@pytest.mark.unit
class TestProcessCleanupTaskErrorHandling:
def test_batch_delete_failure_is_swallowed(self):
"""A failing batch is logged and skipped; the run does not raise."""

class _BoomManager:
@staticmethod
def filter(**kwargs):
raise RuntimeError("db unavailable")

class _BoomModel:
all_objects = _BoomManager()

# Should not raise even though the delete blows up.
process_cleanup_task(lambda: iter([1, 2, 3]), _BoomModel, "Boom")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Assert the error is logged, not just swallowed.

Line 132 states “logged and skipped”, but the test currently only verifies “does not raise”. Please also assert log_exception is called so this resilience contract can’t regress silently.

✅ Suggested test tightening
+from unittest.mock import patch
@@
 class TestProcessCleanupTaskErrorHandling:
     def test_batch_delete_failure_is_swallowed(self):
         """A failing batch is logged and skipped; the run does not raise."""
@@
-        # Should not raise even though the delete blows up.
-        process_cleanup_task(lambda: iter([1, 2, 3]), _BoomModel, "Boom")
+        with patch("plane.bgtasks.cleanup_task.log_exception") as mocked_log_exception:
+            # Should not raise even though the delete blows up.
+            process_cleanup_task(lambda: iter([1, 2, 3]), _BoomModel, "Boom")
+            mocked_log_exception.assert_called_once()
📝 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.

Suggested change
@pytest.mark.unit
class TestProcessCleanupTaskErrorHandling:
def test_batch_delete_failure_is_swallowed(self):
"""A failing batch is logged and skipped; the run does not raise."""
class _BoomManager:
@staticmethod
def filter(**kwargs):
raise RuntimeError("db unavailable")
class _BoomModel:
all_objects = _BoomManager()
# Should not raise even though the delete blows up.
process_cleanup_task(lambda: iter([1, 2, 3]), _BoomModel, "Boom")
from unittest.mock import patch
`@pytest.mark.unit`
class TestProcessCleanupTaskErrorHandling:
def test_batch_delete_failure_is_swallowed(self):
"""A failing batch is logged and skipped; the run does not raise."""
class _BoomManager:
`@staticmethod`
def filter(**kwargs):
raise RuntimeError("db unavailable")
class _BoomModel:
all_objects = _BoomManager()
with patch("plane.bgtasks.cleanup_task.log_exception") as mocked_log_exception:
# Should not raise even though the delete blows up.
process_cleanup_task(lambda: iter([1, 2, 3]), _BoomModel, "Boom")
mocked_log_exception.assert_called_once()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/api/plane/tests/unit/bg_tasks/test_cleanup_task.py` around lines 129 -
143, Update the test
TestProcessCleanupTaskErrorHandling.test_batch_delete_failure_is_swallowed to
also assert that the error is logged: patch or monkeypatch the log_exception
function used by process_cleanup_task (or the module-level logger) with a mock
before calling process_cleanup_task(lambda: iter([1,2,3]), _BoomModel, "Boom"),
call the function, then assert the mock was called once (or with an exception
argument) in addition to asserting no exception was raised; reference
process_cleanup_task and log_exception in the test so the logging call is
validated.

@sriramveeraghanta sriramveeraghanta merged commit edf2475 into preview May 27, 2026
13 checks passed
@sriramveeraghanta sriramveeraghanta deleted the chore/api-log-retention-mongo-removal branch May 27, 2026 10:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants