/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const userAgentID = "7eb873f9-8d47-4218-804b-fff78dc04e88";

function run_test() {
  do_get_profile();
  setPrefs({
    userAgentID,
    "testing.ignorePermission": true,
  });
  run_next_test();
}

add_task(async function test_expiration_origin_threshold() {
  let db = PushServiceWebSocket.newPushDB();
  registerCleanupFunction(() => db.drop().then(_ => db.close()));

  await db.put({
    channelID: "eb33fc90-c883-4267-b5cb-613969e8e349",
    pushEndpoint: "https://example.org/push/1",
    scope: "https://example.com/auctions",
    pushCount: 0,
    lastPush: 0,
    version: null,
    originAttributes: "",
    quota: 16,
  });
  await db.put({
    channelID: "46cc6f6a-c106-4ffa-bb7c-55c60bd50c41",
    pushEndpoint: "https://example.org/push/2",
    scope: "https://example.com/deals",
    pushCount: 0,
    lastPush: 0,
    version: null,
    originAttributes: "",
    quota: 16,
  });

  // The notification threshold is per-origin, even with multiple service
  // workers for different scopes.
  await PlacesTestUtils.addVisits([
    {
      uri: "https://example.com/login",
      title: "Sign in to see your auctions",
      visitDate: (Date.now() - 7 * 24 * 60 * 60 * 1000) * 1000,
      transition: Ci.nsINavHistoryService.TRANSITION_LINK,
    },
    // We'll always use your most recent visit to an origin.
    {
      uri: "https://example.com/auctions",
      title: "Your auctions",
      visitDate: (Date.now() - 2 * 24 * 60 * 60 * 1000) * 1000,
      transition: Ci.nsINavHistoryService.TRANSITION_LINK,
    },
    // ...But we won't count downloads or embeds.
    {
      uri: "https://example.com/invoices/invoice.pdf",
      title: "Invoice #123",
      visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000,
      transition: Ci.nsINavHistoryService.TRANSITION_EMBED,
    },
    {
      uri: "https://example.com/invoices/invoice.pdf",
      title: "Invoice #123",
      visitDate: Date.now() * 1000,
      transition: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
    },
  ]);

  // We expect to receive 6 notifications: 5 on the `auctions` channel,
  // and 1 on the `deals` channel. They're from the same origin, but
  // different scopes, so each can send 5 notifications before we remove
  // their subscription.
  let updates = 0;
  let notifyPromise = promiseObserverNotification(
    PushServiceComponent.pushTopic,
    (subject, data) => {
      updates++;
      return updates == 6;
    }
  );

  let unregisterDone;
  let unregisterPromise = new Promise(resolve => (unregisterDone = resolve));

  PushService.init({
    serverURI: "wss://push.example.org/",
    db,
    makeWebSocket(uri) {
      return new MockWebSocket(uri, {
        onHello(request) {
          this.serverSendMsg(
            JSON.stringify({
              messageType: "hello",
              status: 200,
              uaid: userAgentID,
            })
          );
          // We last visited the site 2 days ago, so we can send 5
          // notifications without throttling. Sending a 6th should
          // drop the registration.
          for (let version = 1; version <= 6; version++) {
            this.serverSendMsg(
              JSON.stringify({
                messageType: "notification",
                updates: [
                  {
                    channelID: "eb33fc90-c883-4267-b5cb-613969e8e349",
                    version,
                  },
                ],
              })
            );
          }
          // But the limits are per-channel, so we can send 5 more
          // notifications on a different channel.
          this.serverSendMsg(
            JSON.stringify({
              messageType: "notification",
              updates: [
                {
                  channelID: "46cc6f6a-c106-4ffa-bb7c-55c60bd50c41",
                  version: 1,
                },
              ],
            })
          );
        },
        onUnregister(request) {
          equal(
            request.channelID,
            "eb33fc90-c883-4267-b5cb-613969e8e349",
            "Unregistered wrong channel ID"
          );
          equal(request.code, 201, "Expected quota exceeded unregister reason");
          unregisterDone();
        },
        // We expect to receive acks, but don't care about their
        // contents.
        onACK(request) {},
      });
    },
  });

  await unregisterPromise;

  await notifyPromise;

  let expiredRecord = await db.getByKeyID(
    "eb33fc90-c883-4267-b5cb-613969e8e349"
  );
  strictEqual(expiredRecord.quota, 0, "Expired record not updated");
});
