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

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

البدء السريع

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

فهم قواعد أمان Cloud Firestore

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

تتضمن قواعد أمان Cloud Firestore عنصرين:

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

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

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

يمكنك التعرّف على مزيد من المعلومات عن قواعد أمان Cloud Firestore في مقالة بدء استخدام قواعد أمان Cloud Firestore.

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

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

firebase setup:emulators:firestore

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

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

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 ويطبّق هذه القواعد على جميع المشاريع. إذا لم توفّر مسار الملف المحلي أو استخدمت الطريقة loadFirestoreRules كما هو موضّح أدناه، سيتعامل المحاكي مع جميع المشاريع على أنّها تتضمّن قواعد مفتوحة.
  • تعمل معظم حِزم SDK لمنصة Firebase مع أدوات المحاكاة مباشرةً، فإنّ مكتبة @firebase/rules-unit-testing فقط تتيح محاكاة auth في "قواعد الأمان"، ما يجعل اختبارات الوحدات أسهل بكثير. بالإضافة إلى ذلك، تدعم المكتبة بعض الميزات الخاصة بالمحاكي مثل محو جميع البيانات، كما هو موضح أدناه.
  • ستقبل أيضًا أدوات المحاكاة رموز مصادقة Firebase المميزة للإنتاج المقدمة من خلال حزم SDK للعميل، وستقيّم القواعد وفقًا لذلك، ما يسمح بربط تطبيقك مباشرةً بالمحاكيات في التكامل والاختبارات اليدوية.

إجراء اختبارات الوحدة المحلية

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

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

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

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

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

يمكنك استيراد المكتبة باستخدام عبارات الاستيراد النموذجية للإصدار v9. على سبيل المثال:

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

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

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

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 للطلبات التي تم إنشاؤها عبر السياق المعروض.

استخدِم عنصر سياق الاختبار الذي تم عرضه في اختباراتك للوصول إلى أي مثيلات للمحاكي تم ضبطها، بما في ذلك تلك التي تم ضبطها باستخدام 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()

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

تأخذ هذه الطريقة دالة استدعاء، والتي تأخذ السياق الذي يتجاوز حدود "قواعد الأمان" وتُرجع وعدًا. وسيتم إتلاف السياق بمجرد حل الوعد / رفضه.

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. وبدلاً من ذلك، في حزمة تطوير البرامج (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 فعليًا. إذا كانت قواعدك تجعل عملية إعداد الاختبار معقّدة، جرِّب استخدام RulesTestEnvironment.withSecurityRulesDisabled في خطوات الإعداد، كي لا تؤدي عمليات القراءة والكتابة إلى ظهور أخطاء PERMISSION_DENIED.

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