Coverage for mcpgateway / toolops / utils / db_util.py: 100%
34 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 00:56 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-06 00:56 +0100
1# -*- coding: utf-8 -*-
2"""Location: ./mcpgateway/toolops/utils/db_util.py
3Copyright 2025
4SPDX-License-Identifier: Apache-2.0
5Authors: Jay Bandlamudi
7ContextForge - Main module for handling toolops related database operations.
9This module defines the utility funtions to read/write/update toolops related database tables.
10"""
12# Third-Party
13from sqlalchemy.orm import Session
15# First-Party
16from mcpgateway.db import Tool
17from mcpgateway.db import ToolOpsTestCases as TestCaseRecord
18from mcpgateway.services.logging_service import LoggingService
19from mcpgateway.utils.services_auth import decode_auth
21logging_service = LoggingService()
22logger = logging_service.get_logger(__name__)
25def populate_testcases_table(tool_id, test_cases, run_status, db: Session):
26 """
27 Method to write and update toolops test cases to database table
29 Args:
30 tool_id: unqiue Tool ID used in MCP-CF
31 test_cases: list of generated test cases, each test case is a dictionary object
32 run_status: status of test case generation request such as in-progess, complete , failed
33 db: DB session to access the database
35 Examples:
36 >>> from unittest.mock import MagicMock, patch
37 >>> # Setup: Get the current module path dynamically to ensure patch works regardless of file location
38 >>> mod_path = populate_testcases_table.__module__
40 >>> # Case 1: Insert New Record (Tool ID not found in DB)
41 >>> mock_db = MagicMock()
42 >>> # Simulate query returning None (record does not exist)
43 >>> mock_db.query.return_value.filter_by.return_value.first.return_value = None
45 >>> # Patch the TestCaseRecord class specifically in THIS module
46 >>> with patch(f"{mod_path}.TestCaseRecord") as MockRecord:
47 ... populate_testcases_table("tool-123", [{"test": "case"}], "in-progress", mock_db)
48 ...
49 ... # Verify the class was instantiated
50 ... MockRecord.assert_called_with(tool_id="tool-123", test_cases=[{"test": "case"}], run_status="in-progress")
51 ... # Verify DB interactions
52 ... mock_db.add.assert_called()
53 ... mock_db.commit.assert_called()
55 >>> # Case 2: Update Existing Record
56 >>> mock_db_update = MagicMock()
57 >>> existing_record = MagicMock()
58 >>> mock_db_update.query.return_value.filter_by.return_value.first.return_value = existing_record
60 >>> # We still patch TestCaseRecord to ensure no side effects, though it's not instantiated here
61 >>> with patch(f"{mod_path}.TestCaseRecord"):
62 ... populate_testcases_table("tool-123", [{"test": "new"}], "completed", mock_db_update)
63 ...
64 ... # Verify fields were updated on existing record
65 ... assert existing_record.test_cases == [{"test": "new"}]
66 ... assert existing_record.run_status == "completed"
67 ... # Verify add was NOT called
68 ... mock_db_update.add.assert_not_called()
69 """
70 tool_record = db.query(TestCaseRecord).filter_by(tool_id=tool_id).first()
71 if not tool_record:
72 test_case_record = TestCaseRecord(tool_id=tool_id, test_cases=test_cases, run_status=run_status)
73 # Add to DB
74 db.add(test_case_record)
75 db.commit()
76 db.refresh(test_case_record)
77 logger.info("Added tool test case record with empty test cases for tool " + str(tool_id) + " with status " + str(run_status))
78 # elif tool_record and test_cases != [] and run_status == 'completed':
79 elif tool_record:
80 tool_record.test_cases = test_cases
81 tool_record.run_status = run_status
82 db.commit()
83 db.refresh(tool_record)
84 logger.info("Updated tool record in table with test cases for tool " + str(tool_id) + " with status " + str(run_status))
87def query_testcases_table(tool_id, db: Session):
88 """
89 Method to read toolops test cases from database table
91 Args:
92 tool_id: unqiue Tool ID used in MCP-CF
93 db: DB session to access the database
95 Returns:
96 This method returns tool record for specified tool id and tool record contains 'tool_id','test_cases','run_status'.
98 Examples:
99 >>> from unittest.mock import MagicMock, patch
100 >>> mock_db = MagicMock()
102 >>> # Create a dummy record to return
103 >>> mock_record = MagicMock()
104 >>> mock_record.tool_id = "tool-abc"
105 >>> mock_record.test_cases = [{"input": "test"}]
107 >>> # Mock the chain: db.query(...).filter_by(...).first()
108 >>> mock_db.query.return_value.filter_by.return_value.first.return_value = mock_record
110 >>> # Execute
111 >>> result = query_testcases_table("tool-abc", mock_db)
113 >>> # Verify result and calls
114 >>> result.tool_id
115 'tool-abc'
116 >>> mock_db.query.assert_called()
117 """
118 tool_record = db.query(TestCaseRecord).filter_by(tool_id=tool_id).first()
119 logger.info("Tool record obtained from table for tool - " + str(tool_id))
120 return tool_record
123def query_tool_auth(tool_id, db: Session):
124 """
125 Method to read tools table from database and get tool auth
127 Args:
128 tool_id: unique Tool ID used in MCP-CF
129 db: DB session to access the database
131 Returns:
132 This method returns tool auth specified tool id.
134 Examples:
135 >>> from unittest.mock import MagicMock, patch
136 >>> mod_path = query_tool_auth.__module__
138 >>> # Case 1: Successful Auth Retrieval
139 >>> mock_db = MagicMock()
140 >>> mock_tool_record = MagicMock()
141 >>> mock_tool_record.auth_value = "encoded-val"
142 >>> mock_db.query.return_value.filter_by.return_value.first.return_value = mock_tool_record
144 >>> # We nest the patches to avoid SyntaxError in doctest multiline 'with' statements
145 >>> with patch(f"{mod_path}.decode_auth", side_effect=lambda x: f"decoded-{x}"):
146 ... with patch(f"{mod_path}.Tool"):
147 ... auth = query_tool_auth("tool-1", mock_db)
148 ... print(auth)
149 decoded-encoded-val
151 >>> # Case 2: Exception Handling
152 >>> import logging
153 >>> logging.disable(logging.CRITICAL)
154 >>> mock_db_fail = MagicMock()
155 >>> mock_db_fail.query.side_effect = Exception("DB Connection Error")
157 >>> with patch(f"{mod_path}.Tool"):
158 ... auth = query_tool_auth("tool-2", mock_db_fail)
159 ... print(auth)
160 None
161 >>> logging.disable(logging.NOTSET)
162 """
163 tool_auth = None
164 try:
165 tool_record = db.query(Tool).filter_by(id=tool_id).first()
166 tool_auth = decode_auth(tool_record.auth_value)
167 logger.info("Tool auth obtained from table for the tool - " + str(tool_id))
168 except Exception as e:
169 logger.error("Error in obtaining authorization for the tool - " + tool_id + " , " + str(e))
170 return tool_auth
173# if __name__=='__main__':
174# # First-Party
175# from mcpgateway.db import SessionLocal
176# from mcpgateway.services.tool_service import ToolService
178# tool_id = '36451eb11de64ebf8f224fc41a846ff0'
179# tool_service = ToolService()
180# db = SessionLocal()
182# tool_auth = query_tool_auth(tool_id, db)
183# print(tool_auth)