UN-3011 [FEAT] Limit maximum file execution count with file history viewer
What
- Add global maximum execution count limit (default: 3) to prevent infinite processing loops
- Add file history management UI with filtering, viewing, and bulk delete operations
Why
- Users need visibility into which files have been processed and how many times
- Users need to prevent infinite processing loops by enforcing maximum execution limits
- Users need to clean up file history to manage storage and maintain data hygiene
- Users need granular control over file history with filtering by status and execution count
How
Backend (Django):
- Created
FileHistoryViewSetwith read-only endpoints for listing, retrieving, deleting individual and bulk clearing file history records - Created
IsWorkflowOwnerOrSharedpermission class to ensure only workflow owners and shared users can access file history - Added database migration (0018) to add
execution_countfield toFileHistorymodel andmax_file_execution_countfield toWorkflowmodel (prepared for future workflow-level limits) - Enhanced
FileHistorymodel with execution count tracking and limit checking logic - Added global default maximum execution count (currently set to 3, configurable via settings)
- Enhanced
Workflowmodel withget_max_execution_count()method supporting configuration hierarchy (prepared for future workflow > org > global expansion) - Updated
FileHistorySerializerto include computed fields for max execution count and limit exceeded status - Added new URL routes under
/workflows/<id>/file-histories/for CRUD operations - Updated internal views to support file history operations
Frontend (React):
- Created
FileHistoryModalcomponent with Ant Design table, pagination, and multi-select row selection - Added filter panel supporting status filter (ERROR, COMPLETED) and execution count range filters (min/max)
- Added action buttons for delete selected, clear all matching filters, and refresh data
- Integrated modal into
Pipelinescomponent with "View File History" menu item - Added API service methods in
workflow-service.jsfor file history operations
Workers:
- Updated filter pipeline to check execution count limits before processing
- Updated workflow execution service to increment execution count and handle limit exceeded scenarios
- Updated core data models to support execution count tracking
Future Expansion:
- Database schema includes
max_file_execution_countfield onWorkflowmodel for future workflow-level limit configuration - Architecture supports extending limits to organization level and workflow level in future releases
Can this PR break any existing features. If yes, please list possible items. If no, please explain why.
- No breaking changes. This PR is backward compatible because:
- Database migration adds new fields with default values (execution_count defaults to 0, max_file_execution_count is nullable)
- Existing file history records are preserved and will work with the new UI
- The feature is additive - existing workflows continue to function without needing configuration changes
- Global default limit (3) is enforced for all workflows, preventing infinite loops while allowing reasonable retries
- Permission checks use existing organization-scoped access patterns
- Workers gracefully handle missing execution count data
Database Migrations
- Migration 0018:
backend/workflow_manager/workflow_v2/migrations/0018_filehistory_execution_count_and_more.py- Adds
execution_countfield toFileHistorymodel (IntegerField, default=0) - Adds
max_file_execution_countfield toWorkflowmodel (IntegerField, nullable, prepared for future workflow-level configuration) - Adds database indexes on
FileHistory.statusandFileHistory.execution_countfor efficient filtering
- Adds
Env Config
- No new environment variables required
- Global default max execution count is currently hardcoded to 3 (can be made configurable in future releases)
Relevant Docs
- File history management API endpoints documentation should be added to API reference
- User guide for file history viewer should be added to product documentation
- Document global execution limit (default: 3) and future expansion to org/workflow levels
Related Issues or PRs
- Jira ticket: UN-3011
Dependencies Versions
- No new dependencies added
- Uses existing Ant Design components (Table, Modal, Button, Select, InputNumber)
- Uses existing Django REST Framework for API endpoints
Notes on Testing
Backend Testing:
- Test file history list endpoint with pagination
- Test filtering by status (ERROR, COMPLETED) and execution count range
- Test delete single file history record
- Test bulk clear with filters
- Test permission checks for workflow owner, shared users, and non-authorized users
- Test execution count increment logic in workers
- Test global max execution count limit enforcement (default: 3)
- Test Redis cache clearing on file history deletion
- Verify workflow-level
max_file_execution_countfield exists but is not enforced yet (prepared for future)
Frontend Testing:
- Test file history modal opens from pipelines action menu
- Test table displays file path, status, execution count, last run, error, and actions
- Test pagination (10 items per page)
- Test multi-select row selection
- Test delete selected files action
- Test clear all action with confirmation dialog
- Test filter panel (status dropdown, min/max execution count inputs)
- Test copy-to-clipboard for file paths and error messages
- Test refresh data action
- Test responsive design on mobile, tablet, and desktop
Integration Testing:
- Test end-to-end workflow: file upload → processing → execution count increment → view in file history
- Test limit enforcement: file reaches max execution count (3) → processing stops → status shown in UI
- Test bulk delete: select multiple files → delete → verify removed from database and cache
- Test behavior when file hits global limit: verify error message, verify file not reprocessed
Screenshots
(Screenshots to be added by QA team during testing)
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
- New Features
- Added file history management interface with status filtering, execution count ranges, and file path search capabilities
- Introduced configurable execution count limits to control how many times files can be reprocessed
- Added bulk delete and clear operations for managing file history records
- New "View File History" action accessible from pipeline and deployment views
- Added copy-to-clipboard functionality for file paths and error messages
✏️ Tip: You can customize this high-level summary in your review settings.
Walkthrough
Adds execution-count tracking and per-workflow execution limits; atomic file-history create/update with race handling; new FileHistory API endpoints and UI for listing/clearing histories; permission checks; worker changes to honor limits and only reuse COMPLETED cached results; frontend clipboard helper and modal UI.
Changes
| Cohort / File(s) | Summary |
|---|---|
Configuration & Env backend/backend/settings/base.py, backend/sample.env |
Add MAX_FILE_EXECUTION_COUNT setting (env var, default 3). |
Models & Migration backend/workflow_manager/workflow_v2/models/file_history.py, backend/workflow_manager/workflow_v2/models/workflow.py, backend/workflow_manager/workflow_v2/migrations/0018_filehistory_execution_count_and_more.py |
Add execution_count to FileHistory; add max_file_execution_count to Workflow and ExecutionAction; add indexes and trigram extension; add __str__, has_exceeded_limit, and get_max_execution_count helpers. |
FileHistory Helper backend/workflow_manager/workflow_v2/file_history_helper.py |
Add atomic increment helper using F(), safe-string helpers, race-condition handling on IntegrityError; create_file_history creates or atomically increments and returns FileHistory with execution_count. |
APIs & Internal Views backend/workflow_manager/workflow_v2/file_history_views.py, backend/workflow_manager/workflow_v2/urls/workflow.py, backend/workflow_manager/internal_views.py, backend/workflow_manager/workflow_v2/views.py |
New FileHistoryViewSet (list/retrieve/destroy/clear) with filters and nested routes; include execution_count and max_execution_count in internal/batch responses; serializer reuse optimization in batch lookup. |
Permissions & Serialization backend/workflow_manager/workflow_v2/permissions.py, backend/workflow_manager/workflow_v2/serializers.py |
New IsWorkflowOwnerOrShared permission (caches workflow on request); FileHistorySerializer adds max_execution_count and has_exceeded_limit fields. |
Workers & Processing workers/shared/processing/filter_pipeline.py, workers/shared/workflow/execution/service.py, workers/api-deployment/tasks.py, workers/shared/processing/files/processor.py |
Enforce execution-count-based skips (use has_exceeded_limit when available), simplify status-based acceptance, only reuse cached results when status is COMPLETED, and propagate status/error into file-history creation and API cached responses. |
Frontend UI & Services frontend/src/components/pipelines-or-deployments/file-history-modal/FileHistoryModal.jsx, frontend/src/components/pipelines-or-deployments/file-history-modal/FileHistoryModal.css, frontend/src/components/pipelines-or-deployments/pipelines/Pipelines.jsx, frontend/src/components/agency/agency/Agency.jsx, frontend/src/components/workflows/workflow/workflow-service.js |
Add File History modal UI (filters, pagination, copy, single/bulk delete, clear) and CSS; integrate modal into Pipelines and Agency; add service methods getFileHistories, deleteFileHistory, bulkClearFileHistories, bulkDeleteFileHistoriesByIds. |
Client Data Model & Helpers unstract/core/src/unstract/core/data_models.py, frontend/src/helpers/GetStaticData.js |
Wire execution_count into client FileHistoryData; add copyToClipboard(text) helper. |
Sequence Diagram(s)
sequenceDiagram
participant FE as Frontend (Modal)
participant API as Django API
participant Helper as FileHistoryHelper
participant DB as Postgres
FE->>API: GET /workflow/{id}/file-histories/ (list) or POST clear/delete
API->>DB: fetch FileHistory (+ workflow.max_file_execution_count)
DB-->>API: return records (status, execution_count, max_file_execution_count)
API-->>FE: list response (includes execution_count, max_execution_count, has_exceeded_limit)
FE->>API: POST /workflow/{id}/file-histories/ (create/update) or DELETE single
API->>Helper: create_file_history(workflow, file_hash, status, result, metadata, error, is_api)
Helper->>DB: SELECT FileHistory WHERE workflow & file_hash
alt exists
Helper->>DB: UPDATE execution_count = F('execution_count') + 1, set status/result/error
DB-->>Helper: updated record
else not exists
Helper->>DB: INSERT FileHistory (execution_count=1)
alt IntegrityError (race)
Helper->>DB: SELECT & UPDATE execution_count = F('execution_count') + 1
DB-->>Helper: updated record
end
end
Helper-->>API: return FileHistory (includes execution_count)
API-->>FE: Response {file_history_id, execution_count, ...}
sequenceDiagram
participant Worker as Worker
participant Pipeline as filter_pipeline
participant API as Django API
participant DB as Postgres
Worker->>API: GET file-history for file (hash)
API->>DB: fetch FileHistory + Workflow.max_file_execution_count
DB-->>API: return data (status, execution_count, max_file_execution_count, has_exceeded_limit)
API-->>Pipeline: return history data
alt has_exceeded_limit == true OR (has_exceeded_limit is None AND execution_count >= max_file_execution_count)
Pipeline-->>Worker: SKIP (limit reached)
else status allows reprocess
Pipeline-->>Worker: ACCEPT (process)
else
Pipeline->>Pipeline: compare history_path vs current_path
alt same path
Pipeline-->>Worker: SKIP
else
Pipeline-->>Worker: ACCEPT
end
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
- Areas needing extra attention:
- backend/workflow_manager/workflow_v2/file_history_helper.py — atomic increments, IntegrityError recovery, freshness of returned instance.
- workers/shared/processing/filter_pipeline.py — new SKIP/ACCEPT logic and backward-compat handling when has_exceeded_limit is None.
- workers/shared/processing/files/processor.py & workers/api-deployment/tasks.py — gating cached-result usage on COMPLETED and correct propagation of status/error to API responses.
- backend/workflow_manager/workflow_v2/file_history_views.py — bulk clear validation (IDs limit) and filter-based deletion semantics.
- frontend/src/components/.../FileHistoryModal.jsx — filter apply/reset UX, bulk action counts/limits, and API error handling.
Pre-merge checks and finishing touches
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The PR title clearly and specifically summarizes the main feature: introducing a maximum file execution count limit with a file history viewer UI. |
| Description check | ✅ Passed | The PR description is comprehensive and well-structured, covering all required template sections: What, Why, How, database migrations, env config, related issues, testing notes, and screenshots. |
| Docstring Coverage | ✅ Passed | Docstring coverage is 84.85% which is sufficient. The required threshold is 80.00%. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
- [ ] Commit unit tests in branch
feat/UN-3011-FEAT_limit_maximum_file_execution_count_with_file_history_viewer
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 @coderabbitai help to get the list of available commands and usage tips.
@muhammad-ali-e make sure to raise a docs PR as well for this
@muhammad-ali-e make sure to raise a docs PR as well for this
once it went to staging
Quality Gate passed
Issues
1 New issue
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code
Test Results
Summary
- ✅ Runner Tests: 11 passed, 0 failed (11 total)
- ✅ SDK1 Tests: 66 passed, 0 failed (66 total)
Runner Tests - Full Report
| filepath | function | $$\textcolor{#23d18b}{\tt{passed}}$$ | SUBTOTAL |
|---|---|---|---|
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_logs}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_cleanup}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_cleanup\_skip}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_client\_init}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_get\_image\_exists}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_get\_image}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_get\_container\_run\_config\_without\_mount}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_run\_container}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_get\_image\_for\_sidecar}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{runner/src/unstract/runner/clients/test\_docker.py}}$$ | $$\textcolor{#23d18b}{\tt{test\_sidecar\_container}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{TOTAL}}$$ | $$\textcolor{#23d18b}{\tt{11}}$$ | $$\textcolor{#23d18b}{\tt{11}}$$ |
SDK1 Tests - Full Report
| filepath | function | $$\textcolor{#23d18b}{\tt{passed}}$$ | SUBTOTAL |
|---|---|---|---|
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_success\_on\_first\_attempt}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_on\_connection\_error}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_non\_retryable\_http\_error}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retryable\_http\_errors}}$$ | $$\textcolor{#23d18b}{\tt{3}}$$ | $$\textcolor{#23d18b}{\tt{3}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_post\_method\_retry}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_platform.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPlatformHelperRetry.test\_retry\_logging}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_success\_on\_first\_attempt}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_retry\_on\_errors}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/test\_prompt.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPromptToolRetry.test\_wrapper\_methods\_retry}}$$ | $$\textcolor{#23d18b}{\tt{4}}$$ | $$\textcolor{#23d18b}{\tt{4}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_connection\_error\_is\_retryable}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_timeout\_is\_retryable}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_retryable\_status\_codes}}$$ | $$\textcolor{#23d18b}{\tt{3}}$$ | $$\textcolor{#23d18b}{\tt{3}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_non\_retryable\_status\_codes}}$$ | $$\textcolor{#23d18b}{\tt{5}}$$ | $$\textcolor{#23d18b}{\tt{5}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_http\_error\_without\_response}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_retryable\_errno}}$$ | $$\textcolor{#23d18b}{\tt{5}}$$ | $$\textcolor{#23d18b}{\tt{5}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_os\_error\_non\_retryable\_errno}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestIsRetryableError.test\_other\_exception\_not\_retryable}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_without\_jitter}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_exponential\_backoff\_with\_jitter}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCalculateDelay.test\_max\_delay\_cap\_with\_jitter}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_successful\_call\_first\_attempt}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_after\_transient\_failure}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_max\_retries\_exceeded}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_max\_time\_exceeded}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_retry\_with\_custom\_predicate}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_no\_retry\_with\_predicate\_false}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_exception\_not\_in\_tuple\_not\_retried}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryWithExponentialBackoff.test\_delay\_would\_exceed\_max\_time}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_default\_configuration}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_environment\_variable\_configuration}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_max\_retries}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_max\_time}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_base\_delay}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_invalid\_multiplier}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_jitter\_values}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ | $$\textcolor{#23d18b}{\tt{2}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_exceptions\_only}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_custom\_predicate\_only}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_both\_exceptions\_and\_predicate}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestCreateRetryDecorator.test\_exceptions\_match\_but\_predicate\_false}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_platform\_service\_call\_exists}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_retry\_prompt\_service\_call\_exists}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_platform\_service\_decorator\_retries\_on\_connection\_error}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestPreconfiguredDecorators.test\_prompt\_service\_decorator\_retries\_on\_timeout}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_warning\_logged\_on\_retry}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_info\_logged\_on\_success\_after\_retry}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{tests/utils/test\_retry\_utils.py}}$$ | $$\textcolor{#23d18b}{\tt{TestRetryLogging.test\_exception\_logged\_on\_giving\_up}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ | $$\textcolor{#23d18b}{\tt{1}}$$ |
| $$\textcolor{#23d18b}{\tt{TOTAL}}$$ | $$\textcolor{#23d18b}{\tt{66}}$$ | $$\textcolor{#23d18b}{\tt{66}}$$ |
Quality Gate passed
Issues
1 New issue
0 Accepted issues
Measures
0 Security Hotspots
0.0% Coverage on New Code
0.0% Duplication on New Code