firebase messaging.sendToDevice()をsinonで置き換える

https://firebase.google.com/docs/cloud-messaging/send-message?hl=ja#send-to-individual-devices

注: 1 つのリクエストでメッセージを送信できる宛先デバイスの最大数は 1,000 台です。配列に 1,000 を超える登録トークンを指定すると、そのリクエストは messaging/invalid-recipient エラーで失敗します。

ということなので分割して送信することにしました。 分割するロジックはfor文で回す感じにしたので、無限ループが怖いので テストコードで確認しました。

他のtest spyみたいなのは使ったことがありますが、 sinonは初めてでしたが、公式のサンプルで使われていたので。

https://firebase.google.cn/docs/functions/unit-testing?hl=ja

import * as admin from 'firebase-admin';
import {
  messaging
} from 'firebase-admin';
import MessagingPayload = messaging.MessagingPayload;
import MessagingOptions = messaging.MessagingOptions;
import MessagingDevicesResponse = messaging.MessagingDevicesResponse;

/** 
 * // https://firebase.google.com/docs/cloud-messaging/send-message?hl=ja#send-to-individual-devices
 * divisionUnit=1000
*/
export async function batchSendToDevice(
  fcmTokens: string[],
  divisionUnit: number,
  message: MessagingPayload,
  options: MessagingOptions)
  : Promise<MessagingDevicesResponse[]> {

  const promises = [];
  for (let i = 0; i < fcmTokens.length; i += divisionUnit) {
    const dividedTokens = fcmTokens.slice(i, i + divisionUnit);
    promises.push(admin.messaging().sendToDevice(dividedTokens, message, options));
  }
  return await Promise.all(promises);
}

テストコード

import * as admin from 'firebase-admin';
import {
  messaging
} from 'firebase-admin';
import MessagingDevicesResponse = messaging.MessagingDevicesResponse;
import * as sinon from 'sinon';
import { batchSendToDevice } from './batch-send-to-device';

admin.initializeApp();

describe("batchSendToDevice", () => {
  it("Split logic works fine", async () => {
    const sandbox = sinon.createSandbox({});
    const messaging = admin.messaging();

    sandbox.replace(messaging, 'sendToDevice',
      (registrationToken, payload, options) => {

        const response: MessagingDevicesResponse = {
          canonicalRegistrationTokenCount: 1,
          failureCount: 0,
          multicastId: 1,
          results: [],
          successCount: 1
        };

        if (!Array.isArray(registrationToken)) {
          response.multicastId = Number(registrationToken);
          return new Promise((result, reject) => {
            return result(response);
          });
        }

        registrationToken.forEach((e) => {
          response.results.push({ messageId: e });
          return response;
        });
        return new Promise((result, reject) => {
          return result(response);
        });
      });
    const tokens = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'];
    const ret = await batchSendToDevice(tokens, 5, {}, {});
    console.log(JSON.stringify(ret, null, 2));
  });
});

完全なサンプル https://github.com/na8esin/firebase-practice/tree/master/functions/src