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

"use strict";

ChromeUtils.defineESModuleGetters(this, {
  RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
  JsonSchema: "resource://gre/modules/JsonSchema.sys.mjs",
});

const URLBAR_PERSISTENCE_SETTINGS_KEY = "urlbar-persisted-search-terms";

/**
 * Checks to see if a value is an object or not.
 *
 * @param {*} value
 *   The value to check.
 * @returns {boolean}
 */
function isObject(value) {
  return value != null && typeof value == "object" && !Array.isArray(value);
}

/**
 * This function modifies the schema to prevent allowing additional properties
 * on objects. This is used to enforce that the schema contains everything that
 * we deliver via the search configuration.
 *
 * These checks are not enabled in-product, as we want to allow older versions
 * to keep working if we add new properties for whatever reason.
 *
 * @param {object} section
 *   The section to check to see if an additionalProperties flag should be added.
 */
function disallowAdditionalProperties(section) {
  // It is generally acceptable for new properties to be added to the
  // configuration as older builds will ignore them.
  //
  // As a result, we only check for new properties on nightly builds, and this
  // avoids us having to uplift schema changes. This also helps preserve the
  // schemas as documentation of "what was supported in this version".
  if (!AppConstants.NIGHTLY_BUILD) {
    info("Skipping additional properties validation.");
    return;
  }

  if (section.type == "object") {
    section.additionalProperties = false;
  }
  for (let value of Object.values(section)) {
    if (isObject(value)) {
      disallowAdditionalProperties(value);
    }
  }
}

add_task(async function test_search_telemetry_validates_to_schema() {
  let schema = await IOUtils.readJSON(
    PathUtils.join(
      do_get_cwd().path,
      "urlbar-persisted-search-terms-schema.json"
    )
  );

  disallowAdditionalProperties(schema);

  let data = await RemoteSettings(URLBAR_PERSISTENCE_SETTINGS_KEY).get();

  let validator = new JsonSchema.Validator(schema);

  for (let entry of data) {
    // Records in Remote Settings contain additional properties independent of
    // the schema. Hence, we don't want to validate their presence.
    delete entry.schema;
    delete entry.id;
    delete entry.last_modified;

    let result = validator.validate(entry);
    let message = `Should validate ${entry.providerId}`;
    if (!result.valid) {
      message += `:\n${JSON.stringify(result.errors, null, 2)}`;
    }
    Assert.ok(result.valid, message);
  }
});
