'How to fix a SQL Integrity error while trying to build a Flask application?

I am currently following Miguel Grinberg's Flask Web Development tutorial. I am trying to code unittests for testing a "Change Email" functionality as described in commit 8h in his GitHub repo :(https://github.com/miguelgrinberg/flasky/commit/d5b2e68ba717b1f1bce2930d43c2099cab6998a5)

class UserModelTestCase(unittest.TestCase):
def test_password_setter(self):
    #...

def test_no_password_getter(self):
    #...

def test_password_verification(self):
    #...

def test_password_salts_are_random(self):
    #...

def test_valid_confirmation_token(self):
    u = User(password='cat')
    db.session.add(u)
    db.session.commit()
    token = u.generate_confirmation_token()
    self.assertTrue(u.confirm(token))

def test_invalid_confirmation_token(self):
    u1 = User(password='cat')
    u2 = User(password='dog')
    db.session.add(u1)
    db.session.add(u2)
    db.session.commit()
    token = u1.generate_confirmation_token()
    self.assertFalse(u2.confirm(token))

def test_expired_confirmation_token(self):
    u = User(password='cat')
    db.session.add(u)
    db.session.commit()
    token = u.generate_confirmation_token(1)
    time.sleep(2)
    self.assertFalse(u.confirm(token))

def test_valid_reset_token(self):
    u = User(password='cat')
    db.session.add(u)
    db.session.commit()
    token = u.generate_reset_token()
    self.assertTrue(User.reset_password(token, 'dog'))
    self.assertTrue(u.verify_password('dog'))

def test_invalid_reset_token(self):
    u = User(password='cat')
    db.session.add(u)
    db.session.commit()
    token = u.generate_reset_token()
    self.assertFalse(User.reset_password(token + 'a', 'horse'))
    self.assertTrue(u.verify_password('cat'))

def test_valid_email_change_token(self):
    u = User(email='[email protected]', password='cat')
    db.session.add(u)
    db.session.commit()
    token = u.generate_email_change_token('[email protected]')
    self.assertTrue(u.change_email(token))
    self.assertTrue(u.email == '[email protected]')

def test_invalid_email_change_token(self):
    u1 = User(email='[email protected]', password='cat')
    u2 = User(email='[email protected]', password='dog')
    db.session.add(u1)
    db.session.add(u2)
    db.session.commit()
    token = u1.generate_email_change_token('[email protected]')
    self.assertFalse(u2.change_email(token))
    self.assertTrue(u2.email == '[email protected]')

def test_duplicate_email_change_token(self):
    u1 = User(email='[email protected]', password='cat')
    u2 = User(email='[email protected]', password='dog')
    db.session.add(u1)
    db.session.add(u2)
    db.session.commit()
    token = u2.generate_email_change_token('[email protected]')
    self.assertFalse(u2.change_email(token))
    self.assertTrue(u2.email == '[email protected]')

However, I keep getting an Integrity Error while running the test.

flask test

test_duplicate_email_change_token (test_user_model.UserModelTestCase)... ok
test_expired_confirmation_token(test_user_model.UserModelTestCase) ... ok
test_invalid_confirmation_token (test_user_model.UserModelTestCase) ... ok
test_invalid_email_change_token(test_user_model.UserModelTestCase) ... ERROR
test_invalid_reset_token(test_user_model.UserModelTestCase) ... ERROR
test_no_password_getter(test_user_model.UserModelTestCase) ... ok
test_password_salts_are_random (test_user_model.UserModelTestCase) ...ok
test_password_setter (test_user_model.UserModelTestCase) ... ok
test_password_verification (test_user_model.UserModelTestCase) ... ok
test_valid_confirmation_token (test_user_model.UserModelTestCase) ... ERROR
test_valid_email_change_token(test_user_model.UserModelTestCase) ... ERROR
test_valid_reset_token(test_user_model.UserModelTestCase) ... ERROR

====================================================================== ERROR: test_invalid_email_change_token(test_user_model.UserModelTestCase)

Traceback (most recent call last): File "c:\users\rishi\pycharmprojects\flasky\lib\site-> packages\sqlalchemy\engine\base.py", line 1802, in _execute_context
self.dialect.do_execute( File "c:\users\rishi\pycharmprojects\flasky\lib\sitepackages\sqlalchemy\engine\default.py", line 732, in do_executecursor.execute(statement, parameters)
sqlite3.IntegrityError: UNIQUE constraint failed: users.email

For reference, please see database model User saved in models.py

class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)

@property
def password(self):
    raise AttributeError('password is not a readable attribute')

@password.setter
def password(self, password):
    self.password_hash = generate_password_hash(password)

def verify_password(self, password):
    return check_password_hash(self.password_hash, password)

def generate_confirmation_token(self, expiration=3600):
    s = Serializer(current_app.config['SECRET_KEY'], expiration)
    return s.dumps({'confirm':self.id}).decode('UTF-8')

def confirm(self, token):
    s = Serializer(current_app.config['SECRET_KEY'])
    try:
        data = s.loads(token.encode('UTF-8'))
    except:
        return False
    if data.get('confirm') != self.id:
        return False
    self.confirmed = True
    db.session.add(self)
    return True

def generate_reset_token(self, expiration=3600):
    s = Serializer(current_app.config['SECRET_KEY'], expiration)
    return s.dumps({'reset': self.id}).decode('utf-8')

@staticmethod
def reset_password(token, new_password):
    s = Serializer(current_app.config['SECRET_KEY'])
    try:
        data = s.loads(token.encode('utf-8'))
    except:
        return False
    user = User.query.get(data.get('reset'))
    if user is None:
        return False
    user.password = new_password
    db.session.add(user)
    return True

def generate_email_change_token(self, new_email, expiration=3600):
    s = Serializer(current_app.config['SECRET_KEY'], expiration)
    return s.dumps({'change_email':self.id, 'new_email': new_email}).decode('utf-8')

def change_email(self, token):
    s = Serializer(current_app.config['SECRET_KEY'])
    try:
        data = s.loads(token.encode('utf-8'))
    except:
        return False
    if data.get('change_email') != self.id:
        return False
    new_email = data.get('new_email')
    if new_email is None:
        return False
    if self.query.filter_by(email=new_email).first() is not None:
        return False
    self.email = new_email
    db.session.add(self)
    return True

def __repr__(self):
    return '<User %r>' % self.username


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source