Skip to main content
Glama

mcp-dbutils

MIT License
81
  • Linux
  • Apple
testing.md19.7 kB
# دليل الاختبار *[English](../../en/technical/testing.md) | [中文](../../zh/technical/testing.md) | [Français](../../fr/technical/testing.md) | [Español](../../es/technical/testing.md) | العربية | [Русский](../../ru/technical/testing.md)* يصف هذا المستند استراتيجية الاختبار، والأدوات، وأفضل الممارسات لاختبار MCP Database Utilities. ## فلسفة الاختبار يعتمد نهجنا في الاختبار على المبادئ التالية: 1. **اختبارات شاملة**: تغطية جميع جوانب الكود، من الدوال الفردية إلى النظام الكامل 2. **اختبارات آلية**: يجب أن تكون جميع الاختبارات آلية وقابلة للتنفيذ عبر CI/CD 3. **اختبارات سريعة**: يجب أن تنفذ الاختبارات بسرعة للسماح بتغذية راجعة سريعة 4. **اختبارات معزولة**: يجب أن يكون كل اختبار مستقلاً ولا يعتمد على اختبارات أخرى 5. **اختبارات حتمية**: يجب أن تنتج الاختبارات نفس النتائج في كل تنفيذ ## أنواع الاختبارات ### اختبارات الوحدة تتحقق اختبارات الوحدة من سلوك المكونات الفردية (الدوال، الفئات، الطرق) في عزلة. **الخصائص**: - سريعة التنفيذ - تختبر وحدة كود واحدة - تستخدم المحاكاة لعزل الكود المختبر - لا تعتمد على موارد خارجية (قواعد بيانات، شبكة، إلخ) **مثال**: ```python def test_query_validator(): """اختبار أن مدقق الاستعلام يحدد بشكل صحيح الاستعلامات غير المصرح بها.""" validator = QueryValidator() # استعلام SELECT صالح assert validator.validate("SELECT * FROM users") is True # استعلامات غير مصرح بها assert validator.validate("INSERT INTO users VALUES (1, 'test')") is False assert validator.validate("UPDATE users SET name = 'test' WHERE id = 1") is False assert validator.validate("DELETE FROM users WHERE id = 1") is False assert validator.validate("DROP TABLE users") is False ``` ### اختبارات التكامل تتحقق اختبارات التكامل من أن المكونات المختلفة تعمل بشكل صحيح معًا. **الخصائص**: - أبطأ من اختبارات الوحدة - تختبر التفاعل بين عدة مكونات - يمكن أن تستخدم موارد خارجية حقيقية أو محاكاة - تتحقق من أن الواجهات بين المكونات تعمل بشكل صحيح **مثال**: ```python def test_sqlite_adapter_integration(): """اختبار التكامل بين محول SQLite ومدير الاستعلامات.""" # إنشاء قاعدة بيانات SQLite في الذاكرة config = {"type": "sqlite", "path": ":memory:"} adapter = SQLiteAdapter(config) # إنشاء جدول اختبار connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)") cursor.execute("INSERT INTO test VALUES (1, 'test1'), (2, 'test2')") connection.commit() # إنشاء مدير استعلامات مع المحول query_manager = QueryManager(adapter) # تنفيذ استعلام عبر المدير results = query_manager.execute("SELECT * FROM test WHERE id = 1") # التحقق من النتائج assert len(results) == 1 assert results[0]["id"] == 1 assert results[0]["name"] == "test1" ``` ### اختبارات من طرف إلى طرف تتحقق اختبارات من طرف إلى طرف من أن النظام الكامل يعمل بشكل صحيح من البداية إلى النهاية. **الخصائص**: - الأبطأ في التنفيذ - تختبر النظام الكامل في بيئة قريبة من الإنتاج - تستخدم موارد خارجية حقيقية - تتحقق من سيناريوهات الاستخدام الحقيقية **مثال**: ```python def test_mcp_server_end_to_end(): """اختبار خادم MCP من طرف إلى طرف مع قاعدة بيانات حقيقية.""" # بدء خادم MCP مع تكوين اختبار config_path = "tests/fixtures/test_config.yaml" server = MCPServer(config_path) server.start() try: # إنشاء عميل MCP محاكي client = MockMCPClient() # تنفيذ استعلام عبر بروتوكول MCP response = client.execute_tool("dbutils-list-tables", {"connection": "test-sqlite"}) # التحقق من الاستجابة assert "tables" in response assert "test" in response["tables"] # تنفيذ استعلام SQL response = client.execute_tool("dbutils-run-query", { "connection": "test-sqlite", "query": "SELECT * FROM test WHERE id = 1" }) # التحقق من النتائج assert len(response["results"]) == 1 assert response["results"][0]["id"] == 1 assert response["results"][0]["name"] == "test1" finally: # إيقاف الخادم server.stop() ``` ### اختبارات الأداء تتحقق اختبارات الأداء من أن النظام يلبي متطلبات الأداء. **الخصائص**: - تقيس وقت التنفيذ، واستخدام الذاكرة، إلخ - تتحقق من أن النظام يمكنه التعامل مع الحمل المتوقع - تحدد نقاط الاختناق **مثال**: ```python def test_query_performance(): """اختبار أداء الاستعلامات.""" # تكوين المحول مع قاعدة بيانات اختبار config = {"type": "sqlite", "path": ":memory:"} adapter = SQLiteAdapter(config) # إنشاء جدول اختبار مع العديد من الصفوف connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)") # إدراج 10000 صف for i in range(10000): cursor.execute("INSERT INTO test VALUES (?, ?)", (i, f"test{i}")) connection.commit() # قياس وقت تنفيذ استعلام start_time = time.time() results = adapter.execute_query("SELECT * FROM test WHERE id < 1000") end_time = time.time() # التحقق من أن الاستعلام ينفذ في أقل من 100 مللي ثانية assert end_time - start_time < 0.1 assert len(results) == 1000 ``` ### اختبارات الأمان تتحقق اختبارات الأمان من أن النظام آمن ضد الهجمات. **الخصائص**: - تتحقق من أن النظام يقاوم الهجمات المعروفة - تختبر آليات الأمان - تحدد الثغرات المحتملة **مثال**: ```python def test_sql_injection_prevention(): """اختبار أن النظام محمي ضد حقن SQL.""" # تكوين المحول مع قاعدة بيانات اختبار config = {"type": "sqlite", "path": ":memory:"} adapter = SQLiteAdapter(config) # إنشاء جدول اختبار connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT, password TEXT)") cursor.execute("INSERT INTO users VALUES (1, 'admin', 'secret')") connection.commit() # إنشاء مدير استعلامات مع المحول query_manager = QueryManager(adapter) # محاولة حقن SQL malicious_query = "SELECT * FROM users WHERE username = 'admin' OR 1=1 --'" # التحقق من أن مدير الاستعلامات يكتشف ويمنع الحقن with pytest.raises(SecurityException): query_manager.execute(malicious_query) ``` ## أدوات الاختبار ### pytest نستخدم [pytest](https://docs.pytest.org/) كإطار اختبار رئيسي: ```bash # تثبيت pytest والإضافات pip install pytest pytest-cov pytest-mock # تنفيذ جميع الاختبارات pytest # تنفيذ اختبارات محددة pytest tests/unit/ pytest tests/integration/ # تنفيذ مع تغطية الكود pytest --cov=mcp_dbutils # إنشاء تقرير تغطية HTML pytest --cov=mcp_dbutils --cov-report=html ``` ### تجهيزات pytest تسمح تجهيزات pytest بإعداد بيئة الاختبار: ```python @pytest.fixture def sqlite_adapter(): """تجهيزة توفر محول SQLite مكون مع قاعدة بيانات في الذاكرة.""" config = {"type": "sqlite", "path": ":memory:"} adapter = SQLiteAdapter(config) # إنشاء جدول اختبار connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)") cursor.execute("INSERT INTO test VALUES (1, 'test1'), (2, 'test2')") connection.commit() yield adapter # التنظيف adapter.disconnect() def test_with_fixture(sqlite_adapter): """اختبار محول SQLite باستخدام التجهيزة.""" results = sqlite_adapter.execute_query("SELECT * FROM test WHERE id = 1") assert len(results) == 1 assert results[0]["id"] == 1 assert results[0]["name"] == "test1" ``` ### المحاكاة نستخدم `unittest.mock` أو `pytest-mock` للمحاكاة: ```python def test_with_mock(mocker): """اختبار دالة باستخدام المحاكاة.""" # إنشاء محاكاة لاتصال قاعدة البيانات mock_connection = mocker.MagicMock() mock_cursor = mocker.MagicMock() mock_connection.cursor.return_value = mock_cursor # تكوين المحاكاة لإرجاع نتائج محددة mock_cursor.fetchall.return_value = [("test1",), ("test2",)] mock_cursor.description = [("name",)] # استبدال طريقة الاتصال بالمحاكاة mocker.patch("mcp_dbutils.adapters.sqlite.sqlite3.connect", return_value=mock_connection) # إنشاء المحول adapter = SQLiteAdapter({"type": "sqlite", "path": "dummy.db"}) # تنفيذ الاستعلام results = adapter.execute_query("SELECT name FROM test") # التحقق من أن الاستعلام تم تنفيذه بشكل صحيح mock_cursor.execute.assert_called_once_with("SELECT name FROM test") assert len(results) == 2 assert results[0]["name"] == "test1" assert results[1]["name"] == "test2" ``` ### قواعد بيانات الاختبار لاختبارات التكامل، نستخدم قواعد بيانات حقيقية: - **SQLite**: قاعدة بيانات في الذاكرة (`:memory:`) - **PostgreSQL**: نسخة Docker للاختبارات - **MySQL**: نسخة Docker للاختبارات ```python @pytest.fixture(scope="session") def postgres_adapter(): """تجهيزة توفر محول PostgreSQL مكون مع قاعدة بيانات اختبار.""" # بدء PostgreSQL مع Docker container = start_postgres_container() # انتظار جاهزية PostgreSQL wait_for_postgres(container) # تكوين المحول config = { "type": "postgres", "host": "localhost", "port": container.get_exposed_port(5432), "dbname": "testdb", "user": "postgres", "password": "postgres" } adapter = PostgreSQLAdapter(config) # إنشاء جدول اختبار connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE test (id SERIAL PRIMARY KEY, name TEXT)") cursor.execute("INSERT INTO test (name) VALUES ('test1'), ('test2')") connection.commit() yield adapter # التنظيف adapter.disconnect() container.stop() ``` ## تنظيم الاختبارات يتم تنظيم الاختبارات في دليل `tests/`: ``` tests/ ├── __init__.py ├── conftest.py # تجهيزات مشتركة ├── fixtures/ # بيانات اختبار │ ├── sqlite_schema.sql # مخطط SQLite للاختبارات │ ├── postgres_schema.sql # مخطط PostgreSQL للاختبارات │ └── mysql_schema.sql # مخطط MySQL للاختبارات ├── unit/ # اختبارات الوحدة │ ├── __init__.py │ ├── test_adapters.py # اختبارات المحولات │ ├── test_config.py # اختبارات التكوين │ ├── test_mcp.py # اختبارات بروتوكول MCP │ └── test_query.py # اختبارات معالجة الاستعلامات ├── integration/ # اختبارات التكامل │ ├── __init__.py │ ├── test_sqlite.py # اختبارات تكامل SQLite │ ├── test_postgres.py # اختبارات تكامل PostgreSQL │ └── test_mysql.py # اختبارات تكامل MySQL └── e2e/ # اختبارات من طرف إلى طرف ├── __init__.py └── test_mcp_server.py # اختبارات خادم MCP ``` ## أفضل الممارسات ### تسمية الاختبارات اتبع هذه اصطلاحات التسمية: - ملفات الاختبار تبدأ بـ `test_` - دوال الاختبار تبدأ بـ `test_` - أسماء الاختبارات يجب أن تكون وصفية وتشير إلى ما يتم اختباره ```python # جيد def test_query_validator_rejects_insert_statements(): ... # سيء def test_validator(): ... ``` ### التأكيدات استخدم تأكيدات pytest لرسائل خطأ أكثر وضوحًا: ```python # جيد assert result == expected, f"توقعت {expected}، حصلت على {result}" # سيء if result != expected: raise AssertionError("فشل الاختبار") ``` ### عزل الاختبارات يجب أن يكون كل اختبار مستقلاً: - لا يعتمد على الحالة التي تركتها اختبارات أخرى - استخدم تجهيزات لإعداد وتنظيف البيئة - تجنب المتغيرات العالمية أو المشتركة ```python # جيد def test_independent_1(sqlite_adapter): sqlite_adapter.execute_query("INSERT INTO test VALUES (3, 'test3')") results = sqlite_adapter.execute_query("SELECT * FROM test WHERE id = 3") assert len(results) == 1 def test_independent_2(sqlite_adapter): # هذا الاختبار لا يعتمد على الحالة التي أنشأها test_independent_1 results = sqlite_adapter.execute_query("SELECT * FROM test WHERE id = 1") assert len(results) == 1 ``` ### اختبارات معلمة استخدم اختبارات معلمة لاختبار حالات متعددة: ```python @pytest.mark.parametrize("query,is_valid", [ ("SELECT * FROM users", True), ("INSERT INTO users VALUES (1, 'test')", False), ("UPDATE users SET name = 'test' WHERE id = 1", False), ("DELETE FROM users WHERE id = 1", False), ("DROP TABLE users", False), ]) def test_query_validator_parametrized(query, is_valid): """اختبار أن مدقق الاستعلام يحدد بشكل صحيح الاستعلامات غير المصرح بها.""" validator = QueryValidator() assert validator.validate(query) is is_valid ``` ### اختبارات الانحدار أنشئ اختبارات محددة للأخطاء المصححة: ```python def test_regression_issue_123(): """اختبار أن الخطأ #123 تم إصلاحه.""" # الخطأ كان: محول SQLite لا يتعامل بشكل صحيح مع قيم NULL config = {"type": "sqlite", "path": ":memory:"} adapter = SQLiteAdapter(config) # إنشاء جدول اختبار connection = adapter.connect() cursor = connection.cursor() cursor.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)") cursor.execute("INSERT INTO test VALUES (1, NULL)") connection.commit() # التحقق من أن قيم NULL يتم التعامل معها بشكل صحيح results = adapter.execute_query("SELECT * FROM test WHERE id = 1") assert len(results) == 1 assert results[0]["id"] == 1 assert results[0]["name"] is None ``` ## التكامل المستمر يتم تنفيذ الاختبارات تلقائيًا عبر GitHub Actions: ```yaml # .github/workflows/test.yml name: Test on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: [3.10, 3.11, 3.12] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev,test]" - name: Lint with ruff run: | ruff check . - name: Test with pytest run: | pytest --cov=mcp_dbutils --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: file: ./coverage.xml ``` ## تغطية الكود نهدف إلى تغطية كود بنسبة 90% على الأقل: ```bash # تنفيذ الاختبارات مع التغطية pytest --cov=mcp_dbutils # إنشاء تقرير تغطية HTML pytest --cov=mcp_dbutils --cov-report=html ``` تقرير التغطية متاح في دليل `htmlcov/`. ## استكشاف أخطاء الاختبارات وإصلاحها ### اختبارات فاشلة إذا فشل اختبار: 1. اقرأ رسالة الخطأ بعناية 2. تحقق من التأكيدات التي فشلت 3. استخدم `pytest -v` لمزيد من التفاصيل 4. استخدم `pytest --pdb` للتصحيح التفاعلي ### اختبارات بطيئة إذا كانت الاختبارات بطيئة: 1. حدد الاختبارات البطيئة باستخدام `pytest --durations=10` 2. استخدم المحاكاة بدلاً من الموارد الحقيقية عندما يكون ذلك ممكنًا 3. حسّن التجهيزات لتقليل وقت الإعداد 4. استخدم `pytest-xdist` لتنفيذ الاختبارات بالتوازي ### اختبارات غير مستقرة (متقلبة) إذا كانت الاختبارات غير مستقرة (تفشل بشكل عشوائي): 1. حدد الاختبارات غير المستقرة 2. تحقق من التبعيات الخارجية (قواعد البيانات، الشبكة، إلخ) 3. تأكد من أن الاختبارات معزولة 4. استخدم `pytest-rerunfailures` لإعادة تنفيذ الاختبارات الفاشلة ## الخلاصة الاختبارات ضرورية لضمان جودة وموثوقية MCP Database Utilities. باتباع أفضل الممارسات واستخدام الأدوات المناسبة، يمكننا الحفاظ على مجموعة اختبارات شاملة وفعالة تمنحنا الثقة في الكود الخاص بنا.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/donghao1393/mcp-dbutils'

If you have feedback or need assistance with the MCP directory API, please join our Discord server