اختبار قواعد أمان Cloud Firestore

أثناء إنشاء تطبيقك، قد تحتاج إلى حظر إمكانية الوصول إلى قاعدة بيانات Cloud Firestore. مع ذلك، قبل إطلاق الميزة، عليك إدخال معلومات أكثر دقة عن Cloud Firestore Security Rules. باستخدام محاكي Cloud Firestore، بالإضافة إلى إنشاء نماذج أولية لاختبار الميزات والسلوك العام لتطبيقك، يمكنك كتابة اختبارات وحدة تتحقّق من سلوك Cloud Firestore Security Rules.

البدء السريع

للحصول على بعض حالات الاختبار الأساسية التي تتضمّن قواعد بسيطة، جرِّب نموذج البدء السريع.

فهم Cloud Firestore Security Rules

يمكنك تنفيذ Firebase Authentication و Cloud Firestore Security Rules للمصادقة بدون خادم والتفويض والتحقق من صحة البيانات عند استخدام مكتبات برامج الويب والأجهزة الجوّالة.

تشمل Cloud Firestore Security Rules جزأين:

  1. عبارة match تحدد المستندات في قاعدة البيانات.
  2. تعبير allow يتحكّم في الوصول إلى هذه المستندات

تُستخدم Firebase Authentication للتحقّق من بيانات اعتماد المستخدمين وتوفير الأساس لأنظمة الوصول المستندة إلى المستخدمين والأدوار.

يتم تقييم كل طلب قاعدة بيانات من Cloud Firestore مكتبة تطبيقات الويب أو الأجهزة الجوّالة باستخدام قواعد الأمان قبل قراءة أي بيانات أو كتابتها. إذا كانت القواعد ترفض الوصول إلى أي من مسارات المستندات المحدّدة، يفشل الطلب بأكمله.

اطّلِع على مزيد من المعلومات عن Cloud Firestore Security Rules في مقالة البدء في استخدام Cloud Firestore Security Rules.

تثبيت المحاكي

لتثبيت المحاكي Cloud Firestore، يمكنك استخدام Firebase CLI وتنفيذ الأمر أدناه:

firebase setup:emulators:firestore

تشغيل المحاكي

ابدأ بإعداد مشروع Firebase في دليل العمل. هذه هي الخطوة الأولى الشائعة عند استخدام واجهة برمجة التطبيقات Firebase CLI.

firebase init

ابدأ تشغيل المحاكي باستخدام الأمر التالي. سيتم تشغيل المحاكي حتى إنهاء العملية:

firebase emulators:start --only firestore

وفي كثير من الحالات، يمكنك بدء تشغيل المحاكي وتشغيل مجموعة تجريبية ثم إغلاق المحاكي بعد إجراء الاختبارات. يمكنك إجراء ذلك بسهولة باستخدام الأمر emulators:exec:

firebase emulators:exec --only firestore "./my-test-script.sh"

عند بدء المحاكي، سيحاول تشغيله على منفذ تلقائي (8080). يمكنك تغيير منفذ المحاكي من خلال تعديل القسم "emulators" في ملف firebase.json باتّباع الخطوات التالية:

{
  // ...
  "emulators": {
    "firestore": {
      "port": "YOUR_PORT"
    }
  }
}

قبل تشغيل المحاكي

قبل البدء في استخدام المحاكي، يجب مراعاة ما يلي:

  • سيحمّل المحاكي بشكل مبدئي القواعد المحدّدة في الحقل firestore.rules من ملف firebase.json. ويتوقع اسم ملف محلي يحتوي على Cloud Firestore Security Rules ويطبّق هذه القواعد على جميع المشاريع. إذا لم تقدِّم مسار الملف المحلي أو لم تستخدم loadFirestoreRules الطريقة الموضّحة أدناه، سيتعامل المحاكي مع كل المشاريع على أنّها تتضمّن قواعد مفتوحة.
  • تعمل معظم حِزم SDK لمنصة Firebase مع أدوات المحاكاة مباشرةً، فإنّ مكتبة @firebase/rules-unit-testing فقط تتيح محاكاة auth في "قواعد الأمان"، ما يجعل اختبارات الوحدات أسهل بكثير. بالإضافة إلى ذلك، تدعم المكتبة بعض الميزات الخاصة بالمحاكي مثل محو جميع البيانات، كما هو موضح أدناه.
  • ستوافق المحاكيات أيضًا على الرموز المميّزة لإصدار الإصدار العلني من Firebase Auth المقدَّمة من خلال حِزم تطوير البرامج (SDK) للعملاء، وستقيّم القواعد وفقًا لذلك، ما يسمح بربط تطبيقك مباشرةً بالمحاكيات في عمليات الدمج والاختبارات اليدوية.

تشغيل اختبارات الوحدة المحلية

إجراء اختبارات الوحدة المحلية باستخدام حزمة تطوير البرامج (SDK) لإصدار 9 من JavaScript

توزّع Firebase مكتبة اختبار وحدات قواعد الأمان مع كلّ من الإصدار 9 من حزمة تطوير البرامج (SDK) لـ JavaScript والإصدار 8 من حزمة SDK. تختلف واجهات برمجة تطبيقات المكتبة بشكل كبير. ننصحك باستخدام مكتبة الاختبار في الإصدار 9، فهي أكثر سلاسة وتطلب إعدادًا أقل للاتصال بالمحاكيات، وبالتالي يمكنك تجنُّب استخدام موارد الإنتاج بدون قصد. للتوافق مع الإصدارات القديمة، نواصل إتاحة مكتبة اختبار الإصدار 8.

استخدِم وحدة @firebase/rules-unit-testing للتفاعل مع المحاكي الذي يتم تشغيله على الجهاز. إذا ظهرت لك أخطاء متعلّقة بانتهاء مهلة أو ECONNREFUSED، تحقّق جيدًا مما إذا كان المحاكي قيد التشغيل.

ننصحك بشدة باستخدام إصدار حديث من Node.js حتى تتمكّن من استخدام رمز async/await. يتضمن تقريبًا كل السلوك الذي قد ترغب في اختباره دوال غير متزامنة، وقد تم تصميم وحدة الاختبار للعمل مع التعليمات البرمجية المستندة إلى التعهد.

تدرك مكتبة اختبار الوحدات v9 (القواعد) v9 دائمًا الأدوات المحاكية ولا تمس موارد الإنتاج أبدًا.

يمكنك استيراد المكتبة باستخدام عبارات الاستيراد المُركّبة في الإصدار 9. على سبيل المثال:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment
} from "@firebase/rules-unit-testing"

// Use `const { … } = require("@firebase/rules-unit-testing")` if imports are not supported
// Or we suggest `const testing = require("@firebase/rules-unit-testing")` if necessary.

بعد الاستيراد، يتضمّن تنفيذ اختبارات الوحدة ما يلي:

  • إنشاء RulesTestEnvironment وضبطه من خلال طلب إلى initializeTestEnvironment
  • إعداد بيانات اختبارية بدون تفعيل القواعد، باستخدام أسلوب سهل الاستخدام يتيح لك تخطّيها مؤقتًا، RulesTestEnvironment.withSecurityRulesDisabled
  • إعداد حزمة الاختبار وعناصر الاختبار لكل اختبار قبل/بعد العناصر الخطية مع عمليات الاتصال لتنظيف بيانات الاختبار والبيئة، مثل RulesTestEnvironment.cleanup() أو RulesTestEnvironment.clearFirestore()
  • تنفيذ حالات الاختبار التي تحاكي حالات المصادقة باستخدام RulesTestEnvironment.authenticatedContext وRulesTestEnvironment.unauthenticatedContext

الطرق الشائعة ووظائف المنفعة

اطّلِع أيضًا على طرق الاختبار الخاصة بالمحاكي في حزمة تطوير البرامج (SDK) من الإصدار 9.

initializeTestEnvironment() => RulesTestEnvironment

تعمل هذه الدالة على إعداد بيئة اختبار لاختبار وحدة القواعد. قم باستدعاء هذه الدالة أولاً لإعداد الاختبار. يتطلب التنفيذ الناجح تشغيل المحاكيات.

تقبل الدالة عنصرًا اختياريًا يحدِّد TestEnvironmentConfig، ويمكن أن يتألّف من رقم تعريف المشروع وإعدادات ضبط المحاكي.

let testEnv = await initializeTestEnvironment({
  projectId: "demo-project-1234",
  firestore: {
    rules: fs.readFileSync("firestore.rules", "utf8"),
  },
});

RulesTestEnvironment.authenticatedContext({ user_id: string, tokenOptions?: TokenOptions }) => RulesTestContext

تؤدي هذه الطريقة إلى إنشاء RulesTestContext، والذي يعمل كمستخدم مصادقة تمت مصادقته. ستتضمّن الطلبات التي تم إنشاؤها من خلال السياق المعروض رمزًا مميّزًا وهميًا للمصادقة مرفقًا. يمكنك اختياريًا تمرير عنصر يحدّد مطالبات مخصّصة أو عمليات إلغاء لحمولات رمز المصادقة.

استخدِم عنصر سياق الاختبار الذي تم عرضه في اختباراتك للوصول إلى أي مثيلات للمحاكي تم ضبطها، بما في ذلك تلك التي تم ضبطها باستخدام initializeTestEnvironment.

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

const alice = testEnv.authenticatedContext("alice", { … });
// Use the Firestore instance associated with this context
await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

RulesTestEnvironment.unauthenticatedContext() => RulesTestContext

تؤدي هذه الطريقة إلى إنشاء RulesTestContext، الذي يتصرّف مثل عميل لم يسجّل الدخول من خلال المصادقة. لن يتم إرفاق علامات Firebase Auth بطلبات الربط التي تم إنشاؤها من خلال السياق الذي تم إرجاعه.

استخدِم عنصر سياق الاختبار الذي تم إرجاعه في اختباراتك للوصول إلى أي مثيلات مُحاكي تم ضبطها، بما في ذلك المثيلات التي تم ضبطها باستخدام initializeTestEnvironment.

// Assuming a Cloud Storage app and the Storage emulator for this example
import { getStorage, ref, deleteObject } from "firebase/storage";

const alice = testEnv.unauthenticatedContext();

// Use the Cloud Storage instance associated with this context
const desertRef = ref(alice.storage(), 'images/desert.jpg');
await assertSucceeds(deleteObject(desertRef));

RulesTestEnvironment.withSecurityRulesDisabled()

يمكنك تشغيل دالة إعداد اختبار باستخدام سياق يعمل كما لو كانت قواعد الأمان متوقفة.

تأخذ هذه الطريقة دالة ردّ اتصال تستخدِم سياق Security-Rules-bypassing وتُعرِض وعدًا. وسيتم إتلاف السياق بمجرد حل الوعد / رفضه.

RulesTestEnvironment.cleanup()

وتؤدي هذه الطريقة إلى إتلاف كل RulesTestContexts التي تم إنشاؤها في بيئة الاختبار، وتنظيف الموارد الأساسية، ما يسمح بالخروج من الأمر.

ولا تؤدي هذه الطريقة إلى تغيير حالة أدوات المحاكاة بأي شكل من الأشكال. لإعادة ضبط البيانات بين الاختبارات، استخدِم طريقة محو البيانات الخاصة بمحاكي التطبيق.

assertSucceeds(pr: Promise<any>)) => Promise<any>

هذه دالة مساعدة حالة اختبار.

تؤكِّد الدالة أنّه سيتم حلّ عملية المحاكي المقدَّمة بجهاز Promise بدون انتهاك لقواعد الأمان.

await assertSucceeds(setDoc(alice.firestore(), '/users/alice'), { ... });

assertFails(pr: Promise<any>)) => Promise<any>

هذه دالة مساعدة حالة اختبار.

تؤكِّد الدالة أنّه سيتم رفض عملية تطبيق Promise المُقدَّمة لعملية المحاكي بسبب انتهاك "قواعد الأمان".

await assertFails(setDoc(alice.firestore(), '/users/bob'), { ... });

الطرق الخاصة بالمحاكي

يُرجى الاطّلاع أيضًا على أساليب الاختبار الشائعة ووظائف الأداة المساعدة في الإصدار 9 من حزمة تطوير البرامج (SDK).

RulesTestEnvironment.clearFirestore() => Promise<void>

تعمل هذه الطريقة على محو البيانات في قاعدة بيانات Firestore التي تنتمي إلى projectId الذي تم إعداده لمحاكي Firestore.

RulesTestContext.firestore(settings?: Firestore.FirestoreSettings) => Firestore;

تحصل هذه الطريقة على مثيل Firestore لسياق الاختبار هذا. ويمكن استخدام مثيل حزمة تطوير البرامج (SDK) لعميل JavaScript JS الذي تم عرضه مع واجهات برمجة تطبيقات حزمة تطوير البرامج (SDK) للعميل (الإصدار 9 الوحداتي أو الإصدار 9 المكمِّل).

عرض تقييمات القواعد

يتيح لك محاكي Cloud Firestore إمكانية عرض طلبات العميل بشكل مرئي في واجهة مستخدم Emulator Suite، بما في ذلك تتبُّع التقييم لقواعد أمان Firebase.

افتح علامة التبويب Firestore > الطلبات للاطّلاع على ترتيب التقييم التفصيلي لكل طلب.

أداة مراقبة طلبات المحاكي في Firestore تعرِض تقييمات قواعد الأمان

إنشاء تقارير اختبارية

بعد إجراء مجموعة من الاختبارات، يمكنك الوصول إلى تقارير التغطية التجريبية التي توضح كيفية تقييم كل قاعدة من قواعد الأمان لديك.

للحصول على التقارير، يمكنك إجراء طلب بحث عن نقطة نهاية معروضة على المحاكي أثناء تشغيله. للحصول على نسخة متوافقة مع المتصفّحات، استخدِم عنوان URL التالي:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage.html

يؤدي ذلك إلى تقسيم قواعدك إلى تعبيرات وتعبيرات فرعية يمكنك تمرير مؤشر الماوس فوقها للحصول على مزيد من المعلومات، بما في ذلك عدد التقييمات والقيم المعروضة. للحصول على نسخة JSON الأوّلية من هذه البيانات، أدرِج عنوان URL التالي في طلب البحث:

http://localhost:8080/emulator/v1/projects/<project_id>:ruleCoverage

الاختلافات بين المحاكي والإصدار العلني

  1. لست بحاجة إلى إنشاء مشروع Cloud Firestore بشكل صريح. وينشئ المحاكي تلقائيًا أي مثيل يتم الوصول إليه.
  2. لا يعمل محاكي Cloud Firestore مع مسار Firebase Authentication العادي. وبدلاً من ذلك، في حزمة تطوير البرامج (SDK) الاختبارية لاختبار Firebase، قدّمنا الطريقة initializeTestApp() في مكتبة rules-unit-testing، وهي تستخدم الحقل auth. سيعمل اسم معرِّف Firebase الذي تم إنشاؤه باستخدام هذه الطريقة كما لو تمت مصادقته بنجاح كأي كيان تقدّمه. إذا نجحت في اجتياز null، سيعمل كمستخدم لم تتم مصادقته (لن تعمل قواعد auth != null على سبيل المثال).

تحديد المشاكل المعروفة وحلّها

أثناء استخدام محاكي Cloud Firestore، قد تواجهك الصعوبات التالية المعروفة. اتّبِع الإرشادات أدناه لتحديد أيّ سلوك غير منتظم تواجهه وحلّه. تمت كتابة هذه الملاحظات مع وضع مكتبة اختبار وحدات "قواعد الأمان" في الاعتبار، ولكن يمكن تطبيق الأساليب العامة على أي حزمة تطوير برامج (SDK) لمنصّة Firebase.

سلوك الاختبار غير متسق

إذا كانت اختباراتك تجتاز وتفشل أحيانًا، حتى بدون أي تغييرات على الاختبارات نفسها، فقد تحتاج إلى التحقق من أنها متسلسلة بشكل صحيح. إنّ معظم التفاعلات مع المحاكي غير متزامنة، لذا تحقّق جيدًا من أنّه تم ترتيب كل الرمز غير المتزامن بشكل صحيح. يمكنك إصلاح التسلسل إما عن طريق سلسلة الوعود، أو باستخدام تدوين await بكل بساطة.

وعلى وجه الخصوص، راجِع العمليات غير المتزامنة التالية:

  • إعداد قواعد الأمان، باستخدام initializeTestEnvironment مثلاً.
  • قراءة البيانات وكتابتها باستخدام، مثل db.collection("users").doc("alice").get()
  • التأكيدات التشغيلية، بما في ذلك assertSucceeds وassertFails

تجتاز الاختبارات في المرة الأولى فقط التي يتم فيها تحميل المحاكي

المحاكي يعتمد على الحالة. ويخزّن جميع البيانات المكتوبة فيه في الذاكرة، لذلك يتم فقدان أي بيانات عند إيقاف المحاكي. إذا كنت تُجري عدة اختبارات باستخدام معرّف المشروع نفسه، يمكن أن ينتج عن كل اختبار بيانات قد تؤثر في الاختبارات اللاحقة. يمكنك استخدام أيّ من الطرق التالية لتجاوز هذا السلوك:

  • استخدِم معرّفات مشاريع فريدة لكل اختبار. يُرجى العلم أنّه إذا اخترت إجراء ذلك، ستحتاج إلى استدعاء initializeTestEnvironment كجزء من كل اختبار، ولا يتم تحميل القواعد تلقائيًا إلا بمعرّف المشروع التلقائي.
  • أعِد هيكلة اختباراتك كي لا تتفاعل مع البيانات المكتوبة سابقًا، (على سبيل المثال، استخدِم مجموعة مختلفة لكل اختبار).
  • حذف جميع البيانات المكتوبة أثناء الاختبار

إعداد الاختبار معقّد جدًا

عند إعداد الاختبار، قد تحتاج إلى تعديل البيانات بطريقة لا تسمح بها Cloud Firestore Security Rules. إذا كانت قواعدك تجعل عملية إعداد الاختبار معقدة، جرِّب استخدام RulesTestEnvironment.withSecurityRulesDisabled في خطوات الإعداد ، لكي لا تؤدي عمليات القراءة والكتابة إلى حدوث أخطاء PERMISSION_DENIED.

بعد ذلك، يمكن إجراء الاختبار كمستخدم تمت مصادقته أو لم تتم مصادقته باستخدام RulesTestEnvironment.authenticatedContext وunauthenticatedContext على التوالي. ويتيح لك ذلك التحقّق من أنّ Cloud Firestore Security Rules يسمح بمختلف الحالات أو يرفضها بشكل صحيح.