text: const path = require('path');
  const should = require('should');
  const supertest = require('supertest');
  const sinon = require('sinon');
  const testUtils = require('../../../../utils');
  const localUtils = require('./utils');
  const config = require('../../../../../core/shared/config');
  const labs = require('../../../../../core/server/services/labs');
  
  const ghost = testUtils.startGhost;
  
  let request;
  
  describe('Members API', function () {
      before(function () {
          sinon.stub(labs, 'isSet').withArgs('members').returns(true);
      });
  
      after(function () {
          sinon.restore();
      });
  
      before(function () {
          return ghost()
              .then(function () {
                  request = supertest.agent(config.get('url'));
              })
              .then(function () {
                  return localUtils.doAuth(request, 'members');
              });
      });
  
      it('Can search by case-insensitive name', function () {
          return request
              .get(localUtils.API.getApiQuery('members/?search=egg'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(200)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
                  jsonResponse.members[0].email.should.equal('member1@test.com');
                  localUtils.API.checkResponse(jsonResponse, 'members');
                  localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
                  localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
              });
      });
  
      it('Can search by case-insensitive email', function () {
          return request
              .get(localUtils.API.getApiQuery('members/?search=MEMBER2'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(200)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
                  jsonResponse.members[0].email.should.equal('member2@test.com');
                  localUtils.API.checkResponse(jsonResponse, 'members');
                  localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
                  localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
              });
      });
  
      it('Can search for paid members', function () {
          return request
              .get(localUtils.API.getApiQuery('members/?search=egon&paid=true'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(200)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
                  jsonResponse.members[0].email.should.equal('paid@test.com');
                  localUtils.API.checkResponse(jsonResponse, 'members');
                  localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
                  localUtils.API.checkResponse(jsonResponse.meta.pagination, 'pagination');
              });
      });
  
      it('Add should fail when passing incorrect email_type query parameter', function () {
          const member = {
              name: 'test',
              email: 'memberTestAdd@test.com'
          };
  
          return request
              .post(localUtils.API.getApiQuery(`members/?send_email=true&email_type=lel`))
              .send({members: [member]})
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(422);
      });
  
      // NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
      it.skip('Can set a "Complimentary" subscription', function () {
          const memberToChange = {
              name: 'Comped Member',
              email: 'member2comp@test.com'
          };
  
          const memberChanged = {
              comped: true
          };
  
          return request
              .post(localUtils.API.getApiQuery(`members/`))
              .send({members: [memberToChange]})
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
  
                  return jsonResponse.members[0];
              })
              .then((newMember) => {
                  return request
                      .put(localUtils.API.getApiQuery(`members/${newMember.id}/`))
                      .send({members: [memberChanged]})
                      .set('Origin', config.get('url'))
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200)
                      .then((res) => {
                          should.not.exist(res.headers['x-cache-invalidate']);
  
                          const jsonResponse = res.body;
  
                          should.exist(jsonResponse);
                          should.exist(jsonResponse.members);
                          jsonResponse.members.should.have.length(1);
                          localUtils.API.checkResponse(jsonResponse.members[0], 'member', 'stripe');
                          jsonResponse.members[0].name.should.equal(memberToChange.name);
                          jsonResponse.members[0].email.should.equal(memberToChange.email);
                          jsonResponse.members[0].comped.should.equal(memberToChange.comped);
                      });
              });
      });
  
      it('Can delete a member without cancelling Stripe Subscription', async function () {
          const member = {
              name: 'Member 2 Delete',
              email: 'Member2Delete@test.com'
          };
  
          const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
              .send({members: [member]})
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
  
                  return jsonResponse.members[0];
              });
  
          await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/`))
              .set('Origin', config.get('url'))
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(204)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
  
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
              });
      });
  
      // NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
      it.skip('Can delete a member and cancel Stripe Subscription', async function () {
          const member = {
              name: 'Member 2 Delete',
              email: 'Member2Delete@test.com',
              comped: true
          };
  
          const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
              .send({members: [member]})
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
  
                  return jsonResponse.members[0];
              });
  
          await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/?cancel=true`))
              .set('Origin', config.get('url'))
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(204)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
  
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
              });
      });
  
      // NOTE: this test should be enabled and expanded once test suite fully supports Stripe mocking
      it.skip('Does not cancel Stripe Subscription if cancel_subscriptions is not set to "true"', async function () {
          const member = {
              name: 'Member 2 Delete',
              email: 'Member2Delete@test.com',
              comped: true
          };
  
          const createdMember = await request.post(localUtils.API.getApiQuery(`members/`))
              .send({members: [member]})
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  jsonResponse.members.should.have.length(1);
  
                  return jsonResponse.members[0];
              });
  
          await request.delete(localUtils.API.getApiQuery(`members/${createdMember.id}/?cancel=false`))
              .set('Origin', config.get('url'))
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(204)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
  
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
              });
      });
  
      it('Can import CSV with minimum one field and labels', function () {
          let importLabel;
  
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .field('labels', ['global-label-1', 'global-label-1'])
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-labels.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  should.exist(jsonResponse.meta.import_label);
                  jsonResponse.meta.import_label.slug.should.equal('global-label-1');
                  jsonResponse.meta.stats.imported.count.should.equal(2);
                  jsonResponse.meta.stats.invalid.count.should.equal(0);
  
                  importLabel = jsonResponse.meta.import_label.slug;
                  return request
                      .get(localUtils.API.getApiQuery(`members/?&filter=label:${importLabel}`))
                      .set('Origin', config.get('url'))
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200);
              })
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  should.equal(jsonResponse.members.length, 2);
  
                  const importedMember1 = jsonResponse.members.find(m => m.email === 'member+labels_1@example.com');
                  should.exist(importedMember1);
                  should(importedMember1.name).equal(null);
                  should(importedMember1.note).equal(null);
                  importedMember1.subscribed.should.equal(true);
                  importedMember1.comped.should.equal(false);
                  importedMember1.stripe.should.not.be.undefined();
                  importedMember1.stripe.subscriptions.length.should.equal(0);
  
                  // check label order
                  // 1 unique global + 1 record labels
                  importedMember1.labels.length.should.equal(2);
                  importedMember1.labels[0].slug.should.equal('label');
                  importedMember1.labels[1].slug.should.equal('global-label-1');
  
                  const importedMember2 = jsonResponse.members.find(m => m.email === 'member+labels_2@example.com');
                  should.exist(importedMember2);
                  // 1 unique global + 2 record labels
                  importedMember2.labels.length.should.equal(3);
                  importedMember2.labels[0].slug.should.equal('another-label');
                  importedMember2.labels[1].slug.should.equal('and-one-more');
                  importedMember2.labels[2].slug.should.equal('global-label-1');
              });
      });
  
      it('Can import CSV with mapped fields', function () {
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .field('mapping[email]', 'correo_electrpnico')
              .field('mapping[name]', 'nombre')
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-mappings.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  jsonResponse.meta.stats.imported.count.should.equal(1);
                  jsonResponse.meta.stats.invalid.count.should.equal(0);
  
                  should.exist(jsonResponse.meta.import_label);
                  jsonResponse.meta.import_label.slug.should.match(/^import-/);
              })
              .then(() => {
                  return request
                      .get(localUtils.API.getApiQuery(`members/?search=${encodeURIComponent('member+mapped_1@example.com')}`))
                      .set('Origin', config.get('url'))
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200);
              })
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
                  should.exist(jsonResponse.members[0]);
  
                  const importedMember1 = jsonResponse.members[0];
                  should(importedMember1.email).equal('member+mapped_1@example.com');
                  should(importedMember1.name).equal('Hannah');
                  should(importedMember1.note).equal('no need to map me');
                  importedMember1.subscribed.should.equal(true);
                  importedMember1.comped.should.equal(false);
                  importedMember1.stripe.should.not.be.undefined();
                  importedMember1.stripe.subscriptions.length.should.equal(0);
                  importedMember1.labels.length.should.equal(1); // auto-generated import label
              });
      });
  
      it('Can import CSV with labels and provide additional labels', function () {
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/valid-members-defaults.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  jsonResponse.meta.stats.imported.count.should.equal(2);
                  jsonResponse.meta.stats.invalid.count.should.equal(0);
              })
              .then(() => {
                  return request
                      .get(localUtils.API.getApiQuery(`members/`))
                      .set('Origin', config.get('url'))
                      .expect('Content-Type', /json/)
                      .expect('Cache-Control', testUtils.cacheRules.private)
                      .expect(200);
              })
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.members);
  
                  const defaultMember1 = jsonResponse.members.find(member => (member.email === 'member+defaults_1@example.com'));
                  should(defaultMember1.name).equal(null);
                  should(defaultMember1.note).equal(null);
                  defaultMember1.subscribed.should.equal(true);
                  defaultMember1.comped.should.equal(false);
                  defaultMember1.stripe.should.not.be.undefined();
                  defaultMember1.stripe.subscriptions.length.should.equal(0);
                  defaultMember1.labels.length.should.equal(1); // auto-generated import label
  
                  const defaultMember2 = jsonResponse.members.find(member => (member.email === 'member+defaults_2@example.com'));
                  should(defaultMember2).not.be.undefined();
              });
      });
  
      it('Fails to import members with stripe_customer_id', function () {
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-with-stripe-ids.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  jsonResponse.meta.stats.imported.count.should.equal(0);
                  jsonResponse.meta.stats.invalid.count.should.equal(2);
  
                  should.equal(jsonResponse.meta.stats.invalid.errors.length, 1);
                  jsonResponse.meta.stats.invalid.errors[0].message.should.equal('Missing Stripe connection');
  
                  should.not.exist(jsonResponse.meta.import_label);
              });
      });
  
      it('Fails to import memmber with invalid values', function () {
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .field('labels', ['new-global-label'])
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-invalid-values.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  jsonResponse.meta.stats.imported.count.should.equal(0);
                  jsonResponse.meta.stats.invalid.count.should.equal(3);
  
                  const validationErrors = jsonResponse.meta.stats.invalid.errors;
  
                  should.equal(validationErrors.length, 4);
  
                  const nameValidationErrors = validationErrors.find(
                      obj => obj.message === 'Validation failed for \'name\'.'
                  );
                  should.exist(nameValidationErrors);
                  nameValidationErrors.count.should.equal(1);
  
                  const emailValidationErrors = validationErrors.find(
                      obj => obj.message === 'Validation (isEmail) failed for email'
                  );
                  should.exist(emailValidationErrors);
                  emailValidationErrors.count.should.equal(1);
  
                  const createdAtValidationErrors = validationErrors.find(
                      obj => obj.message === 'Validation failed for \'created_at\'.'
                  );
                  should.exist(createdAtValidationErrors);
                  createdAtValidationErrors.count.should.equal(1);
  
                  const compedPlanValidationErrors = validationErrors.find(
                      obj => obj.message === 'Validation failed for \'complimentary_plan\'.'
                  );
                  should.exist(compedPlanValidationErrors);
                  compedPlanValidationErrors.count.should.equal(1);
  
                  should.exist(jsonResponse.meta.import_label);
                  jsonResponse.meta.import_label.slug.should.equal('new-global-label');
              });
      });
  
      it('Fails to import member duplicate emails', function () {
          return request
              .post(localUtils.API.getApiQuery(`members/upload/`))
              .attach('membersfile', path.join(__dirname, '/../../../../utils/fixtures/csv/members-duplicate-emails.csv'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(201)
              .then((res) => {
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.meta);
                  should.exist(jsonResponse.meta.stats);
  
                  jsonResponse.meta.stats.imported.count.should.equal(1);
                  jsonResponse.meta.stats.invalid.count.should.equal(1);
  
                  should.equal(jsonResponse.meta.stats.invalid.errors.length, 1);
                  jsonResponse.meta.stats.invalid.errors[0].message.should.equal('Member already exists');
                  jsonResponse.meta.stats.invalid.errors[0].count.should.equal(1);
  
                  should.exist(jsonResponse.meta.import_label);
                  jsonResponse.meta.import_label.slug.should.match(/^import-/);
              });
      });
  
      it('Can fetch stats with no ?days param', function () {
          return request
              .get(localUtils.API.getApiQuery('members/stats/'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              // .expect(200) - doesn't surface underlying errors in tests
              .then((res) => {
                  res.status.should.equal(200, JSON.stringify(res.body));
  
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.total);
                  should.exist(jsonResponse.total_in_range);
                  should.exist(jsonResponse.total_on_date);
                  should.exist(jsonResponse.new_today);
  
                  // 3 from fixtures and 6 imported in previous tests
                  jsonResponse.total.should.equal(10);
              });
      });
  
      it('Can fetch stats with ?days=90', function () {
          return request
              .get(localUtils.API.getApiQuery('members/stats/?days=90'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              // .expect(200) - doesn't surface underlying errors in tests
              .then((res) => {
                  res.status.should.equal(200, JSON.stringify(res.body));
  
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.total);
                  should.exist(jsonResponse.total_in_range);
                  should.exist(jsonResponse.total_on_date);
                  should.exist(jsonResponse.new_today);
  
                  // 3 from fixtures and 6 imported in previous tests
                  jsonResponse.total.should.equal(10);
              });
      });
  
      it('Can fetch stats with ?days=all-time', function () {
          return request
              .get(localUtils.API.getApiQuery('members/stats/?days=all-time'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              // .expect(200) - doesn't surface underlying errors in tests
              .then((res) => {
                  res.status.should.equal(200, JSON.stringify(res.body));
  
                  should.not.exist(res.headers['x-cache-invalidate']);
                  const jsonResponse = res.body;
  
                  should.exist(jsonResponse);
                  should.exist(jsonResponse.total);
                  should.exist(jsonResponse.total_in_range);
                  should.exist(jsonResponse.total_on_date);
                  should.exist(jsonResponse.new_today);
  
                  // 3 from fixtures and 6 imported in previous tests
                  jsonResponse.total.should.equal(10);
              });
      });
  
      it('Errors when fetching stats with unknown days param value', function () {
          return request
              .get(localUtils.API.getApiQuery('members/stats/?days=nope'))
              .set('Origin', config.get('url'))
              .expect('Content-Type', /json/)
              .expect('Cache-Control', testUtils.cacheRules.private)
              .expect(422);
      });
  });
  