// META: title=IndexedDB: scoping for database / object store / index names, and index keys
// META: global=window,worker
// META: script=resources/support-promises.js

// Spec: https://w3c.github.io/IndexedDB/#constructs

'use strict';

// Creates the structure inside a test database.
//
// The structure includes two stores with identical indexes and nearly-similar
// records. The records differ in the "path" attribute values, which are used to
// verify that IndexedDB returns the correct records when queried.
//
// databaseName appears redundant, but we don't want to rely on database.name.
const buildStores = (database, databaseName, useUniqueKeys) => {
  for (let storeName of ['x', 'y']) {
    const store = database.createObjectStore(
        storeName, {keyPath: 'pKey', autoIncrement: true});
    for (let indexName of ['x', 'y']) {
      store.createIndex(indexName, `${indexName}Key`, {unique: useUniqueKeys});
    }

    for (let xKeyRoot of ['x', 'y']) {
      for (let yKeyRoot of ['x', 'y']) {
        let xKey, yKey;
        if (useUniqueKeys) {
          xKey = `${xKeyRoot}${yKeyRoot}`;
          yKey = `${yKeyRoot}${xKeyRoot}`;
        } else {
          xKey = xKeyRoot;
          yKey = yKeyRoot;
        }
        const path = `${databaseName}-${storeName}-${xKeyRoot}-${yKeyRoot}`;
        store.put({xKey: xKey, yKey: yKey, path: path});
      }
    }
  }
};

// Creates two databases with identical structures.
const buildDatabases = (testCase, useUniqueKeys) => {
  return createNamedDatabase(
             testCase, 'x',
             database => buildStores(database, 'x', useUniqueKeys))
      .then(database => database.close())
      .then(
          () => createNamedDatabase(
              testCase, 'y',
              database => buildStores(database, 'y', useUniqueKeys)))
      .then(database => database.close());
};

// Reads all the store's values using an index.
//
// Returns a Promise that resolves with an array of values.
const readIndex =
    (testCase, index) => {
      return new Promise((resolve, reject) => {
        const results = [];
        const request = index.openCursor(IDBKeyRange.bound('a', 'z'), 'next');
        request.onsuccess = testCase.step_func(() => {
          const cursor = request.result;
          if (cursor) {
            results.push(cursor.value);
            cursor.continue();
          } else {
            resolve(results);
          }
        });
      });
    }

// Verifies that a database contains the expected records.
const checkDatabaseContent =
    (testCase, database, databaseName, usedUniqueKeys) => {
      const promises = [];
      const transaction = database.transaction(['x', 'y'], 'readonly');
      for (let storeName of ['x', 'y']) {
        const store = transaction.objectStore(storeName);
        for (let indexName of ['x', 'y']) {
          const index = store.index(indexName);

          const promise = readIndex(testCase, index).then((results) => {
            assert_array_equals(
                results.map(result => `${result.path}:${result.pKey}`).sort(),
                [
                  `${databaseName}-${storeName}-x-x:1`,
                  `${databaseName}-${storeName}-x-y:2`,
                  `${databaseName}-${storeName}-y-x:3`,
                  `${databaseName}-${storeName}-y-y:4`
                ],
                'The results should include all records put into the store');

            let expectedKeys = (usedUniqueKeys) ?
                ['xx:xx', 'xy:yx', 'yx:xy', 'yy:yy'] :
                ['x:x', 'x:y', 'y:x', 'y:y'];
            assert_array_equals(
                results.map(result => `${result.xKey}:${result.yKey}`).sort(),
                expectedKeys,
                'The results should include all the index keys put in the store');

            assert_array_equals(
                results.map(result => result[`${indexName}Key`]),
                results.map(result => result[`${indexName}Key`]).sort(),
                'The results should be sorted by the index key');
          });
          promises.push(promise);
        }
      }

      return Promise.all(promises).then(() => database);
    }

promise_test(testCase => {
  return buildDatabases(testCase, false)
      .then(() => openNamedDatabase(testCase, 'x', 1))
      .then(database => checkDatabaseContent(testCase, database, 'x', false))
      .then(database => database.close())
      .then(() => openNamedDatabase(testCase, 'y', 1))
      .then(database => checkDatabaseContent(testCase, database, 'y', false))
      .then(database => database.close());
}, 'Non-unique index keys');

promise_test(testCase => {
  return buildDatabases(testCase, true)
      .then(() => openNamedDatabase(testCase, 'x', 1))
      .then(database => checkDatabaseContent(testCase, database, 'x', true))
      .then(database => database.close())
      .then(() => openNamedDatabase(testCase, 'y', 1))
      .then(database => checkDatabaseContent(testCase, database, 'y', true))
      .then(database => database.close());
}, 'Unique index keys');
