text: "use strict";
  
  const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
  const request = require('request');
  const MongoClient = require("mongodb").MongoClient;
  
  describe("Email Verification Token Expiration: ", () => {
  
    it_exclude_dbs(['postgres'])('show the invalid link page, if the user clicks on the verify email link after the email verify token expires', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 0.5, // 0.5 second
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      }).then(() => {
        // wait for 1 second - simulate user behavior to some extent
        setTimeout(() => {
          expect(sendEmailOptions).not.toBeUndefined();
  
          request.get(sendEmailOptions.link, {
              followRedirect: false,
          }, (error, response, body) => {
            expect(response.statusCode).toEqual(302);
            expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
            done();
          });
        }, 1000);
      });
    });
  
    it_exclude_dbs(['postgres'])('emailVerified should set to false, if the user does not verify their email before the email verify token expires', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 0.5, // 0.5 second
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      }).then(() => {
        // wait for 1 second - simulate user behavior to some extent
        setTimeout(() => {
          expect(sendEmailOptions).not.toBeUndefined();
  
          request.get(sendEmailOptions.link, {
              followRedirect: false,
          }, (error, response, body) => {
            expect(response.statusCode).toEqual(302);
            user.fetch()
            .then(() => {
              expect(user.get('emailVerified')).toEqual(false);
              done();
            })
            .catch((err) => {
              fail("this should not fail");
              done();
            });
          });
        }, 1000);
      });
    });
  
    it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      }).then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
          expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/verify_email_success.html?username=testEmailVerifyTokenValidity');
          done();
        });
      });
    });
  
    it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      }).then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
          user.fetch()
          .then(() => {
            expect(user.get('emailVerified')).toEqual(true);
            done();
          })
          .catch((err) => {
            fail("this should not fail");
            done();
          });
        });
      });
    });
  
    it_exclude_dbs(['postgres'])('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      }).then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
          Parse.User.logIn("testEmailVerifyTokenValidity", "expiringToken")
          .then(user => {
            expect(typeof user).toBe('object');
            expect(user.get('emailVerified')).toBe(true);
            done();
          })
          .catch((error) => {
            fail('login should have succeeded');
            done();
          });
        });
      });
    });
  
    it_exclude_dbs(['postgres'])('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: 'http://localhost:8378/1'
      })
      .then(() => {
        user.setUsername('sets_email_verify_token_expires_at');
        user.setPassword('expiringToken');
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
        const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
        return MongoClient.connect(databaseURI);
      })
      .then(database => {
        expect(typeof database).toBe('object');
        return database.collection('test__User').findOne({username: 'sets_email_verify_token_expires_at'});
      })
      .then(user => {
        expect(typeof user).toBe('object');
        expect(user.emailVerified).toEqual(false);
        expect(typeof user._email_verify_token).toBe('string');
        expect(typeof user._email_verify_token_expires_at).toBe('object');
        done();
      })
      .catch(error => {
        fail("this should not fail");
        done();
      });
    });
  
    it_exclude_dbs(['postgres'])('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("unsets_email_verify_token_expires_at");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
  
          const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
          MongoClient.connect(databaseURI)
          .then(database => {
            expect(typeof database).toBe('object');
            return database.collection('test__User').findOne({username: 'unsets_email_verify_token_expires_at'});
          })
          .then(user => {
            expect(typeof user).toBe('object');
            expect(user.emailVerified).toEqual(true);
            expect(typeof user._email_verify_token).toBe('undefined');
            expect(typeof user._email_verify_token_expires_at).toBe('undefined');
            done();
          })
          .catch(error => {
            fail("this should not fail");
            done();
          });
        });
      })
      .catch(error => {
        fail("this should not fail");
        done();
      });
    });
  
    it_exclude_dbs(['postgres'])('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      var serverConfig = {
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        publicServerURL: "http://localhost:8378/1"
      };
  
      // setup server WITHOUT enabling the expire email verify token flag
      reconfigureServer(serverConfig)
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
        return new Promise((resolve, reject) => {
          request.get(sendEmailOptions.link, { followRedirect: false, })
          .on('error', error => reject(error))
          .on('response', (response) => {
            expect(response.statusCode).toEqual(302);
            resolve(user.fetch());
          });
        });
      })
      .then(() => {
        expect(user.get('emailVerified')).toEqual(true);
        // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
        serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
        return reconfigureServer(serverConfig);
      })
      .then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
          expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
          done();
        });
      })
      .catch((err) => {
        fail("this should not fail");
        done();
      });
    });
  
    it_exclude_dbs(['postgres'])('clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show an invalid link', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      var serverConfig = {
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        publicServerURL: "http://localhost:8378/1"
      };
  
      // setup server WITHOUT enabling the expire email verify token flag
      reconfigureServer(serverConfig)
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
        // just get the user again - DO NOT email verify the user
        return user.fetch();
      })
      .then(() => {
        expect(user.get('emailVerified')).toEqual(false);
        // RECONFIGURE the server i.e., ENABLE the expire email verify token flag
        serverConfig.emailVerifyTokenValidityDuration = 5; // 5 seconds
        return reconfigureServer(serverConfig);
      })
      .then(() => {
        request.get(sendEmailOptions.link, {
            followRedirect: false,
        }, (error, response, body) => {
          expect(response.statusCode).toEqual(302);
          expect(response.body).toEqual('Found. Redirecting to http://localhost:8378/1/apps/invalid_link.html');
          done();
        });
      })
      .catch((err) => {
        fail("this should not fail");
        done();
      });
    });
  
    it_exclude_dbs(['postgres'])('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
  
      const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
      let db;
  
      let user = new Parse.User();
      let userBeforeEmailReset;
  
      let sendEmailOptions;
      let emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      };
      let serverConfig = {
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      };
  
      reconfigureServer(serverConfig)
      .then(() => {
        user.setUsername("newEmailVerifyTokenOnEmailReset");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
        return MongoClient.connect(databaseURI);
      })
      .then(database => {
        expect(typeof database).toBe('object');
        db = database; //save the db object for later use
        return db.collection('test__User').findOne({username: 'newEmailVerifyTokenOnEmailReset'});
      })
      .then(userFromDb => {
        expect(typeof userFromDb).toBe('object');
        userBeforeEmailReset = userFromDb;
  
        // trigger another token generation by setting the email
        user.set('email', 'user@parse.com');
        return new Promise((resolve, reject) => {
          // wait for half a sec to get a new expiration time
          setTimeout( () => resolve(user.save()), 500 );
        });
      })
      .then(() => {
        // get user data after email reset and new token generation
        return db.collection('test__User').findOne({username: 'newEmailVerifyTokenOnEmailReset'});
      })
      .then(userAfterEmailReset => {
        expect(typeof userAfterEmailReset).toBe('object');
        expect(userBeforeEmailReset._email_verify_token).not.toEqual(userAfterEmailReset._email_verify_token);
        expect(userBeforeEmailReset._email_verify_token_expires_at).not.toEqual(userAfterEmailReset.__email_verify_token_expires_at);
        done();
      })
      .catch((err) => {
        fail("this should not fail");
        done();
      });
    });
  
    it_exclude_dbs(['postgres'])('client should not see the _email_verify_token_expires_at field', done => {
      var user = new Parse.User();
      var sendEmailOptions;
      var emailAdapter = {
        sendVerificationEmail: options => {
          sendEmailOptions = options;
        },
        sendPasswordResetEmail: () => Promise.resolve(),
        sendMail: () => {}
      }
      reconfigureServer({
        appName: 'emailVerifyToken',
        verifyUserEmails: true,
        emailAdapter: emailAdapter,
        emailVerifyTokenValidityDuration: 5, // 5 seconds
        publicServerURL: "http://localhost:8378/1"
      })
      .then(() => {
        user.setUsername("testEmailVerifyTokenValidity");
        user.setPassword("expiringToken");
        user.set('email', 'user@parse.com');
        return user.signUp();
      })
      .then(() => {
  
        user.fetch()
        .then(() => {
          expect(user.get('emailVerified')).toEqual(false);
          expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined');
          done();
        })
        .catch(error => {
          fail("this should not fail");
          done();
        });
  
      });
    });
  
  })
  