text: /**
   * Copyright (c) 2017, salesforce.com, inc.
   * All rights reserved.
   * Licensed under the BSD 3-Clause license.
   * For full license text, see LICENSE.txt file in the repo root or
   * https://opensource.org/licenses/BSD-3-Clause
   */
  
  /**
   * tests/cache/sampleStore.js
   */
  'use strict'; // eslint-disable-line strict
  const sampleStore = require('../../cache/sampleStore');
  const ssConstants = sampleStore.constants;
  const samstoinit = require('../../cache/sampleStoreInit');
  const rcli = require('../../cache/redisCache').client.sampleStore;
  const featureToggles = require('feature-toggles');
  const expect = require('chai').expect;
  const tu = require('../testUtils');
  const u = require('./utils');
  const Aspect = tu.db.Aspect;
  const Subject = tu.db.Subject;
  const Sample = tu.db.Sample;
  const initialFeatureState = featureToggles
    .isFeatureEnabled(sampleStore.constants.featureName);
  const subAspMapType = ssConstants.prefix + ssConstants.separator +
    ssConstants.objectType.subAspMap;
  const aspSubMapType = ssConstants.prefix + ssConstants.separator +
    ssConstants.objectType.aspSubMap;
  
  describe('tests/cache/sampleStore.js >', () => {
    describe('feature off >', () => {
      before(() => tu.toggleOverride(sampleStore.constants.featureName, false));
  
      after(() => tu.toggleOverride(sampleStore.constants.featureName,
        initialFeatureState));
  
      it('init', (done) => {
        samstoinit.init()
        .then((res) => expect(res).to.be.false)
        .then(() => done())
        .catch(done);
      });
    });
  
    describe('feature on >', () => {
      let a1;
      let a2;
      let a3;
      let a4;
      let s1;
      let s2;
      let s3;
      let s4;
      let user1;
      let user2;
      let user3;
      let user4;
  
      before((done) => {
        tu.toggleOverride(sampleStore.constants.featureName, true);
        Aspect.create({
          isPublished: true,
          name: `${tu.namePrefix}Aspect1`,
          timeout: '30s',
          valueType: 'NUMERIC',
          criticalRange: [0, 1],
          relatedLinks: [
            { name: 'Google', value: 'http://www.google.com' },
            { name: 'Yahoo', value: 'http://www.yahoo.com' },
          ],
        })
        .then((created) => (a1 = created))
        .then(() => Aspect.create({
          isPublished: true,
          name: `${tu.namePrefix}Aspect2`,
          timeout: '10m',
          valueType: 'NUMERIC',
          okRange: [10, 100],
        }))
        .then((created) => (a2 = created))
        .then(() => Aspect.create({
          isPublished: true,
          name: `${tu.namePrefix}Aspect3`,
          timeout: '10m',
          valueType: 'NUMERIC',
          okRange: [10, 100],
        }))
        .then((created) => (a3 = created))
        .then(() => Aspect.create({
          isPublished: false, // unpublished aspect should still be found
          name: `${tu.namePrefix}Aspect4`,
          timeout: '10m',
          valueType: 'NUMERIC',
          okRange: [10, 100],
        }))
        .then((created) => (a4 = created))
        .then(() => tu.createUser('myUNiqueUser1'))
        .then((usr) => {
          user1 = usr;
          return a1.addWriter(user1);
        })
        .then(() => tu.createUser('myUniqueUser2'))
        .then((usr) => {
          user2 = usr;
          return a1.addWriter(user2);
        })
        .then(() => tu.createUser('myUniqueUser3'))
        .then((usr) => {
          user3 = usr;
          return a1.addWriter(user3);
        })
        .then(() => tu.createUser('myUniqueUser4'))
        .then((usr) => {
          user4 = usr;
          return a2.addWriter(user4);
        })
        .then(() => Subject.create({
          isPublished: true,
          name: `${tu.namePrefix}Subject1`,
        }))
        .then((created) => (s1 = created))
        .then(() => Subject.create({
          isPublished: true,
          name: `${tu.namePrefix}Subject2`,
          parentId: s1.id,
        }))
        .then((created) => (s2 = created))
        .then(() => Subject.create({
          isPublished: true,
          name: `${tu.namePrefix}Subject3`,
          parentId: s1.id,
        }))
        .then((created) => (s3 = created))
        .then(() => Subject.create({
          isPublished: false, // should still be found in cache
          name: `${tu.namePrefix}Subject4`,
          parentId: s1.id,
        }))
        .then((created) => (s4 = created))
        .then(() => Sample.create({
          subjectId: s2.id,
          aspectId: a1.id,
          value: '0',
          relatedLinks: [
            { name: 'Salesforce', value: 'http://www.salesforce.com' },
          ],
        }))
        .then(() => Sample.create({
          subjectId: s2.id,
          aspectId: a2.id,
          value: '50',
          relatedLinks: [
            { name: 'Salesforce', value: 'http://www.salesforce.com' },
          ],
        }))
        .then(() => Sample.create({
          subjectId: s3.id,
          aspectId: a1.id,
          value: '5',
          relatedLinks: [
            { name: 'Salesforce', value: 'http://www.salesforce.com' },
          ],
        }))
        .then(() => done())
        .catch(done);
      });
  
      after((done) => {
        u.forceDelete(done)
        .then(() => rcli.flushallAsync())
        .then(() => tu.toggleOverride(sampleStore.constants.featureName,
          initialFeatureState))
        .then(() => done())
        .catch(done);
      });
  
      it('unpublished subject is in cache', (done) => {
        const absolutePath = '___subject1.___subject4';
        samstoinit.eradicate()
        .then(() => rcli.keysAsync(sampleStore.constants.prefix + '*'))
        .then((res) => expect(res.length).to.eql(0))
        .then(() => samstoinit.populate())
        .then(() => rcli.smembersAsync(sampleStore.constants.indexKey.subject))
        .then((res) => {
          expect(res.includes('samsto:subject:' + absolutePath))
          .to.be.true;
          expect(res.includes('samsto:subject:___subject1.___subject4'))
          .to.be.true;
        })
        .then(() => rcli.hgetallAsync('samsto:subject:' + absolutePath))
        .then((subject) => {
          // the absolutePath in the key is lowercase
          expect(subject.absolutePath.toLowerCase()).to.equal(absolutePath);
        })
        .then(() => samstoinit.init())
        .then((res) => expect(res).to.not.be.false)
        .then(() => done())
        .catch(done);
      });
  
      it('eradicate and populate', (done) => {
        samstoinit.eradicate()
        .then(() => rcli.keysAsync(sampleStore.constants.prefix + '*'))
        .then((res) => {
          expect(res.length).to.eql(0);
        })
        .then(() => samstoinit.populate())
        .then(() => rcli.smembersAsync(sampleStore.constants.indexKey.aspect))
        .then((res) => {
          expect(res.includes('samsto:aspect:___aspect1')).to.be.true;
          expect(res.includes('samsto:aspect:___aspect2')).to.be.true;
  
          // Make sure aspects that don't have samples are *also* here
          expect(res.includes('samsto:aspect:___aspect3')).to.be.true;
  
          // Make sure unpublished aspects are here
          expect(res.includes('samsto:aspect:___aspect4')).to.be.true;
        })
        .then(() => rcli.smembersAsync(sampleStore.constants.indexKey.sample))
        .then((res) => {
          expect(res.includes('samsto:sample:___subject1.___subject2|___aspect1'))
          .to.be.true;
          expect(res.includes('samsto:sample:___subject1.___subject2|___aspect2'))
          .to.be.true;
          expect(res.includes('samsto:sample:___subject1.___subject3|___aspect1'))
          .to.be.true;
        })
        .then(() => rcli.smembersAsync(sampleStore.constants.indexKey.subject))
        .then((res) => {
          expect(res.includes('samsto:subject:___subject1.___subject2'))
          .to.be.true;
          expect(res.includes('samsto:subject:___subject1.___subject3'))
          .to.be.true;
        })
        .then(() => rcli.keysAsync(subAspMapType + '*'))
        .then((res) => {
          expect(res.includes('samsto:subaspmap:___subject1.___subject2'))
          .to.be.true;
          expect(res.includes('samsto:subaspmap:___subject1.___subject3'))
          .to.be.true;
        })
        .then(() => rcli.smembersAsync('samsto:subaspmap:___subject1.___subject2'))
        .then((res) => {
          expect(res.includes('___aspect1')).to.be.true;
          expect(res.includes('___aspect2')).to.be.true;
        })
        .then(() => rcli.keysAsync(aspSubMapType + '*'))
        .then((res) => {
          expect(res.includes('samsto:aspsubmap:___aspect1'))
          .to.be.true;
          expect(res.includes('samsto:aspsubmap:___aspect2'))
          .to.be.true;
        })
        .then(() => rcli.smembersAsync('samsto:aspsubmap:___aspect1'))
        .then((res) => {
          expect(res.includes('___subject1.___subject2')).to.be.true;
          expect(res.includes('___subject1.___subject3')).to.be.true;
        })
        .then(() => rcli.smembersAsync('samsto:aspsubmap:___aspect2'))
        .then((res) => {
          expect(res.includes('___subject1.___subject2')).to.be.true;
        })
        .then(() => samstoinit.init())
        .then((res) => expect(res).to.not.be.false)
        .then(() => done())
        .catch(done);
      });
  
      it('aspects with associated writers should have its  writers field ' +
      'populated', (done) => {
        samstoinit.eradicate()
        .then(() => rcli.keysAsync(sampleStore.constants.prefix + '*'))
        .then((res) => expect(res.length).to.eql(0))
        .then(() => samstoinit.populate())
        .then(() => rcli.hgetallAsync('samsto:aspect:___aspect1'))
        .then((aspect) => {
          sampleStore.arrayObjsStringsToJson(aspect,
            sampleStore.constants.fieldsToStringify.aspect);
          expect(aspect.writers.length).equal(3);
          expect(aspect.writers)
          .to.have.members([user1.name, user2.name, user3.name]);
        })
        .then(() => rcli.hgetallAsync('samsto:aspect:___aspect2'))
        .then((aspect) => {
          sampleStore.arrayObjsStringsToJson(aspect,
            sampleStore.constants.fieldsToStringify.aspect);
          expect(aspect.writers.length).to.equal(1);
          expect(aspect.writers).to.have.members([user4.name]);
        })
        .then(() => samstoinit.init())
        .then((res) => expect(res).to.not.be.false)
        .then(() => done())
        .catch(done);
      });
  
      it('eradicate should delete all the objects in redis having the sample ' +
      'store prefix', (done) => {
        samstoinit.populate()
        .then(() => samstoinit.eradicate())
        .then(() => rcli.keysAsync(sampleStore.constants.prefix + '*'))
        .then((res) => {
          expect(res.length).to.eql(0);
          done();
        }).catch(done);
      });
    });
  
    describe('convertSubjectStrings >', () => {
      it('ok', (done) => {
        const subj = {
          absolutePath: '___NorthAmerica-limitTest8-even',
          childCount: '0',
          description: 'continent',
          id: 'f4d73b66-eed4-47f8-8006-98fa3c63f9fd',
          isPublished: 'false',
          name: '___NorthAmerica-limitTest8-even',
          relatedLinks: '[{ "name": "a", "url": "b.com" }]',
          tags: '["foo", "bar"]',
          sortBy: '_1',
          createdAt: 'Fri Jul 06 2018 15:19:28 GMT-0700 (PDT)',
          updatedAt: 'Fri Jul 06 2018 15:19:28 GMT-0700 (PDT)',
          hierarchyLevel: '1',
        };
        expect(sampleStore.convertSubjectStrings(subj)).to.deep.equal({
          absolutePath: '___NorthAmerica-limitTest8-even',
          childCount: 0,
          description: 'continent',
          id: 'f4d73b66-eed4-47f8-8006-98fa3c63f9fd',
          isPublished: false,
          name: '___NorthAmerica-limitTest8-even',
          relatedLinks: [{ name: 'a', url: 'b.com' }],
          tags: ['foo', 'bar'],
          sortBy: '_1',
          createdAt: 'Fri Jul 06 2018 15:19:28 GMT-0700 (PDT)',
          updatedAt: 'Fri Jul 06 2018 15:19:28 GMT-0700 (PDT)',
          hierarchyLevel: 1,
        });
        done();
      });
  
      it('use 0 if parseInt fails', (done) => {
        expect(sampleStore.convertSubjectStrings({ childCount: 'ABC' }))
        .to.deep.equal({
          hierarchyLevel: 0,
          childCount: 0,
          isPublished: true,
          relatedLinks: [],
          tags: [],
        });
        done();
      });
  
      it('use true if JSON.parse fails for isPublished', (done) => {
        expect(sampleStore.convertSubjectStrings({}))
        .to.deep.equal({
          hierarchyLevel: 0,
          childCount: 0,
          isPublished: true,
          relatedLinks: [],
          tags: [],
        });
        done();
      });
  
      it('use empty array if JSON.parse fails for tags/relatedLinks', (done) => {
        expect(sampleStore.convertSubjectStrings({}))
        .to.deep.equal({
          hierarchyLevel: 0,
          childCount: 0,
          isPublished: true,
          relatedLinks: [],
          tags: [],
        });
        done();
      });
    });
  });
  