Compare commits

..

153 Commits

Author SHA1 Message Date
09e59ba88b fix repo place 2026-03-04 10:04:40 +03:00
638597f182 move test 2026-03-04 09:18:42 +03:00
bb921ce5dd [
{
        "file": "frontend/src/components/__tests__/task_log_viewer.test.js",
        "verdict": "APPROVED",
        "rejection_reason": "NONE",
        "audit_details": {
            "target_invoked": true,
            "pre_conditions_tested": true,
            "post_conditions_tested": true,
            "test_fixture_used": true,
            "edges_covered": true,
            "invariants_verified": true,
            "ux_states_tested": true,
            "semantic_anchors_present": true
        },
        "coverage_summary": {
            "total_edges": 2,
            "edges_tested": 2,
            "total_invariants": 1,
            "invariants_tested": 1,
            "total_ux_states": 3,
            "ux_states_tested": 3
        },
        "tier_compliance": {
            "source_tier": "CRITICAL",
            "meets_tier_requirements": true
        },
        "feedback": "Remediation successful: test tier matches CRITICAL, missing missing @TEST_EDGE no_task_id coverage added, test for @UX_FEEDBACK (autoScroll) added properly, missing inline=false (show=true) tested properly. Semantic RELATION tag fixed to VERIFIES."
    },
    {
        "file": "frontend/src/lib/components/reports/__tests__/report_card.ux.test.js",
        "verdict": "APPROVED",
        "rejection_reason": "NONE",
        "audit_details": {
            "target_invoked": true,
            "pre_conditions_tested": true,
            "post_conditions_tested": true,
            "test_fixture_used": true,
            "edges_covered": true,
            "invariants_verified": true,
            "ux_states_tested": true,
            "semantic_anchors_present": true
        },
        "coverage_summary": {
            "total_edges": 2,
            "edges_tested": 2,
            "total_invariants": 1,
            "invariants_tested": 1,
            "total_ux_states": 2,
            "ux_states_tested": 2
        },
        "tier_compliance": {
            "source_tier": "CRITICAL",
            "meets_tier_requirements": true
        },
        "feedback": "Remediation successful: @TEST_EDGE random_status and @TEST_EDGE empty_report_object tests explicitly assert on outcomes, @TEST_FIXTURE tested completely, Test tier switched to CRITICAL."
    },
    {
        "file": "backend/tests/test_logger.py",
        "verdict": "APPROVED",
        "rejection_reason": "NONE",
        "audit_details": {
            "target_invoked": true,
            "pre_conditions_tested": true,
            "post_conditions_tested": true,
            "test_fixture_used": true,
            "edges_covered": true,
            "invariants_verified": true,
            "ux_states_tested": false,
            "semantic_anchors_present": true
        },
        "coverage_summary": {
            "total_edges": 0,
            "edges_tested": 0,
            "total_invariants": 0,
            "invariants_tested": 0,
            "total_ux_states": 0,
            "ux_states_tested": 0
        },
        "tier_compliance": {
            "source_tier": "STANDARD",
            "meets_tier_requirements": true
        },
        "feedback": "Remediation successful: Test module semantic anchors added [DEF] and [/DEF] explicitly. Added missing @TIER tag and @RELATION: VERIFIES -> src/core/logger.py at the top of the file."
    }
]
2026-03-03 21:05:29 +03:00
fa380ff9a5 test: remediate audit findings for task log viewer, report card and logger tests 2026-03-03 21:01:24 +03:00
ce3955ed2e chore: commit remaining workspace changes 2026-03-03 19:51:17 +03:00
19898b1570 chore(specs): move clean-repo-enterprise spec from 020 to 023 2026-03-03 19:50:53 +03:00
da24fb9253 dev-preprod-prod logic 2026-03-01 14:39:25 +03:00
80b28ac371 slug first logic 2026-03-01 13:17:05 +03:00
f24200d52a git list refactor 2026-03-01 12:13:19 +03:00
5d45b4adb0 fix(dashboards): lazy-load git status for visible rows 2026-02-28 11:21:37 +03:00
daa9f7be3a причесываем лог 2026-02-28 10:47:19 +03:00
7e43830144 fix(dashboards): stabilize grid layout and remove owners N+1 fallback 2026-02-28 10:46:47 +03:00
066747de59 feat(dashboards): show owners and improve grid actions UI 2026-02-28 10:04:56 +03:00
442d0e0ac2 workflows update 2026-02-28 00:04:55 +03:00
8fa951fc93 dry run migration 2026-02-27 20:48:18 +03:00
149d230426 semantic protocol update 2026-02-27 20:48:06 +03:00
4c601fbe06 [
{
    "file": "backend/src/api/routes/__tests__/test_dashboards.py",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "All 9 previous findings remediated. @TEST_FIXTURE data aligned, all @TEST_EDGE scenarios covered, all @PRE negative tests present, all @SIDE_EFFECT assertions added. Full contract compliance."
  },
  {
    "file": "backend/src/api/routes/__tests__/test_datasets.py",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "All 6 previous findings remediated. Full @PRE boundary coverage including page_size>100, empty IDs, missing env. @SIDE_EFFECT assertions added. 503 error path tested."
  },
  {
    "file": "backend/src/core/auth/__tests__/test_auth.py",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "All 4 previous findings remediated. @SIDE_EFFECT last_login verified. Inactive user @PRE negative test added. Empty hash edge case covered. provision_adfs_user tested for both new and existing user paths."
  },
  {
    "file": "backend/src/services/__tests__/test_resource_service.py",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "Both prior recommendations implemented. Full edge case coverage for _get_last_task_for_resource. No anti-patterns detected."
  },
  {
    "file": "backend/tests/test_resource_hubs.py",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "Pagination boundary tests added. All @TEST_EDGE scenarios now covered. No anti-patterns detected."
  },
  {
    "file": "frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "No changes since previous audit. Contract scanning remains sound."
  },
  {
    "file": "frontend/src/lib/components/assistant/__tests__/assistant_confirmation.integration.test.js",
    "verdict": "APPROVED",
    "rejection_reason": "NONE",
    "audit_details": {
      "target_invoked": true,
      "pre_conditions_tested": true,
      "post_conditions_tested": true,
      "test_data_used": true
    },
    "feedback": "No changes since previous audit. Confirmation flow testing remains sound."
  }
]
2026-02-27 09:59:57 +03:00
36173c0880 test contracts 2026-02-26 19:40:00 +03:00
81d62c1345 new test contracts 2026-02-26 19:29:07 +03:00
a8f7147500 test now STANDARD tier 2026-02-26 18:38:26 +03:00
ce684bc5d1 update test data 2026-02-26 18:38:02 +03:00
484019e750 test semantic harden 2026-02-26 18:26:11 +03:00
4ff6d307f8 +ai update 2026-02-26 17:54:23 +03:00
f4612c0737 Improve dashboard LLM validation UX and report flow 2026-02-26 17:53:41 +03:00
5ec1254336 codex specify 2026-02-25 21:19:48 +03:00
b7d1ee2b71 feat(search): add grouped global results for tasks and reports 2026-02-25 21:09:42 +03:00
87285d8f0a feat(search): implement global navbar search for dashboards and datasets 2026-02-25 21:07:51 +03:00
04b01eadb5 fix(ui): use global environment context on datasets page 2026-02-25 20:59:24 +03:00
4d5b9e88dd fix(auth): defer environment context fetch until token is available 2026-02-25 20:58:14 +03:00
4bad4ab4e2 fix(logging): suppress per-request belief scope spam in API client 2026-02-25 20:52:12 +03:00
3801ca13d9 feat(env): add global production context and safety indicators 2026-02-25 20:46:00 +03:00
999c0c54df + git config 2026-02-25 20:27:29 +03:00
f9ac282596 feat: Implement recursive storage listing and directory browsing for backups, and add a migration option to fix cross-filters. 2026-02-25 20:01:33 +03:00
5d42a6b930 i18 cleanup 2026-02-25 18:31:50 +03:00
99f19ac305 { "verdict": "APPROVED", "rejection_reason": "NONE", "audit_details": { "target_invoked": true, "pre_conditions_tested": true, "post_conditions_tested": true, "test_data_used": true }, "feedback": "The test suite robustly verifies the
MigrationEngine
 contracts. It avoids Tautologies by cleanly substituting IdMappingService without mocking the engine itself. Cross-filter parsing asserts against hard-coded, predefined validation dictionaries (no Logic Mirroring). It successfully addresses @PRE negative cases (e.g. invalid zip paths, missing YAMLs) and rigorously validates @POST file transformations (e.g. in-place UUID substitutions and archive reconstruction)." }
2026-02-25 17:47:55 +03:00
590ba49ddb sync worked 2026-02-25 15:20:26 +03:00
2a5b225800 feat: Enhance ID mapping service robustness, add defensive guards, and expand migration engine and API testing. 2026-02-25 14:44:21 +03:00
33433c3173 ready for test 2026-02-25 13:35:09 +03:00
21e969a769 workflow agy update 2026-02-25 13:29:14 +03:00
783644c6ad tasks ready 2026-02-25 13:28:24 +03:00
d32d85556f +md 2026-02-25 10:34:30 +03:00
bc0367ab72 speckit update 2026-02-25 10:31:48 +03:00
1c362f4092 { "verdict": "APPROVED", "rejection_reason": "NONE", "audit_details": { "target_invoked": true, "pre_conditions_tested": true, "post_conditions_tested": true, "test_data_used": true }, "feedback": "Both test files have successfully passed the audit. The 'task_log_viewer.test.js' suite now correctly imports and mounts the real Svelte component using Test Library, fully eliminating the logic mirror/tautology issue. The 'test_logger.py' suite now properly implements negative tests for the @PRE constraint in 'belief_scope' and fully verifies all @POST effects triggered by 'configure_logger'." } 2026-02-24 21:55:13 +03:00
95ae9c6af1 semantic update 2026-02-24 21:08:12 +03:00
7a12ed0931 chore(gitignore): unignore frontend dashboards routes and track pages 2026-02-24 16:16:41 +03:00
e0c0dd3221 fix(validation): respect settings-bound provider and correct multimodal heuristic 2026-02-24 16:04:14 +03:00
5f6e9c0cc0 fix(llm-validation): accept stepfun multimodal models and return 422 on capability mismatch 2026-02-24 16:00:23 +03:00
4fd9d6b6d5 fix(llm): skip unsupported json_object mode for openrouter stepfun models 2026-02-24 14:22:08 +03:00
7e6bd56488 feat(assistant-chat): add animated thinking loader while waiting for response 2026-02-24 14:15:35 +03:00
5e3c213b92 fix(task-drawer): keep drawer above assistant dim overlay 2026-02-24 14:12:34 +03:00
37b75b5a5c fix(task-drawer): render as side column without modal overlay when opened from assistant 2026-02-24 14:09:34 +03:00
3d42a487f7 fix(assistant): resolve dashboard refs via LLM entities and remove deterministic parser fallback 2026-02-24 13:32:25 +03:00
2e93f5ca63 fix(assistant-chat): prevent stale history response from resetting selected conversation 2026-02-24 13:27:09 +03:00
286167b1d5 generate semantic clean up 2026-02-24 12:51:57 +03:00
7df7b4f98c feat(assistant): add multi-dialog UX, task-aware llm settings, and i18n cleanup 2026-02-23 23:45:01 +03:00
ab1c87ffba feat(assistant): add conversations list, infinite history scroll, and archived tab 2026-02-23 20:27:51 +03:00
40e6d8cd4c chat worked 2026-02-23 20:20:25 +03:00
18e96a58bc feat(assistant): implement spec 021 chat assistant flow with semantic contracts 2026-02-23 19:37:56 +03:00
83e4875097 Merge branch '001-unify-frontend-style' into master 2026-02-23 16:06:12 +03:00
e635bd7e5f Add Apache Superset OpenAPI documentation reference to ROOT.md 2026-02-23 16:04:42 +03:00
43dd97ecbf Новый экранчик для обзора дашей 2026-02-23 15:54:20 +03:00
0685f50ae7 Merge branch '020-task-reports-design' into master 2026-02-23 13:28:31 +03:00
d0ffc2f1df Finalize task-020 reports navigation and stability fixes 2026-02-23 13:28:30 +03:00
26880d2e09 semantic update 2026-02-23 13:15:48 +03:00
008b6d72c9 таски готовы 2026-02-23 10:18:56 +03:00
f0c85e4c03 Fix task API stability and Playwright runtime in Docker 2026-02-21 23:43:46 +03:00
6ffdf5f8a4 feat: restore legacy data and add typed task result views 2026-02-21 23:17:56 +03:00
0cf0ef25f1 db + docker 2026-02-20 20:47:39 +03:00
af74841765 semantic update 2026-02-20 10:41:15 +03:00
d7e4919d54 few shots update 2026-02-20 10:26:01 +03:00
fdcbe32dfa css refactor 2026-02-19 18:24:36 +03:00
4de5b22d57 +Svelte specific 2026-02-19 17:47:24 +03:00
c8029ed309 ai base 2026-02-19 17:43:45 +03:00
c2a4c8062a fix tax log 2026-02-19 16:05:59 +03:00
2c820e103a tests ready 2026-02-19 13:33:20 +03:00
c8b84b7bd7 Coder + fix workflow 2026-02-19 13:33:10 +03:00
fdb944f123 Test logic update 2026-02-19 12:44:31 +03:00
d29bc511a2 task panel 2026-02-19 09:43:01 +03:00
a3a9f0788d docs: amend constitution to v2.3.0 (tailwind css first principle) 2026-02-18 18:29:52 +03:00
77147dc95b refactor 2026-02-18 17:29:46 +03:00
026239e3bf fix 2026-02-15 11:11:30 +03:00
4a0273a604 измененные спеки таски 2026-02-10 15:53:38 +03:00
edb2dd5263 updated tasks 2026-02-10 15:04:43 +03:00
76b98fcf8f linter + новые таски 2026-02-10 12:53:01 +03:00
794cc55fe7 Таски готовы 2026-02-09 12:35:27 +03:00
235b0e3c9f semantic update 2026-02-08 22:53:54 +03:00
e6087bd3c1 таски готовы 2026-02-07 12:42:32 +03:00
0f16bab2b8 Похоже работает 2026-02-07 11:26:06 +03:00
7de96c17c4 feat(llm-plugin): switch to environment API for log retrieval
- Replace local backend.log reading with Superset API /log/ fetch
- Update DashboardValidationPlugin to use SupersetClient
- Filter logs by dashboard_id and last 24 hours
- Update spec FR-006 to reflect API usage
2026-02-06 17:57:25 +03:00
f018b97ed2 Semantic protocol update - add UX 2026-01-30 18:53:52 +03:00
72846aa835 tasks ux-reference 2026-01-30 13:35:03 +03:00
994c0c3e5d feat(speckit): integrate ux reference into workflows
Introduce a UX reference stage to ensure technical plans align with
user experience goals. Adds a new template, a generation step in the
specification workflow, and mandatory validation checks during
planning to prevent technical compromises from degrading the defined
user experience.
2026-01-30 12:31:19 +03:00
252a8601a9 Вроде работает 2026-01-30 11:10:16 +03:00
8044f85ea4 tasks and workflow updated 2026-01-29 10:06:28 +03:00
d4109e5a03 docs: amend constitution to v2.0.0 (delegate semantics to protocol + add async/testability principles) 2026-01-28 18:48:43 +03:00
b2bbd73439 tasks ready 2026-01-28 18:30:23 +03:00
0e0e26e2f7 semantic update 2026-01-28 16:57:19 +03:00
18b42f8dd0 semantic protocol condense + script update 2026-01-28 15:49:39 +03:00
e7b31accd6 tested 2026-01-27 23:49:19 +03:00
d3c3a80ed2 Передаем на тест 2026-01-27 16:32:08 +03:00
cc244c2d86 tasks ready 2026-01-27 13:26:06 +03:00
d10c23e658 Обновил gitignore - убрал логи 2026-01-26 22:15:17 +03:00
1042b35d1b Закончили редизайн, обновили интерфейс бэкапа 2026-01-26 22:12:35 +03:00
16ffeb1ed6 Выполнено, передано на тестирование 2026-01-26 21:17:05 +03:00
da34deac02 tasks ready 2026-01-26 20:58:38 +03:00
51e9ee3fcc semantic update 2026-01-26 11:57:36 +03:00
edf9286071 Файловое хранилище готово 2026-01-26 11:08:18 +03:00
a542e7d2df Передаем на тест 2026-01-25 18:33:00 +03:00
a863807cf2 tasks ready 2026-01-24 16:21:43 +03:00
e2bc68683f Update .gitignore 2026-01-24 11:26:19 +03:00
43cb82697b Update backup scheduler task status 2026-01-24 11:26:05 +03:00
4ba28cf93e semantic cleanup 2026-01-23 21:58:32 +03:00
343f2e29f5 Мультиязночность + причесывание css 2026-01-23 17:53:46 +03:00
c9a53578fd tasks ready 2026-01-23 14:56:05 +03:00
07ec2d9797 Работает создание коммитов и перенос в новый enviroment 2026-01-23 13:57:44 +03:00
e9d3f3c827 tasks ready 2026-01-22 23:59:16 +03:00
26ba015b75 +gitignore 2026-01-22 23:25:29 +03:00
49129d3e86 fix error 2026-01-22 23:18:48 +03:00
d99a13d91f refactor complete 2026-01-22 17:37:17 +03:00
203ce446f4 ашч 2026-01-21 14:00:48 +03:00
c96d50a3f4 fix(backend): standardize superset client init and auth
- Update plugins (debug, mapper, search) to explicitly map environment config to SupersetConfig
- Add authenticate method to SupersetClient for explicit session management
- Add get_environment method to ConfigManager
- Fix navbar dropdown hover stability in frontend with invisible bridge
2026-01-20 19:31:17 +03:00
3bbe320949 TaskLog fix 2026-01-19 17:10:43 +03:00
2d2435642d bug fixs 2026-01-19 00:07:06 +03:00
ec8d67c956 bug fixes 2026-01-18 23:21:00 +03:00
76baeb1038 semantic markup update 2026-01-18 21:29:54 +03:00
11c59fb420 semantic checker script update 2026-01-13 17:33:57 +03:00
b2529973eb constitution update 2026-01-13 15:29:42 +03:00
ae1d630ad6 semantics update 2026-01-13 09:11:27 +03:00
9a9c5879e6 tasks.md status 2026-01-12 12:35:45 +03:00
696aac32e7 1st iter 2026-01-12 12:33:51 +03:00
7a9b1a190a tasks ready 2026-01-07 18:59:49 +03:00
a3dc1fb2b9 docs: amend constitution to v1.6.0 (add 'Everything is a Plugin' principle) and refactor 010 plan 2026-01-07 18:36:38 +03:00
297b29986d Product Manager role 2026-01-07 11:39:44 +03:00
4c6fc8256d project map script | semantic parcer 2026-01-01 16:58:21 +03:00
a747a163c8 backup worked 2025-12-30 22:02:51 +03:00
fce0941e98 docs ready 2025-12-30 21:30:37 +03:00
45c077b928 +api rework 2025-12-30 20:08:48 +03:00
9ed3a5992d cleaned 2025-12-30 18:20:40 +03:00
a032fe8457 Password promt 2025-12-30 17:21:12 +03:00
4c9d554432 TaskManager refactor 2025-12-29 10:13:37 +03:00
6962a78112 mappings+migrate 2025-12-27 10:16:41 +03:00
3d75a21127 tech_lead / coder 2roles 2025-12-27 08:02:59 +03:00
07914c8728 semantic add 2025-12-27 07:14:08 +03:00
cddc259b76 new loggers logic in constitution 2025-12-27 06:51:28 +03:00
dcbf0a7d7f tasks ready 2025-12-27 06:37:03 +03:00
65f61c1f80 Merge branch '001-migration-ui-redesign' into master 2025-12-27 05:58:35 +03:00
cb7386f274 superset_tool logger rework 2025-12-27 05:53:30 +03:00
83e34e1799 feat(logging): implement configurable belief state logging
- Add LoggingConfig model and logging field to GlobalSettings
- Implement belief_scope context manager for structured logging
- Add configure_logger for dynamic level and file rotation settings
- Add logging configuration UI to Settings page
- Update ConfigManager to apply logging settings on initialization and updates
2025-12-27 05:39:33 +03:00
d197303b9f 006 plan ready 2025-12-26 19:36:49 +03:00
a43f8fb021 001-migration-ui-redesign (#3)
Reviewed-on: #3
2025-12-26 18:17:58 +03:00
4aa01b6470 Merge branch 'migration' into 001-migration-ui-redesign 2025-12-26 18:16:24 +03:00
35b423979d spec rules 2025-12-25 22:28:42 +03:00
2ffc3cc68f feat(migration): implement interactive mapping resolution workflow
- Add SQLite database integration for environments and mappings
- Update TaskManager to support pausing tasks (AWAITING_MAPPING)
- Modify MigrationPlugin to detect missing mappings and wait for resolution
- Add frontend UI for handling missing mappings interactively
- Create dedicated migration routes and API endpoints
- Update .gitignore and project documentation
2025-12-25 22:27:29 +03:00
43 changed files with 3737 additions and 17747 deletions

View File

@@ -2,12 +2,12 @@
> High-level module structure for AI Context. Generated automatically.
**Generated:** 2026-03-04T13:18:11.370535
**Generated:** 2026-03-01T12:09:39.463912
## Summary
- **Total Modules:** 83
- **Total Entities:** 2349
- **Total Modules:** 80
- **Total Entities:** 2080
## Module Hierarchy
@@ -28,9 +28,9 @@
### 📁 `src/`
- 🏗️ **Layers:** API, Core, UI (API)
- 📊 **Tiers:** CRITICAL: 2, STANDARD: 20, TRIVIAL: 2
- 📊 **Tiers:** CRITICAL: 2, STANDARD: 19, TRIVIAL: 2
- 📄 **Files:** 2
- 📦 **Entities:** 24
- 📦 **Entities:** 23
**Key Entities:**
@@ -42,21 +42,21 @@
### 📁 `api/`
- 🏗️ **Layers:** API
- 📊 **Tiers:** CRITICAL: 7
- 📊 **Tiers:** STANDARD: 7
- 📄 **Files:** 1
- 📦 **Entities:** 7
**Key Entities:**
- 📦 **backend.src.api.auth** (Module) `[CRITICAL]`
- 📦 **backend.src.api.auth** (Module)
- Authentication API endpoints.
### 📁 `routes/`
- 🏗️ **Layers:** API, UI (API)
- 📊 **Tiers:** CRITICAL: 11, STANDARD: 226, TRIVIAL: 8
- 📄 **Files:** 18
- 📦 **Entities:** 245
- 📊 **Tiers:** CRITICAL: 3, STANDARD: 205, TRIVIAL: 7
- 📄 **Files:** 17
- 📦 **Entities:** 215
**Key Entities:**
@@ -91,10 +91,10 @@
### 📁 `__tests__/`
- 🏗️ **Layers:** API, Domain, Domain (Tests), UI (API Tests), Unknown
- 📊 **Tiers:** STANDARD: 63, TRIVIAL: 134
- 📄 **Files:** 12
- 📦 **Entities:** 197
- 🏗️ **Layers:** API, Domain (Tests), UI (API Tests)
- 📊 **Tiers:** STANDARD: 61, TRIVIAL: 121
- 📄 **Files:** 9
- 📦 **Entities:** 182
**Key Entities:**
@@ -126,7 +126,7 @@
### 📁 `core/`
- 🏗️ **Layers:** Core
- 📊 **Tiers:** CRITICAL: 45, STANDARD: 88, TRIVIAL: 8
- 📊 **Tiers:** CRITICAL: 2, STANDARD: 131, TRIVIAL: 8
- 📄 **Files:** 10
- 📦 **Entities:** 141
@@ -136,13 +136,13 @@
- A session factory for the authentication database.
- **BeliefFormatter** (Class)
- Custom logging formatter that adds belief state prefixes to ...
- **ConfigManager** (Class) `[CRITICAL]`
- **ConfigManager** (Class)
- A class to handle application configuration persistence and ...
- **IdMappingService** (Class) `[CRITICAL]`
- Service handling the cataloging and retrieval of remote Supe...
- **LogEntry** (Class)
- A Pydantic model representing a single, structured log entry...
- **MigrationEngine** (Class) `[CRITICAL]`
- **MigrationEngine** (Class)
- Engine for transforming Superset export ZIPs.
- **PluginBase** (Class)
- Defines the abstract base class that all plugins must implem...
@@ -164,27 +164,27 @@
### 📁 `auth/`
- 🏗️ **Layers:** Core
- 📊 **Tiers:** CRITICAL: 26
- 📊 **Tiers:** STANDARD: 26
- 📄 **Files:** 6
- 📦 **Entities:** 26
**Key Entities:**
- **AuthConfig** (Class) `[CRITICAL]`
- **AuthConfig** (Class)
- Holds authentication-related settings.
- **AuthRepository** (Class) `[CRITICAL]`
- **AuthRepository** (Class)
- Encapsulates database operations for authentication.
- 📦 **backend.src.core.auth.config** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.config** (Module)
- Centralized configuration for authentication and authorizati...
- 📦 **backend.src.core.auth.jwt** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.jwt** (Module)
- JWT token generation and validation logic.
- 📦 **backend.src.core.auth.logger** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.logger** (Module)
- Audit logging for security-related events.
- 📦 **backend.src.core.auth.oauth** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.oauth** (Module)
- ADFS OIDC configuration and client using Authlib.
- 📦 **backend.src.core.auth.repository** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.repository** (Module)
- Data access layer for authentication-related entities.
- 📦 **backend.src.core.auth.security** (Module) `[CRITICAL]`
- 📦 **backend.src.core.auth.security** (Module)
- Utility for password hashing and verification using Passlib.
**Dependencies:**
@@ -222,23 +222,23 @@
### 📁 `migration/`
- 🏗️ **Layers:** Core
- 📊 **Tiers:** CRITICAL: 20, TRIVIAL: 1
- 📊 **Tiers:** STANDARD: 20, TRIVIAL: 1
- 📄 **Files:** 4
- 📦 **Entities:** 21
**Key Entities:**
- **MigrationArchiveParser** (Class) `[CRITICAL]`
- **MigrationArchiveParser** (Class)
- Extract normalized dashboards/charts/datasets metadata from ...
- **MigrationDryRunService** (Class) `[CRITICAL]`
- **MigrationDryRunService** (Class)
- Build deterministic diff/risk payload for migration pre-flig...
- 📦 **backend.src.core.migration.__init__** (Module) `[TRIVIAL]`
- Namespace package for migration pre-flight orchestration com...
- 📦 **backend.src.core.migration.archive_parser** (Module) `[CRITICAL]`
- 📦 **backend.src.core.migration.archive_parser** (Module)
- Parse Superset export ZIP archives into normalized object ca...
- 📦 **backend.src.core.migration.dry_run_orchestrator** (Module) `[CRITICAL]`
- 📦 **backend.src.core.migration.dry_run_orchestrator** (Module)
- Compute pre-flight migration diff and risk scoring without a...
- 📦 **backend.src.core.migration.risk_assessor** (Module) `[CRITICAL]`
- 📦 **backend.src.core.migration.risk_assessor** (Module)
- Risk evaluation helpers for migration pre-flight reporting.
**Dependencies:**
@@ -285,24 +285,12 @@
- 🔗 DEPENDS_ON -> TaskLogger, USED_BY -> plugins
- 🔗 DEPENDS_ON -> TaskManager, CALLS -> TaskManager._add_log
### 📁 `__tests__/`
- 🏗️ **Layers:** Unknown
- 📊 **Tiers:** TRIVIAL: 9
- 📄 **Files:** 1
- 📦 **Entities:** 9
**Key Entities:**
- 📦 **test_task_logger** (Module) `[TRIVIAL]`
- Auto-generated module for backend/src/core/task_manager/__te...
### 📁 `utils/`
- 🏗️ **Layers:** Core, Domain, Infra
- 📊 **Tiers:** STANDARD: 50, TRIVIAL: 1
- 📊 **Tiers:** STANDARD: 48, TRIVIAL: 1
- 📄 **Files:** 4
- 📦 **Entities:** 51
- 📦 **Entities:** 49
**Key Entities:**
@@ -338,15 +326,15 @@
### 📁 `models/`
- 🏗️ **Layers:** Domain, Model
- 📊 **Tiers:** CRITICAL: 20, STANDARD: 33, TRIVIAL: 29
- 📄 **Files:** 12
- 📦 **Entities:** 82
- 📊 **Tiers:** CRITICAL: 9, STANDARD: 22, TRIVIAL: 22
- 📄 **Files:** 11
- 📦 **Entities:** 53
**Key Entities:**
- **ADGroupMapping** (Class) `[CRITICAL]`
- **ADGroupMapping** (Class)
- Maps an Active Directory group to a local System Role.
- **AppConfigRecord** (Class) `[CRITICAL]`
- **AppConfigRecord** (Class)
- Stores the single source of truth for application configurat...
- **AssistantAuditRecord** (Class)
- Store audit decisions and outcomes produced by assistant com...
@@ -354,16 +342,16 @@
- Persist risky operation confirmation tokens with lifecycle s...
- **AssistantMessageRecord** (Class)
- Persist chat history entries for assistant conversations.
- **CheckFinalStatus** (Class)
- Final status for compliance check run.
- **CheckStageName** (Class)
- Mandatory check stages.
- **CheckStageResult** (Class)
- Per-stage compliance result.
- **CheckStageStatus** (Class)
- Stage-level execution status.
- **ClassificationType** (Class)
- Manifest classification outcomes for artifacts.
- **ConnectionConfig** (Class) `[TRIVIAL]`
- Stores credentials for external databases used for column ma...
- **DashboardMetadata** (Class) `[TRIVIAL]`
- Represents a dashboard available for migration.
- **DashboardSelection** (Class) `[TRIVIAL]`
- Represents the user's selection of dashboards to migrate.
- **DatabaseMapping** (Class)
- Represents a mapping between source and target databases.
- **DeploymentEnvironment** (Class) `[TRIVIAL]`
- Target Superset environments for dashboard deployment.
**Dependencies:**
@@ -375,15 +363,13 @@
### 📁 `__tests__/`
- 🏗️ **Layers:** Domain, Unknown
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 38
- 📄 **Files:** 3
- 📦 **Entities:** 40
- 🏗️ **Layers:** Domain
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 27
- 📄 **Files:** 2
- 📦 **Entities:** 29
**Key Entities:**
- 📦 **test_clean_release** (Module) `[TRIVIAL]`
- Auto-generated module for backend/src/models/__tests__/test_...
- 📦 **test_models** (Module) `[TRIVIAL]`
- Unit tests for data models
- 📦 **test_report_models** (Module)
@@ -392,7 +378,7 @@
### 📁 `plugins/`
- 🏗️ **Layers:** App, Plugin, Plugins
- 📊 **Tiers:** CRITICAL: 10, STANDARD: 53
- 📊 **Tiers:** STANDARD: 63
- 📄 **Files:** 6
- 📦 **Entities:** 63
@@ -406,7 +392,7 @@
- Реализация плагина Git Integration для управления версиями д...
- **MapperPlugin** (Class)
- Plugin for mapping dataset columns verbose names.
- **MigrationPlugin** (Class) `[CRITICAL]`
- **MigrationPlugin** (Class)
- Implementation of the migration plugin logic.
- **SearchPlugin** (Class)
- Plugin for searching text patterns in Superset datasets.
@@ -416,7 +402,7 @@
- Implements a plugin for system diagnostics and debugging Sup...
- 📦 **MapperPluginModule** (Module)
- Implements a plugin for mapping dataset columns using extern...
- 📦 **MigrationPlugin** (Module) `[CRITICAL]`
- 📦 **MigrationPlugin** (Module)
- A plugin that provides functionality to migrate Superset das...
**Dependencies:**
@@ -495,31 +481,31 @@
### 📁 `schemas/`
- 🏗️ **Layers:** API
- 📊 **Tiers:** CRITICAL: 10, TRIVIAL: 3
- 📊 **Tiers:** STANDARD: 10, TRIVIAL: 3
- 📄 **Files:** 1
- 📦 **Entities:** 13
**Key Entities:**
- **ADGroupMappingCreate** (Class) `[CRITICAL]`
- **ADGroupMappingCreate** (Class)
- Schema for creating an AD Group mapping.
- **ADGroupMappingSchema** (Class) `[CRITICAL]`
- **ADGroupMappingSchema** (Class)
- Represents an AD Group to Role mapping in API responses.
- **PermissionSchema** (Class) `[TRIVIAL]`
- Represents a permission in API responses.
- **RoleCreate** (Class) `[CRITICAL]`
- **RoleCreate** (Class)
- Schema for creating a new role.
- **RoleSchema** (Class) `[CRITICAL]`
- **RoleSchema** (Class)
- Represents a role in API responses.
- **RoleUpdate** (Class) `[CRITICAL]`
- **RoleUpdate** (Class)
- Schema for updating an existing role.
- **Token** (Class) `[TRIVIAL]`
- Represents a JWT access token response.
- **TokenData** (Class) `[TRIVIAL]`
- Represents the data encoded in a JWT token.
- **User** (Class) `[CRITICAL]`
- **User** (Class)
- Schema for user data in API responses.
- **UserBase** (Class) `[CRITICAL]`
- **UserBase** (Class)
- Base schema for user data.
**Dependencies:**
@@ -528,18 +514,16 @@
### 📁 `scripts/`
- 🏗️ **Layers:** Scripts, UI, Unknown
- 📊 **Tiers:** CRITICAL: 2, STANDARD: 25, TRIVIAL: 3
- 📄 **Files:** 7
- 📦 **Entities:** 30
- 🏗️ **Layers:** Scripts, Unknown
- 📊 **Tiers:** STANDARD: 26, TRIVIAL: 2
- 📄 **Files:** 6
- 📦 **Entities:** 28
**Key Entities:**
- 📦 **backend.src.scripts.clean_release_tui** (Module)
- Provide clean release TUI entrypoint placeholder for phased ...
- 📦 **backend.src.scripts.create_admin** (Module)
- CLI tool for creating the initial admin user.
- 📦 **backend.src.scripts.init_auth_db** (Module) `[CRITICAL]`
- 📦 **backend.src.scripts.init_auth_db** (Module)
- Initializes the auth database and creates the necessary tabl...
- 📦 **backend.src.scripts.migrate_sqlite_to_postgres** (Module)
- Migrates legacy config and task history from SQLite/file sto...
@@ -553,13 +537,13 @@
### 📁 `services/`
- 🏗️ **Layers:** Core, Domain, Service
- 📊 **Tiers:** CRITICAL: 7, STANDARD: 76, TRIVIAL: 6
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 62, TRIVIAL: 6
- 📄 **Files:** 7
- 📦 **Entities:** 89
- 📦 **Entities:** 69
**Key Entities:**
- **AuthService** (Class) `[CRITICAL]`
- **AuthService** (Class)
- Provides high-level authentication services.
- **EncryptionManager** (Class) `[CRITICAL]`
- Handles encryption and decryption of sensitive data like API...
@@ -573,7 +557,7 @@
- Provides centralized access to resource data with enhanced m...
- 📦 **backend.src.services** (Module)
- Package initialization for services module
- 📦 **backend.src.services.auth_service** (Module) `[CRITICAL]`
- 📦 **backend.src.services.auth_service** (Module)
- Orchestrates authentication business logic.
- 📦 **backend.src.services.git_service** (Module)
- Core Git logic using GitPython to manage dashboard repositor...
@@ -590,10 +574,10 @@
### 📁 `__tests__/`
- 🏗️ **Layers:** Domain, Domain Tests, Service, Unknown
- 📊 **Tiers:** STANDARD: 24, TRIVIAL: 17
- 📄 **Files:** 4
- 📦 **Entities:** 41
- 🏗️ **Layers:** Domain, Domain Tests, Service
- 📊 **Tiers:** STANDARD: 24, TRIVIAL: 7
- 📄 **Files:** 3
- 📦 **Entities:** 31
**Key Entities:**
@@ -605,76 +589,11 @@
- Unit tests for ResourceService
- 📦 **test_encryption_manager** (Module)
- Unit tests for EncryptionManager encrypt/decrypt functionali...
- 📦 **test_llm_provider** (Module) `[TRIVIAL]`
- Auto-generated module for backend/src/services/__tests__/tes...
**Dependencies:**
- 🔗 DEPENDS_ON -> backend.src.services.llm_prompt_templates
### 📁 `clean_release/`
- 🏗️ **Layers:** Domain, Infra
- 📊 **Tiers:** CRITICAL: 3, STANDARD: 12, TRIVIAL: 33
- 📄 **Files:** 10
- 📦 **Entities:** 48
**Key Entities:**
- **CleanPolicyEngine** (Class)
- 📦 **backend.src.services.clean_release** (Module)
- Initialize clean release service package and provide explici...
- 📦 **backend.src.services.clean_release.audit_service** (Module)
- Provide lightweight audit hooks for clean release preparatio...
- 📦 **backend.src.services.clean_release.compliance_orchestrator** (Module) `[CRITICAL]`
- Execute mandatory clean compliance stages and produce final ...
- 📦 **backend.src.services.clean_release.manifest_builder** (Module)
- Build deterministic distribution manifest from classified ar...
- 📦 **backend.src.services.clean_release.policy_engine** (Module) `[CRITICAL]`
- Evaluate artifact/source policies for enterprise clean profi...
- 📦 **backend.src.services.clean_release.preparation_service** (Module)
- Prepare release candidate by policy evaluation and determini...
- 📦 **backend.src.services.clean_release.report_builder** (Module) `[CRITICAL]`
- Build and persist compliance reports with consistent counter...
- 📦 **backend.src.services.clean_release.repository** (Module)
- Provide repository adapter for clean release entities with d...
- 📦 **backend.src.services.clean_release.source_isolation** (Module)
- Validate that all resource endpoints belong to the approved ...
**Dependencies:**
- 🔗 DEPENDS_ON -> backend.src.core.logger
- 🔗 DEPENDS_ON -> backend.src.models.clean_release
- 🔗 DEPENDS_ON -> backend.src.models.clean_release.CleanProfilePolicy
- 🔗 DEPENDS_ON -> backend.src.models.clean_release.ResourceSourceRegistry
- 🔗 DEPENDS_ON -> backend.src.services.clean_release.manifest_builder
### 📁 `__tests__/`
- 🏗️ **Layers:** Domain, Infra, Unknown
- 📊 **Tiers:** STANDARD: 18, TRIVIAL: 25
- 📄 **Files:** 8
- 📦 **Entities:** 43
**Key Entities:**
- 📦 **backend.tests.services.clean_release.test_audit_service** (Module)
- Validate audit hooks emit expected log patterns for clean re...
- 📦 **backend.tests.services.clean_release.test_compliance_orchestrator** (Module)
- Validate compliance orchestrator stage transitions and final...
- 📦 **backend.tests.services.clean_release.test_manifest_builder** (Module)
- Validate deterministic manifest generation behavior for US1.
- 📦 **backend.tests.services.clean_release.test_preparation_service** (Module)
- Validate release candidate preparation flow, including polic...
- 📦 **backend.tests.services.clean_release.test_report_builder** (Module)
- Validate compliance report builder counter integrity and blo...
- 📦 **backend.tests.services.clean_release.test_source_isolation** (Module)
- Verify internal source registry validation behavior.
- 📦 **backend.tests.services.clean_release.test_stages** (Module)
- Validate final status derivation logic from stage results.
- 📦 **test_policy_engine** (Module) `[TRIVIAL]`
- Auto-generated module for backend/src/services/clean_release...
### 📁 `reports/`
- 🏗️ **Layers:** Domain
@@ -703,10 +622,10 @@
### 📁 `__tests__/`
- 🏗️ **Layers:** Domain, Domain (Tests), Unknown
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 24
- 📄 **Files:** 3
- 📦 **Entities:** 26
- 🏗️ **Layers:** Domain, Domain (Tests)
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 19
- 📄 **Files:** 2
- 📦 **Entities:** 21
**Key Entities:**
@@ -714,33 +633,31 @@
- Validate unknown task type fallback and partial payload norm...
- 📦 **test_report_service** (Module)
- Unit tests for ReportsService list/detail operations
- 📦 **test_type_profiles** (Module) `[TRIVIAL]`
- Auto-generated module for backend/src/services/reports/__tes...
### 📁 `tests/`
- 🏗️ **Layers:** Core, Domain (Tests), Logging (Tests), Test, Unknown
- 📊 **Tiers:** STANDARD: 86, TRIVIAL: 85
- 🏗️ **Layers:** Core, Domain (Tests), Test, Unknown
- 📊 **Tiers:** CRITICAL: 6, STANDARD: 79, TRIVIAL: 85
- 📄 **Files:** 10
- 📦 **Entities:** 171
- 📦 **Entities:** 170
**Key Entities:**
- **TestLogPersistence** (Class)
- **TestLogPersistence** (Class) `[CRITICAL]`
- Test suite for TaskLogPersistenceService.
- **TestTaskContext** (Class)
- Test suite for TaskContext.
- **TestTaskLogger** (Class)
- Test suite for TaskLogger.
- **TestTaskPersistenceHelpers** (Class)
- **TestTaskPersistenceHelpers** (Class) `[CRITICAL]`
- Test suite for TaskPersistenceService static helper methods.
- **TestTaskPersistenceService** (Class)
- **TestTaskPersistenceService** (Class) `[CRITICAL]`
- Test suite for TaskPersistenceService CRUD operations.
- 📦 **backend.tests.test_dashboards_api** (Module)
- Comprehensive contract-driven tests for Dashboard Hub API
- 📦 **test_auth** (Module) `[TRIVIAL]`
- Auto-generated module for backend/tests/test_auth.py
- 📦 **test_log_persistence** (Module)
- 📦 **test_log_persistence** (Module) `[CRITICAL]`
- Unit tests for TaskLogPersistenceService.
- 📦 **test_resource_hubs** (Module) `[TRIVIAL]`
- Auto-generated module for backend/tests/test_resource_hubs.p...
@@ -750,14 +667,12 @@
### 📁 `core/`
- 🏗️ **Layers:** Domain, Unknown
- 📊 **Tiers:** STANDARD: 5, TRIVIAL: 33
- 📄 **Files:** 4
- 📦 **Entities:** 38
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 31
- 📄 **Files:** 3
- 📦 **Entities:** 33
**Key Entities:**
- 📦 **backend.tests.core.test_git_service_gitea_pr** (Module)
- Validate Gitea PR creation fallback behavior when configured...
- 📦 **backend.tests.core.test_mapping_service** (Module)
- Unit tests for the IdMappingService matching UUIDs to intege...
- 📦 **backend.tests.core.test_migration_engine** (Module)
@@ -782,7 +697,7 @@
### 📁 `components/`
- 🏗️ **Layers:** Component, Feature, UI, UI -->, Unknown
- 📊 **Tiers:** STANDARD: 69, TRIVIAL: 4
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 68, TRIVIAL: 4
- 📄 **Files:** 14
- 📦 **Entities:** 73
@@ -836,9 +751,9 @@
### 📁 `git/`
- 🏗️ **Layers:** Component
- 📊 **Tiers:** STANDARD: 45
- 📊 **Tiers:** STANDARD: 28
- 📄 **Files:** 6
- 📦 **Entities:** 45
- 📦 **Entities:** 28
**Key Entities:**
@@ -853,12 +768,12 @@
- 🧩 **DeploymentModal** (Component)
- Modal for deploying a dashboard to a target environment.
- 🧩 **GitManager** (Component)
- Центральный UI управления Git с фокусом на рабочий поток ана...
- Центральный компонент для управления Git-операциями конкретн...
### 📁 `llm/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 1, TRIVIAL: 11
- 📊 **Tiers:** STANDARD: 2, TRIVIAL: 11
- 📄 **Files:** 3
- 📦 **Entities:** 13
@@ -866,7 +781,7 @@
- 🧩 **DocPreview** (Component)
- UI component for previewing generated dataset documentation ...
- 🧩 **ProviderConfig** (Component) `[CRITICAL]`
- 🧩 **ProviderConfig** (Component)
- UI form for managing LLM provider configurations.
- 📦 **DocPreview** (Module) `[TRIVIAL]`
- Auto-generated module for frontend/src/components/llm/DocPre...
@@ -946,9 +861,9 @@
### 📁 `lib/`
- 🏗️ **Layers:** Infra, Infra-API, UI, UI-State
- 📊 **Tiers:** STANDARD: 24, TRIVIAL: 5
- 📊 **Tiers:** STANDARD: 24, TRIVIAL: 3
- 📄 **Files:** 5
- 📦 **Entities:** 29
- 📦 **Entities:** 27
**Key Entities:**
@@ -1003,25 +918,25 @@
### 📁 `auth/`
- 🏗️ **Layers:** Feature
- 📊 **Tiers:** CRITICAL: 7
- 📊 **Tiers:** STANDARD: 7
- 📄 **Files:** 1
- 📦 **Entities:** 7
**Key Entities:**
- 🗄️ **authStore** (Store) `[CRITICAL]`
- 🗄️ **authStore** (Store)
- Manages the global authentication state on the frontend.
### 📁 `assistant/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 14, TRIVIAL: 5
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 13, TRIVIAL: 5
- 📄 **Files:** 1
- 📦 **Entities:** 19
**Key Entities:**
- 🧩 **AssistantChatPanel** (Component)
- 🧩 **AssistantChatPanel** (Component) `[CRITICAL]`
- Slide-out assistant chat panel for natural language command ...
- 📦 **AssistantChatPanel** (Module) `[TRIVIAL]`
- Auto-generated module for frontend/src/lib/components/assist...
@@ -1041,7 +956,7 @@
### 📁 `layout/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 8, TRIVIAL: 48
- 📊 **Tiers:** CRITICAL: 3, STANDARD: 5, TRIVIAL: 48
- 📄 **Files:** 4
- 📦 **Entities:** 56
@@ -1049,11 +964,11 @@
- 🧩 **Breadcrumbs** (Component)
- Display page hierarchy navigation
- 🧩 **Sidebar** (Component)
- 🧩 **Sidebar** (Component) `[CRITICAL]`
- Persistent left sidebar with resource categories navigation
- 🧩 **TaskDrawer** (Component)
- 🧩 **TaskDrawer** (Component) `[CRITICAL]`
- Global task drawer for monitoring background operations
- 🧩 **TopNavbar** (Component)
- 🧩 **TopNavbar** (Component) `[CRITICAL]`
- Unified top navigation bar with Logo, Search, Activity, and ...
- 📦 **Breadcrumbs** (Module) `[TRIVIAL]`
- Auto-generated module for frontend/src/lib/components/layout...
@@ -1079,17 +994,17 @@
### 📁 `reports/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 4, TRIVIAL: 10
- 📊 **Tiers:** CRITICAL: 4, STANDARD: 1, TRIVIAL: 10
- 📄 **Files:** 4
- 📦 **Entities:** 15
**Key Entities:**
- 🧩 **ReportCard** (Component)
- 🧩 **ReportCard** (Component) `[CRITICAL]`
- Render one report with explicit textual type label and profi...
- 🧩 **ReportDetailPanel** (Component)
- 🧩 **ReportDetailPanel** (Component) `[CRITICAL]`
- Display detailed report context with diagnostics and actiona...
- 🧩 **ReportsList** (Component)
- 🧩 **ReportsList** (Component) `[CRITICAL]`
- Render unified list of normalized reports with canonical min...
- 📦 **ReportCard** (Module) `[TRIVIAL]`
- Auto-generated module for frontend/src/lib/components/report...
@@ -1107,9 +1022,9 @@
### 📁 `__tests__/`
- 🏗️ **Layers:** UI, UI (Tests)
- 📊 **Tiers:** STANDARD: 7, TRIVIAL: 4
- 📄 **Files:** 7
- 📦 **Entities:** 11
- 📊 **Tiers:** STANDARD: 6, TRIVIAL: 4
- 📄 **Files:** 6
- 📦 **Entities:** 10
**Key Entities:**
@@ -1123,8 +1038,6 @@
- Validate report type profile mapping and unknown fallback be...
- 📦 **frontend.src.lib.components.reports.__tests__.reports_filter_performance** (Module)
- Guard test for report filter responsiveness on moderate in-m...
- 📦 **frontend.src.lib.components.reports.__tests__.reports_list.ux** (Module)
- Test ReportsList component iteration and event forwarding.
- 📦 **frontend.src.lib.components.reports.__tests__.reports_page.integration** (Module)
- Integration-style checks for unified mixed-type reports rend...
@@ -1347,9 +1260,9 @@
### 📁 `dashboards/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 24, TRIVIAL: 61
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 23, TRIVIAL: 60
- 📄 **Files:** 1
- 📦 **Entities:** 85
- 📦 **Entities:** 84
**Key Entities:**
@@ -1359,9 +1272,9 @@
### 📁 `[id]/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 1, TRIVIAL: 28
- 📊 **Tiers:** CRITICAL: 1, TRIVIAL: 17
- 📄 **Files:** 1
- 📦 **Entities:** 29
- 📦 **Entities:** 18
**Key Entities:**
@@ -1371,7 +1284,7 @@
### 📁 `datasets/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 1, TRIVIAL: 15
- 📊 **Tiers:** CRITICAL: 1, TRIVIAL: 15
- 📄 **Files:** 1
- 📦 **Entities:** 16
@@ -1383,7 +1296,7 @@
### 📁 `[id]/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** STANDARD: 1, TRIVIAL: 6
- 📊 **Tiers:** CRITICAL: 1, TRIVIAL: 6
- 📄 **Files:** 1
- 📦 **Entities:** 7
@@ -1419,26 +1332,26 @@
### 📁 `migration/`
- 🏗️ **Layers:** Page
- 📊 **Tiers:** CRITICAL: 11
- 📊 **Tiers:** STANDARD: 11
- 📄 **Files:** 1
- 📦 **Entities:** 11
**Key Entities:**
- 🧩 **DashboardSelectionSection** (Component) `[CRITICAL]`
- 🧩 **MigrationDashboard** (Component) `[CRITICAL]`
- 🧩 **DashboardSelectionSection** (Component)
- 🧩 **MigrationDashboard** (Component)
- Main dashboard for configuring and starting migrations.
### 📁 `mappings/`
- 🏗️ **Layers:** Page
- 📊 **Tiers:** CRITICAL: 4
- 📊 **Tiers:** STANDARD: 4
- 📄 **Files:** 1
- 📦 **Entities:** 4
**Key Entities:**
- 🧩 **MappingManagement** (Component) `[CRITICAL]`
- 🧩 **MappingManagement** (Component)
- Page for managing database mappings between environments.
### 📁 `reports/`
@@ -1470,9 +1383,9 @@
### 📁 `settings/`
- 🏗️ **Layers:** UI, Unknown
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 1, TRIVIAL: 25
- 📊 **Tiers:** CRITICAL: 1, STANDARD: 1, TRIVIAL: 23
- 📄 **Files:** 2
- 📦 **Entities:** 27
- 📦 **Entities:** 25
**Key Entities:**
@@ -1494,9 +1407,9 @@
### 📁 `git/`
- 🏗️ **Layers:** Page
- 📊 **Tiers:** STANDARD: 8
- 📊 **Tiers:** STANDARD: 5
- 📄 **Files:** 1
- 📦 **Entities:** 8
- 📦 **Entities:** 5
**Key Entities:**
@@ -1555,9 +1468,9 @@
### 📁 `services/`
- 🏗️ **Layers:** Service
- 📊 **Tiers:** STANDARD: 33, TRIVIAL: 1
- 📊 **Tiers:** STANDARD: 33
- 📄 **Files:** 6
- 📦 **Entities:** 34
- 📦 **Entities:** 33
**Key Entities:**
@@ -1587,7 +1500,7 @@
### 📁 `root/`
- 🏗️ **Layers:** DevOps/Tooling, Domain, Unknown
- 📊 **Tiers:** CRITICAL: 11, STANDARD: 27, TRIVIAL: 12
- 📊 **Tiers:** CRITICAL: 14, STANDARD: 24, TRIVIAL: 12
- 📄 **Files:** 4
- 📦 **Entities:** 50
@@ -1595,7 +1508,7 @@
- **ComplianceIssue** (Class) `[TRIVIAL]`
- Represents a single compliance issue with severity.
- **ReportsService** (Class)
- **ReportsService** (Class) `[CRITICAL]`
- Service layer for list/detail report retrieval and normaliza...
- **SemanticEntity** (Class) `[CRITICAL]`
- Represents a code entity (Module, Function, Component) found...
@@ -1605,7 +1518,7 @@
- Severity levels for compliance issues.
- **Tier** (Class) `[TRIVIAL]`
- Enumeration of semantic tiers defining validation strictness...
- 📦 **backend.src.services.reports.report_service** (Module)
- 📦 **backend.src.services.reports.report_service** (Module) `[CRITICAL]`
- Aggregate, normalize, filter, and paginate task reports for ...
- 📦 **check_test_data** (Module) `[TRIVIAL]`
- Auto-generated module for check_test_data.py
@@ -1653,10 +1566,6 @@ graph TD
routes-->|DEPENDS_ON|backend
routes-->|DEPENDS_ON|backend
routes-->|DEPENDS_ON|backend
routes-->|DEPENDS_ON|backend
routes-->|DEPENDS_ON|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
@@ -1716,28 +1625,6 @@ graph TD
__tests__-->|TESTS|backend
__tests__-->|DEPENDS_ON|backend
__tests__-->|TESTS|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
clean_release-->|DEPENDS_ON|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|VERIFIES|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
reports-->|DEPENDS_ON|backend
reports-->|DEPENDS_ON|backend
reports-->|DEPENDS_ON|backend
@@ -1748,13 +1635,11 @@ graph TD
__tests__-->|TESTS|backend
__tests__-->|TESTS|backend
tests-->|TESTS|backend
core-->|TESTS|backend
core-->|VERIFIES|backend
core-->|VERIFIES|backend
migration-->|VERIFIES|backend
migration-->|VERIFIES|backend
__tests__-->|VERIFIES|components
__tests__-->|VERIFIES|components
__tests__-->|VERIFIES|lib
reports-->|DEPENDS_ON|lib
__tests__-->|TESTS|routes

File diff suppressed because it is too large Load Diff

BIN
backend/mappings.db Normal file

Binary file not shown.

View File

@@ -97,17 +97,17 @@ def test_get_dashboards_with_search(mock_deps):
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
async def mock_get_dashboards(env, tasks, include_git_status=False):
async def mock_get_dashboards(env, tasks):
return [
{"id": 1, "title": "Sales Report", "slug": "sales", "git_status": {"branch": "main", "sync_status": "OK"}, "last_task": None},
{"id": 2, "title": "Marketing Dashboard", "slug": "marketing", "git_status": {"branch": "main", "sync_status": "OK"}, "last_task": None}
{"id": 1, "title": "Sales Report", "slug": "sales"},
{"id": 2, "title": "Marketing Dashboard", "slug": "marketing"}
]
mock_deps["resource"].get_dashboards_with_status = AsyncMock(
side_effect=mock_get_dashboards
)
response = client.get("/api/dashboards?env_id=prod&search=sales")
assert response.status_code == 200
data = response.json()
# @POST: Filtered result count must match search

View File

@@ -1,73 +0,0 @@
# [DEF:__tests__/test_tasks_logs:Module]
# @RELATION: VERIFIES -> ../tasks.py
# @PURPOSE: Contract testing for task logs API endpoints.
# [/DEF:__tests__/test_tasks_logs:Module]
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from unittest.mock import MagicMock
from src.dependencies import get_task_manager, has_permission
from src.api.routes.tasks import router
# @TEST_FIXTURE: mock_app
@pytest.fixture
def client():
app = FastAPI()
app.include_router(router, prefix="/tasks")
# Mock TaskManager
mock_tm = MagicMock()
app.dependency_overrides[get_task_manager] = lambda: mock_tm
# Mock permissions (bypass for unit test)
app.dependency_overrides[has_permission("tasks", "READ")] = lambda: True
return TestClient(app), mock_tm
# @TEST_CONTRACT: get_task_logs_api -> Invariants
# @TEST_FIXTURE: valid_task_logs_request
def test_get_task_logs_success(client):
tc, tm = client
# Setup mock task
mock_task = MagicMock()
tm.get_task.return_value = mock_task
tm.get_task_logs.return_value = [{"level": "INFO", "message": "msg1"}]
response = tc.get("/tasks/task-1/logs?level=INFO")
assert response.status_code == 200
assert response.json() == [{"level": "INFO", "message": "msg1"}]
tm.get_task.assert_called_with("task-1")
# Verify filter construction inside route
args = tm.get_task_logs.call_args
assert args[0][0] == "task-1"
assert args[0][1].level == "INFO"
# @TEST_EDGE: task_not_found
def test_get_task_logs_not_found(client):
tc, tm = client
tm.get_task.return_value = None
response = tc.get("/tasks/missing/logs")
assert response.status_code == 404
assert response.json()["detail"] == "Task not found"
# @TEST_EDGE: invalid_limit
def test_get_task_logs_invalid_limit(client):
tc, tm = client
# limit=0 is ge=1 in Query
response = tc.get("/tasks/task-1/logs?limit=0")
assert response.status_code == 422
# @TEST_INVARIANT: response_purity
def test_get_task_log_stats_success(client):
tc, tm = client
tm.get_task.return_value = MagicMock()
tm.get_task_log_stats.return_value = {"INFO": 5, "ERROR": 1}
response = tc.get("/tasks/task-1/logs/stats")
assert response.status_code == 200
# response_model=LogStats might wrap this, but let's check basic structure
# assuming tm.get_task_log_stats returns something compatible with LogStats

View File

@@ -4,30 +4,30 @@
# @PURPOSE: Defines the FastAPI router for task-related endpoints, allowing clients to create, list, and get the status of tasks.
# @LAYER: UI (API)
# @RELATION: Depends on the TaskManager. It is included by the main app.
from typing import List, Dict, Any, Optional
from typing import List, Dict, Any, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from pydantic import BaseModel
from ...core.logger import belief_scope
from ...core.task_manager import TaskManager, Task, TaskStatus, LogEntry
from ...core.task_manager.models import LogFilter, LogStats
from ...dependencies import get_task_manager, has_permission, get_current_user, get_config_manager
from ...core.config_manager import ConfigManager
from ...services.llm_prompt_templates import (
is_multimodal_model,
normalize_llm_settings,
resolve_bound_provider_id,
)
from ...core.task_manager import TaskManager, Task, TaskStatus, LogEntry
from ...core.task_manager.models import LogFilter, LogStats
from ...dependencies import get_task_manager, has_permission, get_current_user, get_config_manager
from ...core.config_manager import ConfigManager
from ...services.llm_prompt_templates import (
is_multimodal_model,
normalize_llm_settings,
resolve_bound_provider_id,
)
router = APIRouter()
TASK_TYPE_PLUGIN_MAP = {
"llm_validation": ["llm_dashboard_validation"],
"backup": ["superset-backup"],
"migration": ["superset-migration"],
}
class CreateTaskRequest(BaseModel):
router = APIRouter()
TASK_TYPE_PLUGIN_MAP = {
"llm_validation": ["llm_dashboard_validation"],
"backup": ["superset-backup"],
"migration": ["superset-migration"],
}
class CreateTaskRequest(BaseModel):
plugin_id: str
params: Dict[str, Any]
@@ -45,54 +45,54 @@ class ResumeTaskRequest(BaseModel):
# @PRE: plugin_id must exist and params must be valid for that plugin.
# @POST: A new task is created and started.
# @RETURN: Task - The created task instance.
async def create_task(
request: CreateTaskRequest,
task_manager: TaskManager = Depends(get_task_manager),
current_user = Depends(get_current_user),
config_manager: ConfigManager = Depends(get_config_manager),
):
async def create_task(
request: CreateTaskRequest,
task_manager: TaskManager = Depends(get_task_manager),
current_user = Depends(get_current_user),
config_manager: ConfigManager = Depends(get_config_manager),
):
# Dynamic permission check based on plugin_id
has_permission(f"plugin:{request.plugin_id}", "EXECUTE")(current_user)
"""
Create and start a new task for a given plugin.
"""
with belief_scope("create_task"):
try:
# Special handling for LLM tasks to resolve provider config by task binding.
if request.plugin_id in {"llm_dashboard_validation", "llm_documentation"}:
from ...core.database import SessionLocal
from ...services.llm_provider import LLMProviderService
db = SessionLocal()
try:
llm_service = LLMProviderService(db)
provider_id = request.params.get("provider_id")
if not provider_id:
llm_settings = normalize_llm_settings(config_manager.get_config().settings.llm)
binding_key = "dashboard_validation" if request.plugin_id == "llm_dashboard_validation" else "documentation"
provider_id = resolve_bound_provider_id(llm_settings, binding_key)
if provider_id:
request.params["provider_id"] = provider_id
if not provider_id:
providers = llm_service.get_all_providers()
active_provider = next((p for p in providers if p.is_active), None)
if active_provider:
provider_id = active_provider.id
request.params["provider_id"] = provider_id
if provider_id:
db_provider = llm_service.get_provider(provider_id)
if not db_provider:
raise ValueError(f"LLM Provider {provider_id} not found")
if request.plugin_id == "llm_dashboard_validation" and not is_multimodal_model(
db_provider.default_model,
db_provider.provider_type,
):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Selected provider model is not multimodal for dashboard validation",
)
finally:
db.close()
try:
# Special handling for LLM tasks to resolve provider config by task binding.
if request.plugin_id in {"llm_dashboard_validation", "llm_documentation"}:
from ...core.database import SessionLocal
from ...services.llm_provider import LLMProviderService
db = SessionLocal()
try:
llm_service = LLMProviderService(db)
provider_id = request.params.get("provider_id")
if not provider_id:
llm_settings = normalize_llm_settings(config_manager.get_config().settings.llm)
binding_key = "dashboard_validation" if request.plugin_id == "llm_dashboard_validation" else "documentation"
provider_id = resolve_bound_provider_id(llm_settings, binding_key)
if provider_id:
request.params["provider_id"] = provider_id
if not provider_id:
providers = llm_service.get_all_providers()
active_provider = next((p for p in providers if p.is_active), None)
if active_provider:
provider_id = active_provider.id
request.params["provider_id"] = provider_id
if provider_id:
db_provider = llm_service.get_provider(provider_id)
if not db_provider:
raise ValueError(f"LLM Provider {provider_id} not found")
if request.plugin_id == "llm_dashboard_validation" and not is_multimodal_model(
db_provider.default_model,
db_provider.provider_type,
):
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Selected provider model is not multimodal for dashboard validation",
)
finally:
db.close()
task = await task_manager.create_task(
plugin_id=request.plugin_id,
@@ -113,36 +113,36 @@ async def create_task(
# @PRE: task_manager must be available.
# @POST: Returns a list of tasks.
# @RETURN: List[Task] - List of tasks.
async def list_tasks(
limit: int = 10,
offset: int = 0,
status_filter: Optional[TaskStatus] = Query(None, alias="status"),
task_type: Optional[str] = Query(None, description="Task category: llm_validation, backup, migration"),
plugin_id: Optional[List[str]] = Query(None, description="Filter by plugin_id (repeatable query param)"),
completed_only: bool = Query(False, description="Return only completed tasks (SUCCESS/FAILED)"),
task_manager: TaskManager = Depends(get_task_manager),
_ = Depends(has_permission("tasks", "READ"))
):
"""
Retrieve a list of tasks with pagination and optional status filter.
"""
with belief_scope("list_tasks"):
plugin_filters = list(plugin_id) if plugin_id else []
if task_type:
if task_type not in TASK_TYPE_PLUGIN_MAP:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unsupported task_type '{task_type}'. Allowed: {', '.join(TASK_TYPE_PLUGIN_MAP.keys())}"
)
plugin_filters.extend(TASK_TYPE_PLUGIN_MAP[task_type])
return task_manager.get_tasks(
limit=limit,
offset=offset,
status=status_filter,
plugin_ids=plugin_filters or None,
completed_only=completed_only
)
async def list_tasks(
limit: int = 10,
offset: int = 0,
status_filter: Optional[TaskStatus] = Query(None, alias="status"),
task_type: Optional[str] = Query(None, description="Task category: llm_validation, backup, migration"),
plugin_id: Optional[List[str]] = Query(None, description="Filter by plugin_id (repeatable query param)"),
completed_only: bool = Query(False, description="Return only completed tasks (SUCCESS/FAILED)"),
task_manager: TaskManager = Depends(get_task_manager),
_ = Depends(has_permission("tasks", "READ"))
):
"""
Retrieve a list of tasks with pagination and optional status filter.
"""
with belief_scope("list_tasks"):
plugin_filters = list(plugin_id) if plugin_id else []
if task_type:
if task_type not in TASK_TYPE_PLUGIN_MAP:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Unsupported task_type '{task_type}'. Allowed: {', '.join(TASK_TYPE_PLUGIN_MAP.keys())}"
)
plugin_filters.extend(TASK_TYPE_PLUGIN_MAP[task_type])
return task_manager.get_tasks(
limit=limit,
offset=offset,
status=status_filter,
plugin_ids=plugin_filters or None,
completed_only=completed_only
)
# [/DEF:list_tasks:Function]
@router.get("/{task_id}", response_model=Task)
@@ -182,23 +182,6 @@ async def get_task(
# @POST: Returns a list of log entries or raises 404.
# @RETURN: List[LogEntry] - List of log entries.
# @TIER: CRITICAL
# @TEST_CONTRACT get_task_logs_api ->
# {
# required_params: {task_id: str},
# optional_params: {level: str, source: str, search: str},
# invariants: ["returns 404 for non-existent task", "applies filters correctly"]
# }
# @TEST_FIXTURE valid_task_logs_request -> {"task_id": "test_1", "level": "INFO"}
# @TEST_EDGE task_not_found -> raises 404
# @TEST_EDGE invalid_limit -> Query(limit=0) returns 422
# @TEST_INVARIANT response_purity -> verifies: [valid_task_logs_request]
# @TEST_CONTRACT: TaskLogQueryInput -> List[LogEntry]
# @TEST_SCENARIO: existing_task_logs_filtered -> Returns filtered logs by level/source/search with pagination.
# @TEST_FIXTURE: valid_task_with_mixed_logs -> backend/tests/fixtures/task_logs/valid_task_with_mixed_logs.json
# @TEST_EDGE: missing_task -> Unknown task_id returns 404 Task not found.
# @TEST_EDGE: invalid_level_type -> Non-string/invalid level query rejected by validation or yields empty result.
# @TEST_EDGE: pagination_bounds -> offset=0 and limit=1000 remain within API bounds and do not overflow.
# @TEST_INVARIANT: logs_only_for_existing_task -> VERIFIED_BY: [existing_task_logs_filtered, missing_task]
async def get_task_logs(
task_id: str,
level: Optional[str] = Query(None, description="Filter by log level (DEBUG, INFO, WARNING, ERROR)"),
@@ -345,4 +328,4 @@ async def clear_tasks(
task_manager.clear_tasks(status)
return
# [/DEF:clear_tasks:Function]
# [/DEF:TasksRouter:Module]
# [/DEF:TasksRouter:Module]

View File

@@ -1,102 +0,0 @@
# [DEF:__tests__/test_task_logger:Module]
# @RELATION: VERIFIES -> ../task_logger.py
# @PURPOSE: Contract testing for TaskLogger
# [/DEF:__tests__/test_task_logger:Module]
import pytest
from unittest.mock import MagicMock
from src.core.task_manager.task_logger import TaskLogger
# @TEST_FIXTURE: valid_task_logger -> {"task_id": "test_123", "add_log_fn": lambda *args: None, "source": "test_plugin"}
@pytest.fixture
def mock_add_log():
return MagicMock()
@pytest.fixture
def task_logger(mock_add_log):
return TaskLogger(task_id="test_123", add_log_fn=mock_add_log, source="test_plugin")
# @TEST_CONTRACT: TaskLoggerModel -> Invariants
def test_task_logger_initialization(task_logger):
"""Verify TaskLogger is bound to specific task_id and source."""
assert task_logger._task_id == "test_123"
assert task_logger._default_source == "test_plugin"
# @TEST_CONTRACT: invariants -> "All specific log methods (info, error) delegate to _log"
def test_log_methods_delegation(task_logger, mock_add_log):
"""Verify info, error, warning, debug delegate to internal _log."""
task_logger.info("info message", metadata={"k": "v"})
mock_add_log.assert_called_with(
task_id="test_123",
level="INFO",
message="info message",
source="test_plugin",
metadata={"k": "v"}
)
task_logger.error("error message", source="override")
mock_add_log.assert_called_with(
task_id="test_123",
level="ERROR",
message="error message",
source="override",
metadata=None
)
task_logger.warning("warning message")
mock_add_log.assert_called_with(
task_id="test_123",
level="WARNING",
message="warning message",
source="test_plugin",
metadata=None
)
task_logger.debug("debug message")
mock_add_log.assert_called_with(
task_id="test_123",
level="DEBUG",
message="debug message",
source="test_plugin",
metadata=None
)
# @TEST_CONTRACT: invariants -> "with_source creates a new logger with the same task_id"
def test_with_source(task_logger):
"""Verify with_source returns a new instance with updated default source."""
new_logger = task_logger.with_source("new_source")
assert isinstance(new_logger, TaskLogger)
assert new_logger._task_id == "test_123"
assert new_logger._default_source == "new_source"
assert new_logger is not task_logger
# @TEST_EDGE: missing_task_id -> raises TypeError
def test_missing_task_id():
with pytest.raises(TypeError):
TaskLogger(add_log_fn=lambda x: x)
# @TEST_EDGE: invalid_add_log_fn -> raises TypeError
# (Python doesn't strictly enforce this at init, but let's verify it fails on call if not callable)
def test_invalid_add_log_fn():
logger = TaskLogger(task_id="msg", add_log_fn=None)
with pytest.raises(TypeError):
logger.info("test")
# @TEST_INVARIANT: consistent_delegation
def test_progress_log(task_logger, mock_add_log):
"""Verify progress method correctly formats metadata."""
task_logger.progress("Step 1", 45.5)
mock_add_log.assert_called_with(
task_id="test_123",
level="INFO",
message="Step 1",
source="test_plugin",
metadata={"progress": 45.5}
)
# Boundary checks
task_logger.progress("Step high", 150)
assert mock_add_log.call_args[1]["metadata"]["progress"] == 100
task_logger.progress("Step low", -10)
assert mock_add_log.call_args[1]["metadata"]["progress"] == 0

View File

@@ -1,149 +0,0 @@
# [DEF:__tests__/test_clean_release:Module]
# @RELATION: VERIFIES -> ../clean_release.py
# @PURPOSE: Contract testing for Clean Release models
# [/DEF:__tests__/test_clean_release:Module]
import pytest
from datetime import datetime
from pydantic import ValidationError
from src.models.clean_release import (
ReleaseCandidate,
ReleaseCandidateStatus,
ProfileType,
CleanProfilePolicy,
DistributionManifest,
ManifestItem,
ManifestSummary,
ClassificationType,
ComplianceCheckRun,
CheckFinalStatus,
CheckStageResult,
CheckStageName,
CheckStageStatus,
ComplianceReport,
ExecutionMode
)
# @TEST_FIXTURE: valid_enterprise_candidate
@pytest.fixture
def valid_candidate_data():
return {
"candidate_id": "RC-001",
"version": "1.0.0",
"profile": ProfileType.ENTERPRISE_CLEAN,
"created_at": datetime.now(),
"created_by": "admin",
"source_snapshot_ref": "v1.0.0-snapshot"
}
def test_release_candidate_valid(valid_candidate_data):
rc = ReleaseCandidate(**valid_candidate_data)
assert rc.candidate_id == "RC-001"
assert rc.status == ReleaseCandidateStatus.DRAFT
def test_release_candidate_empty_id(valid_candidate_data):
valid_candidate_data["candidate_id"] = " "
with pytest.raises(ValueError, match="candidate_id must be non-empty"):
ReleaseCandidate(**valid_candidate_data)
# @TEST_FIXTURE: valid_enterprise_policy
@pytest.fixture
def valid_policy_data():
return {
"policy_id": "POL-001",
"policy_version": "1",
"active": True,
"prohibited_artifact_categories": ["test-data"],
"required_system_categories": ["core"],
"internal_source_registry_ref": "REG-1",
"effective_from": datetime.now(),
"profile": ProfileType.ENTERPRISE_CLEAN
}
# @TEST_INVARIANT: policy_purity
def test_enterprise_policy_valid(valid_policy_data):
policy = CleanProfilePolicy(**valid_policy_data)
assert policy.external_source_forbidden is True
# @TEST_EDGE: enterprise_policy_missing_prohibited
def test_enterprise_policy_missing_prohibited(valid_policy_data):
valid_policy_data["prohibited_artifact_categories"] = []
with pytest.raises(ValueError, match="enterprise-clean policy requires prohibited_artifact_categories"):
CleanProfilePolicy(**valid_policy_data)
# @TEST_EDGE: enterprise_policy_external_allowed
def test_enterprise_policy_external_allowed(valid_policy_data):
valid_policy_data["external_source_forbidden"] = False
with pytest.raises(ValueError, match="enterprise-clean policy requires external_source_forbidden=true"):
CleanProfilePolicy(**valid_policy_data)
# @TEST_INVARIANT: manifest_consistency
# @TEST_EDGE: manifest_count_mismatch
def test_manifest_count_mismatch():
summary = ManifestSummary(included_count=1, excluded_count=0, prohibited_detected_count=0)
item = ManifestItem(path="p", category="c", classification=ClassificationType.ALLOWED, reason="r")
# Valid
DistributionManifest(
manifest_id="m1", candidate_id="rc1", policy_id="p1",
generated_at=datetime.now(), generated_by="u", items=[item],
summary=summary, deterministic_hash="h"
)
# Invalid count
summary.included_count = 2
with pytest.raises(ValueError, match="manifest summary counts must match items size"):
DistributionManifest(
manifest_id="m1", candidate_id="rc1", policy_id="p1",
generated_at=datetime.now(), generated_by="u", items=[item],
summary=summary, deterministic_hash="h"
)
# @TEST_INVARIANT: run_integrity
# @TEST_EDGE: compliant_run_stage_fail
def test_compliant_run_validation():
base_run = {
"check_run_id": "run1",
"candidate_id": "rc1",
"policy_id": "p1",
"started_at": datetime.now(),
"triggered_by": "u",
"execution_mode": ExecutionMode.TUI,
"final_status": CheckFinalStatus.COMPLIANT,
"checks": [
CheckStageResult(stage=CheckStageName.DATA_PURITY, status=CheckStageStatus.PASS),
CheckStageResult(stage=CheckStageName.INTERNAL_SOURCES_ONLY, status=CheckStageStatus.PASS),
CheckStageResult(stage=CheckStageName.NO_EXTERNAL_ENDPOINTS, status=CheckStageStatus.PASS),
CheckStageResult(stage=CheckStageName.MANIFEST_CONSISTENCY, status=CheckStageStatus.PASS),
]
}
# Valid
ComplianceCheckRun(**base_run)
# One stage fails -> cannot be COMPLIANT
base_run["checks"][0].status = CheckStageStatus.FAIL
with pytest.raises(ValueError, match="compliant run requires PASS on all mandatory stages"):
ComplianceCheckRun(**base_run)
# Missing stage -> cannot be COMPLIANT
base_run["checks"] = base_run["checks"][1:]
with pytest.raises(ValueError, match="compliant run requires all mandatory stages"):
ComplianceCheckRun(**base_run)
def test_report_validation():
# Valid blocked report
ComplianceReport(
report_id="rep1", check_run_id="run1", candidate_id="rc1",
generated_at=datetime.now(), final_status=CheckFinalStatus.BLOCKED,
operator_summary="Blocked", structured_payload_ref="ref",
violations_count=2, blocking_violations_count=2
)
# BLOCKED with 0 blocking violations
with pytest.raises(ValueError, match="blocked report requires blocking violations"):
ComplianceReport(
report_id="rep1", check_run_id="run1", candidate_id="rc1",
generated_at=datetime.now(), final_status=CheckFinalStatus.BLOCKED,
operator_summary="Blocked", structured_payload_ref="ref",
violations_count=2, blocking_violations_count=0
)

View File

@@ -5,35 +5,6 @@
# @LAYER: Domain
# @RELATION: BINDS_TO -> specs/023-clean-repo-enterprise/data-model.md
# @INVARIANT: Enterprise-clean policy always forbids external sources.
#
# @TEST_CONTRACT CleanReleaseModels ->
# {
# required_fields: {
# ReleaseCandidate: [candidate_id, version, profile, source_snapshot_ref],
# CleanProfilePolicy: [policy_id, policy_version, internal_source_registry_ref]
# },
# invariants: [
# "enterprise-clean profile enforces external_source_forbidden=True",
# "manifest summary counts are consistent with items",
# "compliant run requires all mandatory stages to pass"
# ]
# }
# @TEST_FIXTURE valid_enterprise_candidate -> {"candidate_id": "RC-001", "version": "1.0.0", "profile": "enterprise-clean", "source_snapshot_ref": "v1.0.0-snapshot"}
# @TEST_FIXTURE valid_enterprise_policy -> {"policy_id": "POL-001", "policy_version": "1", "internal_source_registry_ref": "REG-1", "prohibited_artifact_categories": ["test-data"]}
# @TEST_EDGE enterprise_policy_missing_prohibited -> profile=enterprise-clean with empty prohibited_artifact_categories raises ValueError
# @TEST_EDGE enterprise_policy_external_allowed -> profile=enterprise-clean with external_source_forbidden=False raises ValueError
# @TEST_EDGE manifest_count_mismatch -> included + excluded != len(items) raises ValueError
# @TEST_EDGE compliant_run_stage_fail -> COMPLIANT run with failed stage raises ValueError
# @TEST_INVARIANT policy_purity -> verifies: [valid_enterprise_policy, enterprise_policy_external_allowed]
# @TEST_INVARIANT manifest_consistency -> verifies: [manifest_count_mismatch]
# @TEST_INVARIANT run_integrity -> verifies: [compliant_run_stage_fail]
# @TEST_CONTRACT: CleanReleaseModelPayload -> ValidatedCleanReleaseModel | ValidationError
# @TEST_SCENARIO: valid_enterprise_models -> CRITICAL entities validate and preserve lifecycle/compliance invariants.
# @TEST_FIXTURE: clean_release_models_baseline -> backend/tests/fixtures/clean_release/fixtures_clean_release.json
# @TEST_EDGE: empty_required_identifiers -> Empty candidate_id/source_snapshot_ref/internal_source_registry_ref fails validation.
# @TEST_EDGE: compliant_run_missing_mandatory_stage -> COMPLIANT run without all mandatory PASS stages fails validation.
# @TEST_EDGE: blocked_report_without_blocking_violations -> BLOCKED report with zero blocking violations fails validation.
# @TEST_INVARIANT: external_source_must_block -> VERIFIED_BY: [valid_enterprise_models, blocked_report_without_blocking_violations]
from __future__ import annotations

View File

@@ -9,8 +9,8 @@
"last_name": "Admin"
},
"changed_by_name": "Superset Admin",
"changed_on": "2026-02-24T19:24:01.850617",
"changed_on_delta_humanized": "7 days ago",
"changed_on": "2026-02-10T13:39:35.945662",
"changed_on_delta_humanized": "16 days ago",
"charts": [
"TA-0001-001 test_chart"
],
@@ -19,12 +19,12 @@
"id": 1,
"last_name": "Admin"
},
"created_on_delta_humanized": "13 days ago",
"created_on_delta_humanized": "16 days ago",
"css": null,
"dashboard_title": "TA-0001 Test dashboard",
"id": 13,
"is_managed_externally": false,
"json_metadata": "{\"color_scheme_domain\": [], \"shared_label_colors\": [], \"map_label_colors\": {}, \"label_colors\": {}, \"native_filter_configuration\": []}",
"json_metadata": "{\"color_scheme_domain\": [], \"shared_label_colors\": [], \"map_label_colors\": {}, \"label_colors\": {}}",
"owners": [
{
"first_name": "Superset",
@@ -32,13 +32,13 @@
"last_name": "Admin"
}
],
"position_json": "{\"DASHBOARD_VERSION_KEY\": \"v2\", \"ROOT_ID\": {\"children\": [\"GRID_ID\"], \"id\": \"ROOT_ID\", \"type\": \"ROOT\"}, \"GRID_ID\": {\"children\": [\"ROW-N-LH8TG1XX\"], \"id\": \"GRID_ID\", \"parents\": [\"ROOT_ID\"], \"type\": \"GRID\"}, \"HEADER_ID\": {\"id\": \"HEADER_ID\", \"meta\": {\"text\": \"TA-0001 Test dashboard\"}, \"type\": \"HEADER\"}, \"ROW-N-LH8TG1XX\": {\"children\": [\"CHART-1EKC8H7C\"], \"id\": \"ROW-N-LH8TG1XX\", \"meta\": {\"0\": \"ROOT_ID\", \"background\": \"BACKGROUND_TRANSPARENT\"}, \"type\": \"ROW\", \"parents\": [\"ROOT_ID\", \"GRID_ID\"]}, \"CHART-1EKC8H7C\": {\"children\": [], \"id\": \"CHART-1EKC8H7C\", \"meta\": {\"chartId\": 162, \"height\": 50, \"sliceName\": \"TA-0001-001 test_chart\", \"uuid\": \"008cdaa7-21b3-4042-9f55-f15653609ebd\", \"width\": 4}, \"type\": \"CHART\", \"parents\": [\"ROOT_ID\", \"GRID_ID\", \"ROW-N-LH8TG1XX\"]}}",
"position_json": null,
"published": true,
"roles": [],
"slug": null,
"tags": [],
"theme": null,
"thumbnail_url": "/api/v1/dashboard/13/thumbnail/97dfd5d8d24f7cf01de45671c9a0699d/",
"thumbnail_url": "/api/v1/dashboard/13/thumbnail/3cfc57e6aea7188b139f94fb437a1426/",
"url": "/superset/dashboard/13/",
"uuid": "124b28d4-d54a-4ade-ade7-2d0473b90686"
}
@@ -53,15 +53,15 @@
"first_name": "Superset",
"last_name": "Admin"
},
"changed_on": "2026-02-18T14:56:04.863722",
"changed_on_humanized": "13 days ago",
"changed_on": "2026-02-10T13:38:26.175551",
"changed_on_humanized": "16 days ago",
"column_formats": {},
"columns": [
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.382289",
"column_name": "has_2fa",
"created_on": "2026-02-18T14:56:05.382138",
"changed_on": "2026-02-10T13:38:26.158196",
"column_name": "color",
"created_on": "2026-02-10T13:38:26.158189",
"description": null,
"expression": null,
"extra": null,
@@ -71,16 +71,16 @@
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "fe374f2a-9e06-4708-89fd-c3926e3e5faa",
"type": "STRING",
"type_generic": 1,
"uuid": "4fa810ee-99cc-4d1f-8c0d-0f289c3b01f4",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.545701",
"column_name": "is_ultra_restricted",
"created_on": "2026-02-18T14:56:05.545465",
"changed_on": "2026-02-10T13:38:26.158249",
"column_name": "deleted",
"created_on": "2026-02-10T13:38:26.158245",
"description": null,
"expression": null,
"extra": null,
@@ -92,14 +92,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "eac7ecce-d472-4933-9652-d4f2811074fd",
"uuid": "ebc07e82-7250-4eef-8d13-ea61561fa52c",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.683578",
"column_name": "is_primary_owner",
"created_on": "2026-02-18T14:56:05.683257",
"changed_on": "2026-02-10T13:38:26.158289",
"column_name": "has_2fa",
"created_on": "2026-02-10T13:38:26.158285",
"description": null,
"expression": null,
"extra": null,
@@ -111,14 +111,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "94a15acd-ef98-425b-8f0d-1ce038ca95c5",
"uuid": "08e72f4d-3ced-4d9a-9f7d-2f85291ce88b",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.758231",
"column_name": "is_app_user",
"created_on": "2026-02-18T14:56:05.758142",
"changed_on": "2026-02-10T13:38:26.158328",
"column_name": "id",
"created_on": "2026-02-10T13:38:26.158324",
"description": null,
"expression": null,
"extra": null,
@@ -128,16 +128,16 @@
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "d3fcd712-dc96-4bba-a026-aa82022eccf5",
"type": "STRING",
"type_generic": 1,
"uuid": "fd11955c-0130-4ea1-b3c0-d8b159971789",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.799597",
"changed_on": "2026-02-10T13:38:26.158366",
"column_name": "is_admin",
"created_on": "2026-02-18T14:56:05.799519",
"created_on": "2026-02-10T13:38:26.158362",
"description": null,
"expression": null,
"extra": null,
@@ -149,14 +149,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "5a1c9de5-80f1-4fe8-a91b-e6e530688aae",
"uuid": "13a6c8e1-c9f8-4f08-aa62-05bca7be547b",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.819443",
"column_name": "is_bot",
"created_on": "2026-02-18T14:56:05.819382",
"changed_on": "2026-02-10T13:38:26.158404",
"column_name": "is_app_user",
"created_on": "2026-02-10T13:38:26.158400",
"description": null,
"expression": null,
"extra": null,
@@ -168,14 +168,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "6c93e5de-e0d7-430c-88d7-87158905d60a",
"uuid": "6321ba8a-28d7-4d68-a6b3-5cef6cd681a2",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.827568",
"column_name": "is_restricted",
"created_on": "2026-02-18T14:56:05.827556",
"changed_on": "2026-02-10T13:38:26.158442",
"column_name": "is_bot",
"created_on": "2026-02-10T13:38:26.158438",
"description": null,
"expression": null,
"extra": null,
@@ -187,14 +187,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "2e8e6d32-0124-4e3a-a53f-6f200f852439",
"uuid": "f3ded50e-b1a2-4a88-b805-781d5923e062",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.835380",
"changed_on": "2026-02-10T13:38:26.158480",
"column_name": "is_owner",
"created_on": "2026-02-18T14:56:05.835366",
"created_on": "2026-02-10T13:38:26.158477",
"description": null,
"expression": null,
"extra": null,
@@ -206,14 +206,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "510d651b-a595-4261-98e4-278af0a06594",
"uuid": "8a1408eb-050d-4455-878c-22342df5da3d",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.843802",
"column_name": "deleted",
"created_on": "2026-02-18T14:56:05.843784",
"changed_on": "2026-02-10T13:38:26.158532",
"column_name": "is_primary_owner",
"created_on": "2026-02-10T13:38:26.158528",
"description": null,
"expression": null,
"extra": null,
@@ -225,14 +225,14 @@
"python_date_format": null,
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "2653fd2f-c0ce-484e-a5df-d2515b1e822d",
"uuid": "054b8c16-82fd-480c-82e0-a0975229673a",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.851074",
"column_name": "updated",
"created_on": "2026-02-18T14:56:05.851063",
"changed_on": "2026-02-10T13:38:26.158583",
"column_name": "is_restricted",
"created_on": "2026-02-10T13:38:26.158579",
"description": null,
"expression": null,
"extra": null,
@@ -240,18 +240,18 @@
"groupby": true,
"id": 781,
"is_active": true,
"is_dttm": true,
"is_dttm": false,
"python_date_format": null,
"type": "DATETIME",
"type_generic": 2,
"uuid": "1b1f90c8-2567-49b8-9398-e7246396461e",
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "6932c25f-0273-4595-85c1-29422a801ded",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.857578",
"column_name": "tz_offset",
"created_on": "2026-02-18T14:56:05.857571",
"changed_on": "2026-02-10T13:38:26.158621",
"column_name": "is_ultra_restricted",
"created_on": "2026-02-10T13:38:26.158618",
"description": null,
"expression": null,
"extra": null,
@@ -261,16 +261,16 @@
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "LONGINTEGER",
"type_generic": 0,
"uuid": "e6d19b74-7f5d-447b-8071-951961dc2295",
"type": "BOOLEAN",
"type_generic": 3,
"uuid": "9b14e5f9-3ab4-498e-b1e3-bbf49e9d61fe",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.863101",
"column_name": "channel_name",
"created_on": "2026-02-18T14:56:05.863094",
"changed_on": "2026-02-10T13:38:26.158660",
"column_name": "name",
"created_on": "2026-02-10T13:38:26.158656",
"description": null,
"expression": null,
"extra": null,
@@ -282,14 +282,14 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "e1f34628-ebc1-4e0c-8eea-54c3c9efba1b",
"uuid": "ebee8249-0e10-4157-8a8e-96ae107887a3",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.877136",
"changed_on": "2026-02-10T13:38:26.158697",
"column_name": "real_name",
"created_on": "2026-02-18T14:56:05.877083",
"created_on": "2026-02-10T13:38:26.158694",
"description": null,
"expression": null,
"extra": null,
@@ -301,14 +301,14 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "6cc5ab57-9431-428a-a331-0a5b10e4b074",
"uuid": "553517a0-fe05-4ff5-a4eb-e9d2165d6f64",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.893859",
"column_name": "tz_label",
"created_on": "2026-02-18T14:56:05.893834",
"changed_on": "2026-02-10T13:38:26.158735",
"column_name": "team_id",
"created_on": "2026-02-10T13:38:26.158731",
"description": null,
"expression": null,
"extra": null,
@@ -320,14 +320,14 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "8e6dbd8e-b880-4517-a5f6-64e429bd1bea",
"uuid": "6c207fac-424d-465c-b80a-306b42b55ce8",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.902363",
"column_name": "team_id",
"created_on": "2026-02-18T14:56:05.902352",
"changed_on": "2026-02-10T13:38:26.158773",
"column_name": "tz",
"created_on": "2026-02-10T13:38:26.158769",
"description": null,
"expression": null,
"extra": null,
@@ -339,14 +339,14 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "ba8e225d-221b-4275-aadb-e79557756f89",
"uuid": "6efcc042-0b78-4362-9373-2f684077d574",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.910169",
"column_name": "name",
"created_on": "2026-02-18T14:56:05.910151",
"changed_on": "2026-02-10T13:38:26.158824",
"column_name": "tz_label",
"created_on": "2026-02-10T13:38:26.158820",
"description": null,
"expression": null,
"extra": null,
@@ -358,14 +358,14 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "02a7a026-d9f3-49e9-9586-534ebccdd867",
"uuid": "c6a6ac40-5c60-472d-a878-4b65b8460ccc",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.915366",
"column_name": "color",
"created_on": "2026-02-18T14:56:05.915357",
"changed_on": "2026-02-10T13:38:26.158861",
"column_name": "tz_offset",
"created_on": "2026-02-10T13:38:26.158857",
"description": null,
"expression": null,
"extra": null,
@@ -375,16 +375,16 @@
"is_active": true,
"is_dttm": false,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "0702fcdf-2d03-45db-8496-697d47b300d6",
"type": "LONGINTEGER",
"type_generic": 0,
"uuid": "cf6da93a-bba9-47df-9154-6cfd0c9922fc",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.919466",
"column_name": "id",
"created_on": "2026-02-18T14:56:05.919460",
"changed_on": "2026-02-10T13:38:26.158913",
"column_name": "updated",
"created_on": "2026-02-10T13:38:26.158909",
"description": null,
"expression": null,
"extra": null,
@@ -392,18 +392,18 @@
"groupby": true,
"id": 789,
"is_active": true,
"is_dttm": false,
"is_dttm": true,
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "a4b58528-fcbf-45e9-af39-fe9d737ba380",
"type": "DATETIME",
"type_generic": 2,
"uuid": "2aa0a72a-5602-4799-b5ab-f22000108d62",
"verbose_name": null
},
{
"advanced_data_type": null,
"changed_on": "2026-02-18T14:56:05.932553",
"column_name": "tz",
"created_on": "2026-02-18T14:56:05.932530",
"changed_on": "2026-02-10T13:38:26.158967",
"column_name": "channel_name",
"created_on": "2026-02-10T13:38:26.158963",
"description": null,
"expression": null,
"extra": null,
@@ -415,7 +415,7 @@
"python_date_format": null,
"type": "STRING",
"type_generic": 1,
"uuid": "bc872357-1920-42f3-aeda-b596122bcdb8",
"uuid": "a84bd658-c83c-4e7f-9e1b-192595092d9b",
"verbose_name": null
}
],
@@ -423,8 +423,8 @@
"first_name": "Superset",
"last_name": "Admin"
},
"created_on": "2026-02-18T14:56:04.317950",
"created_on_humanized": "13 days ago",
"created_on": "2026-02-10T13:38:26.050436",
"created_on_humanized": "16 days ago",
"database": {
"allow_multi_catalog": false,
"backend": "postgresql",
@@ -452,8 +452,8 @@
"main_dttm_col": "updated",
"metrics": [
{
"changed_on": "2026-02-18T14:56:05.085244",
"created_on": "2026-02-18T14:56:05.085166",
"changed_on": "2026-02-10T13:38:26.182269",
"created_on": "2026-02-10T13:38:26.182264",
"currency": null,
"d3format": null,
"description": null,
@@ -462,7 +462,7 @@
"id": 33,
"metric_name": "count",
"metric_type": "count",
"uuid": "10c8b8cf-b697-4512-9e9e-2996721f829e",
"uuid": "7510f8ca-05ee-4a37-bec1-4a5d7bf2ac50",
"verbose_name": "COUNT(*)",
"warning_text": null
}

View File

@@ -100,10 +100,7 @@ def test_dashboard_dataset_relations():
logger.info(f" Found {len(dashboards)} dashboards using this dataset:")
for dash in dashboards:
if isinstance(dash, dict):
logger.info(f" - Dashboard ID {dash.get('id')}: {dash.get('dashboard_title', dash.get('title', 'Unknown'))}")
else:
logger.info(f" - Dashboard: {dash}")
logger.info(f" - Dashboard ID {dash.get('id')}: {dash.get('dashboard_title', dash.get('title', 'Unknown'))}")
elif 'result' in related_objects:
# Some Superset versions use 'result' wrapper
result = related_objects['result']

View File

@@ -27,7 +27,7 @@ class TestEncryptionManager:
# Re-implement the same logic as EncryptionManager to avoid import issues
# with the llm_provider module's relative imports
import os
key = os.getenv("ENCRYPTION_KEY", "REMOVED_HISTORICAL_SECRET_DO_NOT_USE").encode()
key = os.getenv("ENCRYPTION_KEY", "ZcytYzi0iHIl4Ttr-GdAEk117aGRogkGvN3wiTxrPpE=").encode()
fernet = Fernet(key)
class EncryptionManager:

View File

@@ -1,81 +0,0 @@
# [DEF:__tests__/test_llm_provider:Module]
# @RELATION: VERIFIES -> ../llm_provider.py
# @PURPOSE: Contract testing for LLMProviderService and EncryptionManager
# [/DEF:__tests__/test_llm_provider:Module]
import pytest
import os
from unittest.mock import MagicMock
from sqlalchemy.orm import Session
from src.services.llm_provider import EncryptionManager, LLMProviderService
from src.models.llm import LLMProvider
from src.plugins.llm_analysis.models import LLMProviderConfig, ProviderType
# @TEST_CONTRACT: EncryptionManagerModel -> Invariants
# @TEST_INVARIANT: symmetric_encryption
def test_encryption_cycle():
"""Verify encrypted data can be decrypted back to original string."""
manager = EncryptionManager()
original = "secret_api_key_123"
encrypted = manager.encrypt(original)
assert encrypted != original
assert manager.decrypt(encrypted) == original
# @TEST_EDGE: empty_string_encryption
def test_empty_string_encryption():
manager = EncryptionManager()
original = ""
encrypted = manager.encrypt(original)
assert manager.decrypt(encrypted) == ""
# @TEST_EDGE: decrypt_invalid_data
def test_decrypt_invalid_data():
manager = EncryptionManager()
with pytest.raises(Exception):
manager.decrypt("not-encrypted-string")
# @TEST_FIXTURE: mock_db_session
@pytest.fixture
def mock_db():
return MagicMock(spec=Session)
@pytest.fixture
def service(mock_db):
return LLMProviderService(db=mock_db)
def test_get_all_providers(service, mock_db):
service.get_all_providers()
mock_db.query.assert_called()
mock_db.query().all.assert_called()
def test_create_provider(service, mock_db):
config = LLMProviderConfig(
provider_type=ProviderType.OPENAI,
name="Test OpenAI",
base_url="https://api.openai.com",
api_key="sk-test",
default_model="gpt-4",
is_active=True
)
provider = service.create_provider(config)
mock_db.add.assert_called()
mock_db.commit.assert_called()
# Verify API key was encrypted
assert provider.api_key != "sk-test"
# Decrypt to verify it matches
assert EncryptionManager().decrypt(provider.api_key) == "sk-test"
def test_get_decrypted_api_key(service, mock_db):
# Setup mock provider
encrypted_key = EncryptionManager().encrypt("secret-value")
mock_provider = LLMProvider(id="p1", api_key=encrypted_key)
mock_db.query().filter().first.return_value = mock_provider
key = service.get_decrypted_api_key("p1")
assert key == "secret-value"
def test_get_decrypted_api_key_not_found(service, mock_db):
mock_db.query().filter().first.return_value = None
assert service.get_decrypted_api_key("missing") is None

View File

@@ -1,24 +0,0 @@
# [DEF:backend.tests.services.clean_release.test_audit_service:Module]
# @TIER: STANDARD
# @SEMANTICS: tests, clean-release, audit, logging
# @PURPOSE: Validate audit hooks emit expected log patterns for clean release lifecycle.
# @LAYER: Infra
# @RELATION: TESTS -> backend.src.services.clean_release.audit_service
from unittest.mock import patch
from src.services.clean_release.audit_service import audit_preparation, audit_check_run, audit_report
@patch("src.services.clean_release.audit_service.logger")
def test_audit_preparation(mock_logger):
audit_preparation("cand-1", "PREPARED")
mock_logger.info.assert_called_with("[REASON] clean-release preparation candidate=cand-1 status=PREPARED")
@patch("src.services.clean_release.audit_service.logger")
def test_audit_check_run(mock_logger):
audit_check_run("check-1", "COMPLIANT")
mock_logger.info.assert_called_with("[REFLECT] clean-release check_run=check-1 final_status=COMPLIANT")
@patch("src.services.clean_release.audit_service.logger")
def test_audit_report(mock_logger):
audit_report("rep-1", "cand-1")
mock_logger.info.assert_called_with("[EXPLORE] clean-release report_id=rep-1 candidate=cand-1")

View File

@@ -48,33 +48,6 @@ def test_orchestrator_stage_failure_blocks_release():
# [/DEF:test_orchestrator_stage_failure_blocks_release:Function]
# [DEF:test_orchestrator_compliant_candidate:Function]
# @PURPOSE: Verify happy path where all mandatory stages pass yields COMPLIANT.
def test_orchestrator_compliant_candidate():
repository = CleanReleaseRepository()
orchestrator = CleanComplianceOrchestrator(repository)
run = orchestrator.start_check_run(
candidate_id="2026.03.03-rc1",
policy_id="policy-enterprise-clean-v1",
triggered_by="tester",
execution_mode="tui",
)
run = orchestrator.execute_stages(
run,
forced_results=[
CheckStageResult(stage=CheckStageName.DATA_PURITY, status=CheckStageStatus.PASS, details="ok"),
CheckStageResult(stage=CheckStageName.INTERNAL_SOURCES_ONLY, status=CheckStageStatus.PASS, details="ok"),
CheckStageResult(stage=CheckStageName.NO_EXTERNAL_ENDPOINTS, status=CheckStageStatus.PASS, details="ok"),
CheckStageResult(stage=CheckStageName.MANIFEST_CONSISTENCY, status=CheckStageStatus.PASS, details="ok"),
],
)
run = orchestrator.finalize_run(run)
assert run.final_status == CheckFinalStatus.COMPLIANT
# [/DEF:test_orchestrator_compliant_candidate:Function]
# [DEF:test_orchestrator_missing_stage_result:Function]
# @PURPOSE: Verify incomplete mandatory stage set cannot end as COMPLIANT and results in FAILED.
def test_orchestrator_missing_stage_result():

View File

@@ -1,114 +0,0 @@
# [DEF:__tests__/test_policy_engine:Module]
# @RELATION: VERIFIES -> ../policy_engine.py
# @PURPOSE: Contract testing for CleanPolicyEngine
# [/DEF:__tests__/test_policy_engine:Module]
import pytest
from datetime import datetime
from src.models.clean_release import (
CleanProfilePolicy,
ResourceSourceRegistry,
ResourceSourceEntry,
ProfileType,
RegistryStatus
)
from src.services.clean_release.policy_engine import CleanPolicyEngine
# @TEST_FIXTURE: policy_enterprise_clean
@pytest.fixture
def enterprise_clean_setup():
policy = CleanProfilePolicy(
policy_id="POL-1",
policy_version="1",
active=True,
prohibited_artifact_categories=["demo", "test"],
required_system_categories=["core"],
internal_source_registry_ref="REG-1",
effective_from=datetime.now(),
profile=ProfileType.ENTERPRISE_CLEAN
)
registry = ResourceSourceRegistry(
registry_id="REG-1",
name="Internal Registry",
entries=[
ResourceSourceEntry(source_id="S1", host="internal.com", protocol="https", purpose="p1", enabled=True)
],
updated_at=datetime.now(),
updated_by="admin",
status=RegistryStatus.ACTIVE
)
return policy, registry
# @TEST_SCENARIO: policy_valid
def test_policy_valid(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
engine = CleanPolicyEngine(policy, registry)
result = engine.validate_policy()
assert result.ok is True
assert not result.blocking_reasons
# @TEST_EDGE: missing_registry_ref
def test_missing_registry_ref(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
policy.internal_source_registry_ref = " "
engine = CleanPolicyEngine(policy, registry)
result = engine.validate_policy()
assert result.ok is False
assert "Policy missing internal_source_registry_ref" in result.blocking_reasons
# @TEST_EDGE: conflicting_registry
def test_conflicting_registry(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
registry.registry_id = "WRONG-REG"
engine = CleanPolicyEngine(policy, registry)
result = engine.validate_policy()
assert result.ok is False
assert "Policy registry ref does not match provided registry" in result.blocking_reasons
# @TEST_INVARIANT: deterministic_classification
def test_classify_artifact(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
engine = CleanPolicyEngine(policy, registry)
# Required
assert engine.classify_artifact({"category": "core", "path": "p1"}) == "required-system"
# Prohibited
assert engine.classify_artifact({"category": "demo", "path": "p2"}) == "excluded-prohibited"
# Allowed
assert engine.classify_artifact({"category": "others", "path": "p3"}) == "allowed"
# @TEST_EDGE: external_endpoint
def test_validate_resource_source(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
engine = CleanPolicyEngine(policy, registry)
# Internal (OK)
res_ok = engine.validate_resource_source("internal.com")
assert res_ok.ok is True
# External (Blocked)
res_fail = engine.validate_resource_source("external.evil")
assert res_fail.ok is False
assert res_fail.violation["category"] == "external-source"
assert res_fail.violation["blocked_release"] is True
def test_evaluate_candidate(enterprise_clean_setup):
policy, registry = enterprise_clean_setup
engine = CleanPolicyEngine(policy, registry)
artifacts = [
{"path": "core.js", "category": "core"},
{"path": "demo.sql", "category": "demo"}
]
sources = ["internal.com", "google.com"]
classified, violations = engine.evaluate_candidate(artifacts, sources)
assert len(classified) == 2
assert classified[0]["classification"] == "required-system"
assert classified[1]["classification"] == "excluded-prohibited"
# 1 violation for demo artifact + 1 for google.com source
assert len(violations) == 2
assert violations[0]["category"] == "data-purity"
assert violations[1]["category"] == "external-source"

View File

@@ -1,127 +0,0 @@
# [DEF:backend.tests.services.clean_release.test_preparation_service:Module]
# @TIER: STANDARD
# @SEMANTICS: tests, clean-release, preparation, flow
# @PURPOSE: Validate release candidate preparation flow, including policy evaluation and manifest persisting.
# @LAYER: Domain
# @RELATION: TESTS -> backend.src.services.clean_release.preparation_service
# @INVARIANT: Candidate preparation always persists manifest and candidate status deterministically.
import pytest
from unittest.mock import MagicMock, patch
from datetime import datetime, timezone
from src.models.clean_release import (
CleanProfilePolicy,
ResourceSourceRegistry,
ResourceSourceEntry,
ReleaseCandidate,
ReleaseCandidateStatus,
ProfileType,
DistributionManifest
)
from src.services.clean_release.preparation_service import prepare_candidate
def _mock_policy() -> CleanProfilePolicy:
return CleanProfilePolicy(
policy_id="pol-1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=["prohibited"],
required_system_categories=["system"],
external_source_forbidden=True,
internal_source_registry_ref="reg-1",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
def _mock_registry() -> ResourceSourceRegistry:
return ResourceSourceRegistry(
registry_id="reg-1",
name="Reg",
entries=[ResourceSourceEntry(source_id="s1", host="nexus.internal", protocol="https", purpose="pkg", enabled=True)],
updated_at=datetime.now(timezone.utc),
updated_by="tester"
)
def _mock_candidate(candidate_id: str) -> ReleaseCandidate:
return ReleaseCandidate(
candidate_id=candidate_id,
version="1.0.0",
profile=ProfileType.ENTERPRISE_CLEAN,
created_at=datetime.now(timezone.utc),
status=ReleaseCandidateStatus.DRAFT,
created_by="tester",
source_snapshot_ref="v1.0.0-snapshot"
)
def test_prepare_candidate_success():
# Setup
repository = MagicMock()
candidate_id = "cand-1"
candidate = _mock_candidate(candidate_id)
repository.get_candidate.return_value = candidate
repository.get_active_policy.return_value = _mock_policy()
repository.get_registry.return_value = _mock_registry()
artifacts = [{"path": "file1.txt", "category": "system"}]
sources = ["nexus.internal"]
# Execute
with patch("src.services.clean_release.preparation_service.CleanPolicyEngine") as MockEngine:
mock_engine_instance = MockEngine.return_value
mock_engine_instance.validate_policy.return_value.ok = True
mock_engine_instance.evaluate_candidate.return_value = (
[{"path": "file1.txt", "category": "system", "classification": "required-system", "reason": "system-core"}],
[]
)
result = prepare_candidate(repository, candidate_id, artifacts, sources, "operator-1")
# Verify
assert result["status"] == ReleaseCandidateStatus.PREPARED.value
assert candidate.status == ReleaseCandidateStatus.PREPARED
repository.save_manifest.assert_called_once()
repository.save_candidate.assert_called_with(candidate)
def test_prepare_candidate_with_violations():
# Setup
repository = MagicMock()
candidate_id = "cand-1"
candidate = _mock_candidate(candidate_id)
repository.get_candidate.return_value = candidate
repository.get_active_policy.return_value = _mock_policy()
repository.get_registry.return_value = _mock_registry()
artifacts = [{"path": "bad.txt", "category": "prohibited"}]
sources = []
# Execute
with patch("src.services.clean_release.preparation_service.CleanPolicyEngine") as MockEngine:
mock_engine_instance = MockEngine.return_value
mock_engine_instance.validate_policy.return_value.ok = True
mock_engine_instance.evaluate_candidate.return_value = (
[{"path": "bad.txt", "category": "prohibited", "classification": "excluded-prohibited", "reason": "test-data"}],
[{"category": "data-purity", "blocked_release": True}]
)
result = prepare_candidate(repository, candidate_id, artifacts, sources, "operator-1")
# Verify
assert result["status"] == ReleaseCandidateStatus.BLOCKED.value
assert candidate.status == ReleaseCandidateStatus.BLOCKED
assert len(result["violations"]) == 1
def test_prepare_candidate_not_found():
repository = MagicMock()
repository.get_candidate.return_value = None
with pytest.raises(ValueError, match="Candidate not found"):
prepare_candidate(repository, "non-existent", [], [], "op")
def test_prepare_candidate_no_active_policy():
repository = MagicMock()
repository.get_candidate.return_value = _mock_candidate("cand-1")
repository.get_active_policy.return_value = None
with pytest.raises(ValueError, match="Active clean policy not found"):
prepare_candidate(repository, "cand-1", [], [], "op")

View File

@@ -66,26 +66,6 @@ def test_report_builder_blocked_requires_blocking_violations():
# [/DEF:test_report_builder_blocked_requires_blocking_violations:Function]
# [DEF:test_report_builder_blocked_with_two_violations:Function]
# @PURPOSE: Verify report builder generates conformant payload for a BLOCKED run with violations.
def test_report_builder_blocked_with_two_violations():
builder = ComplianceReportBuilder(CleanReleaseRepository())
run = _terminal_run(CheckFinalStatus.BLOCKED)
v1 = _blocking_violation()
v2 = _blocking_violation()
v2.violation_id = "viol-2"
v2.category = ViolationCategory.DATA_PURITY
report = builder.build_report_payload(run, [v1, v2])
assert report.check_run_id == run.check_run_id
assert report.candidate_id == run.candidate_id
assert report.final_status == CheckFinalStatus.BLOCKED
assert report.violations_count == 2
assert report.blocking_violations_count == 2
# [/DEF:test_report_builder_blocked_with_two_violations:Function]
# [DEF:test_report_builder_counter_consistency:Function]
# @PURPOSE: Verify violations counters remain consistent for blocking payload.
def test_report_builder_counter_consistency():

View File

@@ -1,27 +0,0 @@
# [DEF:backend.tests.services.clean_release.test_stages:Module]
# @TIER: STANDARD
# @SEMANTICS: tests, clean-release, compliance, stages
# @PURPOSE: Validate final status derivation logic from stage results.
# @LAYER: Domain
# @RELATION: TESTS -> backend.src.services.clean_release.stages
from src.models.clean_release import CheckFinalStatus, CheckStageName, CheckStageResult, CheckStageStatus
from src.services.clean_release.stages import derive_final_status, MANDATORY_STAGE_ORDER
def test_derive_final_status_compliant():
results = [CheckStageResult(stage=s, status=CheckStageStatus.PASS, details="ok") for s in MANDATORY_STAGE_ORDER]
assert derive_final_status(results) == CheckFinalStatus.COMPLIANT
def test_derive_final_status_blocked():
results = [CheckStageResult(stage=s, status=CheckStageStatus.PASS, details="ok") for s in MANDATORY_STAGE_ORDER]
results[1].status = CheckStageStatus.FAIL
assert derive_final_status(results) == CheckFinalStatus.BLOCKED
def test_derive_final_status_failed_missing():
results = [CheckStageResult(stage=MANDATORY_STAGE_ORDER[0], status=CheckStageStatus.PASS, details="ok")]
assert derive_final_status(results) == CheckFinalStatus.FAILED
def test_derive_final_status_failed_skipped():
results = [CheckStageResult(stage=s, status=CheckStageStatus.PASS, details="ok") for s in MANDATORY_STAGE_ORDER]
results[2].status = CheckStageStatus.SKIPPED
assert derive_final_status(results) == CheckFinalStatus.FAILED

View File

@@ -46,21 +46,10 @@ class GitService:
backend_root = Path(__file__).parents[2]
self.legacy_base_path = str((backend_root / "git_repos").resolve())
self.base_path = self._resolve_base_path(base_path)
self._ensure_base_path_exists()
if not os.path.exists(self.base_path):
os.makedirs(self.base_path)
# [/DEF:__init__:Function]
# [DEF:_ensure_base_path_exists:Function]
# @PURPOSE: Ensure the repositories root directory exists and is a directory.
# @PRE: self.base_path is resolved to filesystem path.
# @POST: self.base_path exists as directory or raises ValueError.
# @RETURN: None
def _ensure_base_path_exists(self) -> None:
base = Path(self.base_path)
if base.exists() and not base.is_dir():
raise ValueError(f"Git repositories base path is not a directory: {self.base_path}")
base.mkdir(parents=True, exist_ok=True)
# [/DEF:_ensure_base_path_exists:Function]
# [DEF:_resolve_base_path:Function]
# @PURPOSE: Resolve base repository directory from explicit argument or global storage settings.
# @PRE: base_path is a string path.
@@ -178,7 +167,6 @@ class GitService:
with belief_scope("GitService._get_repo_path"):
if dashboard_id is None:
raise ValueError("dashboard_id cannot be None")
self._ensure_base_path_exists()
fallback_key = repo_key if repo_key is not None else str(dashboard_id)
normalized_key = self._normalize_repo_key(fallback_key)
target_path = os.path.join(self.base_path, normalized_key)
@@ -226,7 +214,6 @@ class GitService:
# @RETURN: Repo - GitPython Repo object.
def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: Optional[str] = None) -> Repo:
with belief_scope("GitService.init_repo"):
self._ensure_base_path_exists()
repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id))
Path(repo_path).parent.mkdir(parents=True, exist_ok=True)

View File

@@ -36,7 +36,7 @@ class EncryptionManager:
# @PRE: ENCRYPTION_KEY env var must be set or use default dev key.
# @POST: Fernet instance ready for encryption/decryption.
def __init__(self):
self.key = os.getenv("ENCRYPTION_KEY", "REMOVED_HISTORICAL_SECRET_DO_NOT_USE").encode()
self.key = os.getenv("ENCRYPTION_KEY", "ZcytYzi0iHIl4Ttr-GdAEk117aGRogkGvN3wiTxrPpE=").encode()
self.fernet = Fernet(self.key)
# [/DEF:EncryptionManager.__init__:Function]

View File

@@ -1,47 +0,0 @@
# [DEF:__tests__/test_report_type_profiles:Module]
# @RELATION: VERIFIES -> ../type_profiles.py
# @PURPOSE: Contract testing for task type profiles and resolution logic.
# [/DEF:__tests__/test_report_type_profiles:Module]
import pytest
from src.models.report import TaskType
from src.services.reports.type_profiles import resolve_task_type, get_type_profile
# @TEST_CONTRACT: ResolveTaskType -> Invariants
# @TEST_INVARIANT: fallback_to_unknown
def test_resolve_task_type_fallbacks():
"""Verify missing/unmapped plugin_id returns TaskType.UNKNOWN."""
assert resolve_task_type(None) == TaskType.UNKNOWN
assert resolve_task_type("") == TaskType.UNKNOWN
assert resolve_task_type(" ") == TaskType.UNKNOWN
assert resolve_task_type("invalid_plugin") == TaskType.UNKNOWN
# @TEST_FIXTURE: valid_plugin
def test_resolve_task_type_valid():
"""Verify known plugin IDs map correctly."""
assert resolve_task_type("superset-migration") == TaskType.MIGRATION
assert resolve_task_type("llm_dashboard_validation") == TaskType.LLM_VERIFICATION
assert resolve_task_type("superset-backup") == TaskType.BACKUP
assert resolve_task_type("documentation") == TaskType.DOCUMENTATION
# @TEST_FIXTURE: valid_profile
def test_get_type_profile_valid():
"""Verify known task types return correct profile metadata."""
profile = get_type_profile(TaskType.MIGRATION)
assert profile["display_label"] == "Migration"
assert profile["visual_variant"] == "migration"
assert profile["fallback"] is False
# @TEST_INVARIANT: always_returns_dict
# @TEST_EDGE: missing_profile
def test_get_type_profile_fallback():
"""Verify unknown task type returns fallback profile."""
# Assuming TaskType.UNKNOWN or any non-mapped value
profile = get_type_profile(TaskType.UNKNOWN)
assert profile["display_label"] == "Other / Unknown"
assert profile["fallback"] is True
# Passing a value that might not be in the dict explicitly
profile_fallback = get_type_profile("non-enum-value")
assert profile_fallback["display_label"] == "Other / Unknown"
assert profile_fallback["fallback"] is True

BIN
backend/tasks.db Normal file

Binary file not shown.

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python3
"""Debug script to test Superset API authentication"""
from pprint import pprint
from src.core.superset_client import SupersetClient
from src.core.config_manager import ConfigManager
def main():
print("Debugging Superset API authentication...")
config = ConfigManager()
# Select first available environment
environments = config.get_environments()
if not environments:
print("No environments configured")
return
env = environments[0]
print(f"\nTesting environment: {env.name}")
print(f"URL: {env.url}")
try:
# Test API client authentication
print("\n--- Testing API Authentication ---")
client = SupersetClient(env)
tokens = client.authenticate()
print("\nAPI Auth Success!")
print(f"Access Token: {tokens.get('access_token', 'N/A')}")
print(f"CSRF Token: {tokens.get('csrf_token', 'N/A')}")
# Debug cookies from session
print("\n--- Session Cookies ---")
for cookie in client.network.session.cookies:
print(f"{cookie.name}={cookie.value}")
# Test accessing UI via requests
print("\n--- Testing UI Access ---")
ui_url = env.url.rstrip('/').replace('/api/v1', '')
print(f"UI URL: {ui_url}")
# Try to access UI home page
ui_response = client.network.session.get(ui_url, timeout=30, allow_redirects=True)
print(f"Status Code: {ui_response.status_code}")
print(f"URL: {ui_response.url}")
# Check response headers
print("\n--- Response Headers ---")
pprint(dict(ui_response.headers))
print("\n--- Response Content Preview (200 chars) ---")
print(repr(ui_response.text[:200]))
if ui_response.status_code == 200:
print("\nUI Access: Success")
# Try to access a dashboard
# For testing, just use the home page
print("\n--- Checking if login is required ---")
if "login" in ui_response.url.lower() or "login" in ui_response.text.lower():
print("❌ Not logged in to UI")
else:
print("✅ Logged in to UI")
except Exception as e:
print(f"\n❌ Error: {type(e).__name__}: {e}")
import traceback
print("\nStack Trace:")
print(traceback.format_exc())
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Test script to debug API key decryption issue."""
from src.core.database import SessionLocal
from src.models.llm import LLMProvider
from cryptography.fernet import Fernet
import os
# Get the encryption key
key = os.getenv("ENCRYPTION_KEY", "ZcytYzi0iHIl4Ttr-GdAEk117aGRogkGvN3wiTxrPpE=").encode()
print(f"Encryption key (first 20 chars): {key[:20]}")
print(f"Encryption key length: {len(key)}")
# Create Fernet instance
fernet = Fernet(key)
# Get provider from database
db = SessionLocal()
provider = db.query(LLMProvider).filter(LLMProvider.id == '6c899741-4108-4196-aea4-f38ad2f0150e').first()
if provider:
print("\nProvider found:")
print(f" ID: {provider.id}")
print(f" Name: {provider.name}")
print(f" Encrypted API Key (first 50 chars): {provider.api_key[:50]}")
print(f" Encrypted API Key Length: {len(provider.api_key)}")
# Test decryption
print("\nAttempting decryption...")
try:
decrypted = fernet.decrypt(provider.api_key.encode()).decode()
print("Decryption successful!")
print(f" Decrypted key length: {len(decrypted)}")
print(f" Decrypted key (first 8 chars): {decrypted[:8]}")
print(f" Decrypted key is empty: {len(decrypted) == 0}")
except Exception as e:
print(f"Decryption failed with error: {e}")
print(f"Error type: {type(e).__name__}")
import traceback
traceback.print_exc()
else:
print("Provider not found")
db.close()

View File

@@ -0,0 +1 @@
[{"key[": 20, ")\n\n# Create Fernet instance\nfernet = Fernet(key)\n\n# Test encrypting an empty string\nempty_encrypted = fernet.encrypt(b\"": ".", "print(f": "nEncrypted empty string: {empty_encrypted"}, {"test-api-key-12345\"\ntest_encrypted = fernet.encrypt(test_key.encode()).decode()\nprint(f": "nEncrypted test key: {test_encrypted"}, {"gAAAAABphhwSZie0OwXjJ78Fk-c4Uo6doNJXipX49AX7Bypzp4ohiRX3hXPXKb45R1vhNUOqbm6Ke3-eRwu_KdWMZ9chFBKmqw==\"\nprint(f": "nStored encrypted key: {stored_key"}, {"len(stored_key)}": "Check if stored key matches empty string encryption\nif stored_key == empty_encrypted:\n print(", "string!": "else:\n print(", "print(f": "mpty string encryption: {empty_encrypted"}, {"stored_key}": "Try to decrypt the stored key\ntry:\n decrypted = fernet.decrypt(stored_key.encode()).decode()\n print(f", "print(f": "ecrypted key length: {len(decrypted)"}, {")\nexcept Exception as e:\n print(f": "nDecryption failed with error: {e"}]

View File

@@ -19,8 +19,6 @@ from src.models.clean_release import (
ReleaseCandidateStatus,
ResourceSourceEntry,
ResourceSourceRegistry,
ComplianceReport,
CheckFinalStatus,
)
from src.services.clean_release.repository import CleanReleaseRepository
@@ -109,49 +107,5 @@ def test_get_report_not_found_returns_404():
client = TestClient(app)
resp = client.get("/api/clean-release/reports/unknown-report")
assert resp.status_code == 404
finally:
app.dependency_overrides.clear()
def test_get_report_success():
repo = _repo_with_seed_data()
report = ComplianceReport(
report_id="rep-1",
check_run_id="run-1",
candidate_id="2026.03.03-rc1",
generated_at=datetime.now(timezone.utc),
final_status=CheckFinalStatus.COMPLIANT,
operator_summary="all systems go",
structured_payload_ref="manifest-1",
violations_count=0,
blocking_violations_count=0
)
repo.save_report(report)
app.dependency_overrides[get_clean_release_repository] = lambda: repo
try:
client = TestClient(app)
resp = client.get("/api/clean-release/reports/rep-1")
assert resp.status_code == 200
assert resp.json()["report_id"] == "rep-1"
finally:
app.dependency_overrides.clear()
def test_prepare_candidate_api_success():
repo = _repo_with_seed_data()
app.dependency_overrides[get_clean_release_repository] = lambda: repo
try:
client = TestClient(app)
response = client.post(
"/api/clean-release/candidates/prepare",
json={
"candidate_id": "2026.03.03-rc1",
"artifacts": [{"path": "file1.txt", "category": "system-init", "reason": "core"}],
"sources": ["repo.intra.company.local"],
"operator_id": "operator-1",
},
)
assert response.status_code == 200
data = response.json()
assert data["status"] == "prepared"
assert "manifest_id" in data
finally:
app.dependency_overrides.clear()

View File

@@ -1,6 +1,5 @@
import sys
from pathlib import Path
import shutil
import pytest
from unittest.mock import MagicMock
@@ -16,17 +15,6 @@ def test_git_service_get_repo_path_guard():
with pytest.raises(ValueError, match="dashboard_id cannot be None"):
service._get_repo_path(None)
def test_git_service_get_repo_path_recreates_base_dir():
"""Verify _get_repo_path recreates missing base directory before returning repo path."""
service = GitService(base_path="test_repos_runtime_recreate")
shutil.rmtree(service.base_path, ignore_errors=True)
repo_path = service._get_repo_path(42)
assert Path(service.base_path).is_dir()
assert repo_path == str(Path(service.base_path) / "42")
def test_superset_client_import_dashboard_guard():
"""Verify that import_dashboard raises ValueError if file_name is None."""
mock_env = Environment(

View File

@@ -0,0 +1,144 @@
# [DEF:backend.tests.services.clean_release.test_policy_engine:Module]
# @TIER: CRITICAL
# @SEMANTICS: tests, clean-release, policy-engine, deterministic
# @PURPOSE: Validate policy model contracts and deterministic classification prerequisites for US1.
# @LAYER: Domain
# @RELATION: VERIFIES -> backend.src.models.clean_release.CleanProfilePolicy
# @INVARIANT: Enterprise policy rejects invalid activation states.
import pytest
from datetime import datetime, timezone
from src.models.clean_release import CleanProfilePolicy, ProfileType
# [DEF:test_policy_enterprise_clean_valid:Function]
# @PURPOSE: Ensure valid enterprise policy payload is accepted.
# @PRE: Fixture-like payload contains prohibited categories and registry ref.
# @POST: Model is created with external_source_forbidden=True.
def test_policy_enterprise_clean_valid():
policy = CleanProfilePolicy(
policy_id="policy-enterprise-clean-v1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=["test-data", "demo-data"],
required_system_categories=["system-init"],
external_source_forbidden=True,
internal_source_registry_ref="registry-internal-v1",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
assert policy.external_source_forbidden is True
assert policy.prohibited_artifact_categories == ["test-data", "demo-data"]
# [/DEF:test_policy_enterprise_clean_valid:Function]
# [DEF:test_policy_missing_registry_fails:Function]
# @PURPOSE: Verify missing registry ref violates policy contract.
# @PRE: enterprise-clean policy payload has blank registry ref.
# @POST: Validation error is raised.
def test_policy_missing_registry_fails():
with pytest.raises(ValueError):
CleanProfilePolicy(
policy_id="policy-enterprise-clean-v1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=["test-data"],
required_system_categories=["system-init"],
external_source_forbidden=True,
internal_source_registry_ref="",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
# [/DEF:test_policy_missing_registry_fails:Function]
# [DEF:test_policy_empty_prohibited_categories_fails:Function]
# @PURPOSE: Verify enterprise policy cannot activate without prohibited categories.
# @PRE: enterprise-clean policy payload has empty prohibited categories.
# @POST: Validation error is raised.
def test_policy_empty_prohibited_categories_fails():
with pytest.raises(ValueError):
CleanProfilePolicy(
policy_id="policy-enterprise-clean-v1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=[],
required_system_categories=["system-init"],
external_source_forbidden=True,
internal_source_registry_ref="registry-internal-v1",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
# [/DEF:test_policy_empty_prohibited_categories_fails:Function]
# [DEF:test_policy_conflicting_external_forbidden_flag_fails:Function]
# @PURPOSE: Verify enterprise policy enforces external_source_forbidden=true.
# @PRE: enterprise-clean policy payload sets external_source_forbidden to false.
# @POST: Validation error is raised.
def test_policy_conflicting_external_forbidden_flag_fails():
with pytest.raises(ValueError):
CleanProfilePolicy(
policy_id="policy-enterprise-clean-v1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=["test-data"],
required_system_categories=["system-init"],
external_source_forbidden=False,
internal_source_registry_ref="registry-internal-v1",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
# [/DEF:test_policy_conflicting_external_forbidden_flag_fails:Function]
# [/DEF:backend.tests.services.clean_release.test_policy_engine:Module]
from src.models.clean_release import ResourceSourceRegistry, ResourceSourceEntry, RegistryStatus
from src.services.clean_release.policy_engine import CleanPolicyEngine
def _policy_enterprise_clean() -> CleanProfilePolicy:
return CleanProfilePolicy(
policy_id="policy-enterprise-clean-v1",
policy_version="1.0.0",
active=True,
prohibited_artifact_categories=["test-data"],
required_system_categories=["system-init"],
external_source_forbidden=True,
internal_source_registry_ref="registry-internal-v1",
effective_from=datetime.now(timezone.utc),
profile=ProfileType.ENTERPRISE_CLEAN,
)
def _registry() -> ResourceSourceRegistry:
return ResourceSourceRegistry(
registry_id="registry-internal-v1",
name="Internal",
entries=[ResourceSourceEntry(source_id="1", host="nexus.internal", protocol="https", purpose="pkg", enabled=True)],
updated_at=datetime.now(timezone.utc),
updated_by="tester",
)
# [DEF:test_policy_valid:Function]
# @PURPOSE: Validate policy valid scenario
def test_policy_valid():
engine = CleanPolicyEngine(_policy_enterprise_clean(), _registry())
res = engine.validate_policy()
assert res.ok is True
# [DEF:test_conflicting_registry:Function]
# @PURPOSE: Validate policy conflicting registry edge
def test_conflicting_registry():
reg = _registry()
reg.registry_id = "other-registry"
engine = CleanPolicyEngine(_policy_enterprise_clean(), reg)
res = engine.validate_policy()
assert res.ok is False
assert "Policy registry ref does not match provided registry" in res.blocking_reasons
# [DEF:test_external_endpoint:Function]
# @PURPOSE: Validate policy external endpoint edge
def test_external_endpoint():
engine = CleanPolicyEngine(_policy_enterprise_clean(), _registry())
res = engine.validate_resource_source("external.org")
assert res.ok is False
assert res.violation["category"] == "external-source"

27
check_test_data.py Normal file
View File

@@ -0,0 +1,27 @@
import os
def check_file(filepath):
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
if '@TIER: CRITICAL' in content:
if '@TEST_DATA' not in content:
return filepath
except Exception as e:
print(f"Error reading {filepath}: {e}")
return None
missing_files = []
for root_dir in ['backend/src', 'frontend/src']:
for dirpath, _, filenames in os.walk(root_dir):
for name in filenames:
ext = os.path.splitext(name)[1]
if ext in ['.py', '.js', '.ts', '.svelte']:
full_path = os.path.join(dirpath, name)
res = check_file(full_path)
if res:
missing_files.append(res)
print("Files missing @TEST_DATA:")
for f in missing_files:
print(f)

View File

@@ -1,17 +1,10 @@
// [DEF:frontend.src.components.__tests__.task_log_viewer:Module]
// @TIER: STANDARD
// @TIER: CRITICAL
// @SEMANTICS: tests, task-log, viewer, mount, components
// @PURPOSE: Unit tests for TaskLogViewer component by mounting it and observing the DOM.
// @LAYER: UI (Tests)
// @RELATION: VERIFIES -> frontend/src/components/TaskLogViewer.svelte
// @INVARIANT: Duplicate logs are never appended. Polling only active for in-progress tasks.
// @TEST_CONTRACT: TaskLogViewerPropsAndLogStream -> RenderedLogTimeline
// @TEST_SCENARIO: historical_and_realtime_merge -> Historical logs render and realtime logs append without duplication.
// @TEST_FIXTURE: valid_viewer -> INLINE_JSON
// @TEST_EDGE: no_task_id -> Null taskId does not trigger fetch.
// @TEST_EDGE: fetch_failure -> Network failure renders recoverable error state with retry action.
// @TEST_EDGE: duplicate_realtime_entry -> Existing log is not duplicated when repeated in realtime stream.
// @TEST_INVARIANT: no_duplicate_log_rows -> VERIFIED_BY: [historical_and_realtime_merge, duplicate_realtime_entry]
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/svelte';
@@ -22,8 +15,6 @@ vi.mock('../../services/taskService.js', () => ({
getTaskLogs: vi.fn()
}));
const getTaskLogsMock = vi.mocked(getTaskLogs);
vi.mock('../../lib/i18n', () => ({
t: {
subscribe: (fn) => {
@@ -48,13 +39,13 @@ describe('TaskLogViewer Component', () => {
});
it('renders loading state initially', () => {
getTaskLogsMock.mockResolvedValue([]);
getTaskLogs.mockResolvedValue([]);
render(TaskLogViewer, { inline: true, taskId: 'task-123' });
expect(screen.getByText('Loading...')).toBeDefined();
});
it('fetches and displays historical logs', async () => {
getTaskLogsMock.mockResolvedValue([
getTaskLogs.mockResolvedValue([
{ timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Historical log entry' }
]);
@@ -68,7 +59,7 @@ describe('TaskLogViewer Component', () => {
});
it('displays error message on fetch failure', async () => {
getTaskLogsMock.mockRejectedValue(new Error('Network error fetching logs'));
getTaskLogs.mockRejectedValue(new Error('Network error fetching logs'));
render(TaskLogViewer, { inline: true, taskId: 'task-123' });
@@ -79,7 +70,7 @@ describe('TaskLogViewer Component', () => {
});
it('appends real-time logs passed as props', async () => {
getTaskLogsMock.mockResolvedValue([
getTaskLogs.mockResolvedValue([
{ timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Historical log entry' }
]);
@@ -108,7 +99,7 @@ describe('TaskLogViewer Component', () => {
});
it('deduplicates real-time logs that are already in historical logs', async () => {
getTaskLogsMock.mockResolvedValue([
getTaskLogs.mockResolvedValue([
{ timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Duplicate log entry' }
]);
@@ -141,7 +132,7 @@ describe('TaskLogViewer Component', () => {
// @TEST_FIXTURE valid_viewer
it('fetches and displays historical logs in modal mode under valid_viewer fixture', async () => {
getTaskLogsMock.mockResolvedValue([
getTaskLogs.mockResolvedValue([
{ timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Modal log entry' }
]);

View File

@@ -5,13 +5,6 @@
// @LAYER: UI Tests
// @RELATION: VERIFIES -> frontend/src/lib/components/assistant/AssistantChatPanel.svelte
// @INVARIANT: Critical assistant UX states and action hooks remain present in component source.
// @TEST_CONTRACT: AssistantChatSourceArtifacts -> ContractAssertions
// @TEST_SCENARIO: assistant_contract_and_i18n_intact -> Component semantic/UX anchors and locale keys stay consistent.
// @TEST_FIXTURE: assistant_locales_en_ru -> file:src/lib/i18n/locales/en.json + file:src/lib/i18n/locales/ru.json
// @TEST_EDGE: missing_component_anchor -> Missing DEF/UX tags fails contract assertion.
// @TEST_EDGE: missing_action_hook -> Missing confirm/cancel/open_task hooks fails integration assertion.
// @TEST_EDGE: missing_locale_key -> Missing assistant locale key in en/ru fails dictionary assertion.
// @TEST_INVARIANT: assistant_ux_contract_visible -> VERIFIED_BY: [assistant_contract_and_i18n_intact]
import { describe, it, expect } from 'vitest';
import fs from 'node:fs';
@@ -48,7 +41,7 @@ describe('AssistantChatPanel integration contract', () => {
const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');
expect(source).toContain('<!-- [DEF' + ':AssistantChatPanel:Component] -->');
expect(source).toContain('@TIER' + ': CRITICAL');
expect(source).toContain('@TIER: CRITICAL');
expect(source).toContain('@UX_STATE: LoadingHistory');
expect(source).toContain('@UX_STATE: Sending');
expect(source).toContain('@UX_STATE: Error');

View File

@@ -27,12 +27,14 @@
* @TEST_INVARIANT correct_iteration -> verifies: [renders_list, empty_list]
*/
import { createEventDispatcher } from "svelte";
import ReportCard from "./ReportCard.svelte";
let { reports = [], selectedReportId = null, onselect } = $props();
let { reports = [], selectedReportId = null } = $props();
const dispatch = createEventDispatcher();
function handleSelect(event) {
if (onselect) onselect({ report: event.detail.report });
dispatch("select", { report: event.detail.report });
}
</script>

View File

@@ -2,19 +2,12 @@
* @vitest-environment jsdom
*/
// [DEF:frontend.src.lib.components.reports.__tests__.report_card.ux:Module]
// @TIER: STANDARD
// @TIER: CRITICAL
// @SEMANTICS: reports, ux-tests, card, states, recovery
// @PURPOSE: Test UX states and transitions for ReportCard component
// @LAYER: UI
// @RELATION: VERIFIES -> ../ReportCard.svelte
// @INVARIANT: Each test asserts at least one observable UX contract outcome.
// @TEST_CONTRACT: ReportCardInputProps -> ObservableUXOutput
// @TEST_SCENARIO: ready_state_shows_summary_status_type -> Ready state renders summary/status/type labels.
// @TEST_FIXTURE: valid_report_card -> INLINE_JSON
// @TEST_EDGE: empty_report_object -> Missing fields use placeholders and fallback labels.
// @TEST_EDGE: random_status -> Unknown status is rendered without crashing.
// @TEST_EDGE: missing_optional_fields -> Partial report keeps component interactive and emits select.
// @TEST_INVARIANT: report_card_state_is_observable -> VERIFIED_BY: [ready_state_shows_summary_status_type, empty_report_object, random_status]
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
@@ -46,7 +39,7 @@ describe('ReportCard UX Contract', () => {
// @UX_STATE: Ready -> Card displays summary/status/type.
it('should display summary, status and type in Ready state', () => {
render(ReportCard, { report: mockReport, onselect: vi.fn() });
render(ReportCard, { report: mockReport });
expect(screen.getByText(mockReport.summary)).toBeDefined();
// mockReport.status is "success", getStatusLabel(status) returns $t.reports?.status_success
expect(screen.getByText('Success')).toBeDefined();
@@ -68,7 +61,7 @@ describe('ReportCard UX Contract', () => {
// @UX_RECOVERY: Missing fields are rendered with explicit placeholder text.
it('should render placeholders for missing fields', () => {
const partialReport = { report_id: 'partial-1' };
render(ReportCard, { report: partialReport, onselect: vi.fn() });
render(ReportCard, { report: partialReport });
// Check placeholders (using text from mocked $t)
const placeholders = screen.getAllByText('Not provided');
@@ -86,7 +79,7 @@ describe('ReportCard UX Contract', () => {
summary: "Test Summary",
updated_at: "2024-01-01"
};
render(ReportCard, { report: validReportCard, onselect: vi.fn() });
render(ReportCard, { report: validReportCard });
expect(screen.getByText('Test Summary')).toBeDefined();
expect(screen.getByText('Success')).toBeDefined();
@@ -94,14 +87,14 @@ describe('ReportCard UX Contract', () => {
// @TEST_EDGE empty_report_object
it('should handle completely empty report object gracefully', () => {
render(ReportCard, { report: {}, onselect: vi.fn() });
render(ReportCard, { report: {} });
const placeholders = screen.getAllByText('Not provided');
expect(placeholders.length).toBeGreaterThan(0);
});
// @TEST_EDGE random_status
it('should render random status directly if no translation matches', () => {
render(ReportCard, { report: { status: "unknown_status_code" }, onselect: vi.fn() });
render(ReportCard, { report: { status: "unknown_status_code" } });
expect(screen.getByText('unknown_status_code')).toBeDefined();
});
});

View File

@@ -1,74 +0,0 @@
/**
* @vitest-environment jsdom
*/
// [DEF:frontend.src.lib.components.reports.__tests__.reports_list.ux:Module]
// @TIER: STANDARD
// @SEMANTICS: reports, list, ux-tests, events, iteration
// @PURPOSE: Test ReportsList component iteration and event forwarding.
// @LAYER: UI
// @RELATION: VERIFIES -> ../ReportsList.svelte
// [/DEF:frontend.src.lib.components.reports.__tests__.reports_list.ux:Module]
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import ReportsList from '../ReportsList.svelte';
// Mock i18n since ReportsList -> ReportCard -> i18n
vi.mock('$lib/i18n', () => ({
t: {
subscribe: (fn) => {
fn({
reports: {
not_provided: 'N/A',
status_success: 'OK',
status_failed: 'ERR'
}
});
return () => { };
}
},
_: vi.fn((key) => key)
}));
describe('ReportsList UX Contract', () => {
const mockReports = [
{ report_id: '1', summary: 'Report One', task_type: 'migration', status: 'success' },
{ report_id: '2', summary: 'Report Two', task_type: 'backup', status: 'failed' }
];
// @TEST_FIXTURE renders_list
it('should render multiple report cards and mark the selected one', () => {
const { container } = render(ReportsList, { reports: mockReports, selectedReportId: '2' });
expect(screen.getByText('Report One')).toBeDefined();
expect(screen.getByText('Report Two')).toBeDefined();
// Check selection logic - we look for a marker or class change in the child cards
// In our simplified test, we check if screen find two buttons
const buttons = screen.getAllByRole('button');
expect(buttons.length).toBe(2);
});
// @TEST_EDGE empty_list
// @TEST_INVARIANT correct_iteration
it('should render empty container for empty list', () => {
const { container } = render(ReportsList, { reports: [] });
// Root div should have space-y-2 class but be empty
const div = container.querySelector('.space-y-2');
expect(div).toBeDefined();
expect(div.children.length).toBe(0);
});
// @UX_FEEDBACK: Click on report emits select event.
// @TEST_CONTRACT Component_ReportsList -> Forwards select events from children
it('should forward select event when a report card is clicked', async () => {
const onSelect = vi.fn();
const { component } = render(ReportsList, { reports: [mockReports[0]], onselect: onSelect });
const button = screen.getByRole('button');
await fireEvent.click(button);
expect(onSelect).toHaveBeenCalled();
expect(onSelect.mock.calls[0][0].report.report_id).toBe('1');
});
});

View File

@@ -189,36 +189,9 @@ class SemanticEntity:
with belief_scope("get_tier"):
tier_str = self.tags.get("TIER", "STANDARD").upper()
try:
base_tier = Tier(tier_str)
return Tier(tier_str)
except ValueError:
base_tier = Tier.STANDARD
# Dynamic Tier Adjustments based on User Feedback
# 1. Tests should never be higher than STANDARD
if "test" in self.file_path.lower() or "/__tests__/" in self.file_path or self.name.startswith("test_"):
if base_tier == Tier.CRITICAL:
return Tier.STANDARD
# 2. Svelte components -> TRIVIAL/STANDARD (unless layout/page)
if self.file_path.endswith(".svelte"):
if "+page" not in self.name and "+layout" not in self.name and "Page" not in self.name and "Layout" not in self.name:
if base_tier == Tier.CRITICAL:
return Tier.STANDARD
# 3. Tooling scripts
if "scripts/" in self.file_path or "_tui.py" in self.file_path:
if base_tier == Tier.CRITICAL:
return Tier.STANDARD
# 4. Promote critical security/data paths
critical_keywords = ["auth", "security", "jwt", "database", "migration", "config", "session"]
if any(keyword in self.file_path.lower() for keyword in critical_keywords) and "test" not in self.file_path.lower():
# Allow explicit overrides to lower tiers if explicitly tagged TRIVIAL, otherwise promote logic mapping
if base_tier != Tier.TRIVIAL:
return Tier.CRITICAL
return base_tier
return Tier.STANDARD
# [/DEF:get_tier:Function]
# [DEF:to_dict:Function]

File diff suppressed because it is too large Load Diff

View File

@@ -137,7 +137,6 @@
- [X] T041 Add release checklist artifact template for compliance evidence packaging in `specs/023-clean-repo-enterprise/checklists/release-readiness.md`
- [X] T042 Resolve numeric-prefix governance conflict note (`020-*`) and document decision in `specs/023-clean-repo-enterprise/plan.md`
- [X] T043 Update feature status traceability and final notes in `specs/023-clean-repo-enterprise/plan.md`
- [X] T044 Remediate CRITICAL semantic test-contract gaps by adding `@TEST_CONTRACT` metadata in backend/frontend flagged modules and recording coverage update in `specs/023-clean-repo-enterprise/tests/coverage.md`
---

View File

@@ -7,15 +7,7 @@
| `clean_release.report_builder` | `report_builder.py` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `clean_release.manifest_builder` | `manifest_builder.py` | STANDARD | ✅ Yes | N/A | N/A | N/A |
| `clean_release.source_isolation` | `source_isolation.py` | STANDARD | ✅ Yes | N/A | N/A | N/A |
| `clean_release.preparation_service` | `preparation_service.py` | STANDARD | ✅ Yes | 1/1 | 2/2 | 1/1 |
| `clean_release.audit_service` | `audit_service.py` | STANDARD | ✅ Yes | N/A | N/A | 1/1 |
| `clean_release.stages` | `stages.py` | STANDARD | ✅ Yes | N/A | 3/3 | N/A |
| `api.routes.clean_release` | `clean_release.py` | STANDARD | ✅ Yes | 1/1 | 2/2 | 1/1 |
| `api.routes.tasks.get_task_logs` | `tasks.py` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `models.clean_release` | `clean_release.py` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `frontend.assistant_chat.integration` | `assistant_chat.integration.test.js` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `frontend.reports.report_card.ux` | `report_card.ux.test.js` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `frontend.task_log_viewer` | `task_log_viewer.test.js` | CRITICAL | ✅ Yes | 1/1 | 3/3 | 1/1 |
| `api.routes.clean_release` | `clean_release.py` | STANDARD | ✅ Yes | N/A | N/A | N/A |
## CRITICAL Edge Cases Covered

View File

@@ -1,48 +0,0 @@
# Test Report: Global CRITICAL Coverage
Date: 2026-03-04
Executor: GRACE Tester
## Coverage Matrix
| Module | TIER | Tests | Edge Covered | Invariants Covered |
|--------|------|------|----------|------------|
| backend/src/api/routes/tasks.py | CRITICAL | - | - | - |
| backend/src/models/clean_release.py | CRITICAL | - | - | - |
| frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js | CRITICAL | - | - | - |
| frontend/src/lib/components/reports/__tests__/report_card.ux.test.js | CRITICAL | - | - | - |
| frontend/src/components/__tests__/task_log_viewer.test.js | CRITICAL | - | - | - |
*(Note: Matrix focuses only on modules that triggered the fail policy)*
## Contract Validation
- TEST_CONTRACT validated ❌
- All FIXTURES tested ❌
- All EDGES tested ❌
- All INVARIANTS verified ❌
## Results
Total: 0
Passed: 0
Failed: 5
Skipped: 38
## Violations
| Module | Problem | Severity |
|--------|---------|----------|
| `backend/src/api/routes/tasks.py` | [COHERENCE_CHECK_FAILED] Missing TEST_CONTRACT | CRITICAL |
| `backend/src/models/clean_release.py` | [COHERENCE_CHECK_FAILED] Missing TEST_CONTRACT | CRITICAL |
| `frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js` | [COHERENCE_CHECK_FAILED] Missing TEST_CONTRACT | CRITICAL |
| `frontend/src/lib/components/reports/__tests__/report_card.ux.test.js` | [COHERENCE_CHECK_FAILED] Missing TEST_CONTRACT | CRITICAL |
| `frontend/src/components/__tests__/task_log_viewer.test.js` | [COHERENCE_CHECK_FAILED] Missing TEST_CONTRACT | CRITICAL |
## Next Actions
- [ ] Add `@TEST_CONTRACT` to `backend/src/api/routes/tasks.py` (for `get_task_logs` method)
- [ ] Add `@TEST_CONTRACT` to `backend/src/models/clean_release.py`
- [ ] Add `@TEST_CONTRACT` to `frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js` or adjust TIER
- [ ] Add `@TEST_CONTRACT` to `frontend/src/lib/components/reports/__tests__/report_card.ux.test.js` or adjust TIER
- [ ] Add `@TEST_CONTRACT` to `frontend/src/components/__tests__/task_log_viewer.test.js` or adjust TIER