diff --git a/enterprise/migrations/versions/090_add_git_user_fields_to_user.py b/enterprise/migrations/versions/090_add_git_user_fields_to_user.py new file mode 100644 index 0000000000..a8ee5416f6 --- /dev/null +++ b/enterprise/migrations/versions/090_add_git_user_fields_to_user.py @@ -0,0 +1,28 @@ +"""Add git_user_name and git_user_email columns to user table. + +Revision ID: 090 +Revises: 089 +Create Date: 2025-01-22 +""" + +import sqlalchemy as sa +from alembic import op + +revision = '090' +down_revision = '089' + + +def upgrade() -> None: + op.add_column( + 'user', + sa.Column('git_user_name', sa.String, nullable=True), + ) + op.add_column( + 'user', + sa.Column('git_user_email', sa.String, nullable=True), + ) + + +def downgrade() -> None: + op.drop_column('user', 'git_user_email') + op.drop_column('user', 'git_user_name') diff --git a/enterprise/storage/user.py b/enterprise/storage/user.py index 7b19a4eac6..adedf85366 100644 --- a/enterprise/storage/user.py +++ b/enterprise/storage/user.py @@ -31,6 +31,8 @@ class User(Base): # type: ignore user_consents_to_analytics = Column(Boolean, nullable=True) email = Column(String, nullable=True) email_verified = Column(Boolean, nullable=True) + git_user_name = Column(String, nullable=True) + git_user_email = Column(String, nullable=True) # Relationships role = relationship('Role', back_populates='users') diff --git a/enterprise/tests/unit/test_models.py b/enterprise/tests/unit/test_models.py index 95bb90fde8..0ec8c5e013 100644 --- a/enterprise/tests/unit/test_models.py +++ b/enterprise/tests/unit/test_models.py @@ -68,3 +68,84 @@ def test_user_model(session_maker): ) assert queried_org_member is not None assert queried_org_member.llm_api_key.get_secret_value() == 'test-api-key' + + +def test_user_model_git_user_fields(session_maker): + """Test that git_user_name and git_user_email columns exist and work correctly.""" + with session_maker() as session: + # Arrange + org = Org(name='test_org_git') + session.add(org) + session.flush() + + test_user_id = uuid4() + + # Act + user = User( + id=test_user_id, + current_org_id=org.id, + git_user_name='Test Git Author', + git_user_email='git@example.com', + ) + session.add(user) + session.commit() + + # Assert + queried_user = session.query(User).filter(User.id == test_user_id).first() + assert queried_user.git_user_name == 'Test Git Author' + assert queried_user.git_user_email == 'git@example.com' + + +def test_user_model_git_user_fields_nullable(session_maker): + """Test that git_user_name and git_user_email can be null.""" + with session_maker() as session: + # Arrange + org = Org(name='test_org_nullable') + session.add(org) + session.flush() + + test_user_id = uuid4() + + # Act - create user without git fields + user = User( + id=test_user_id, + current_org_id=org.id, + ) + session.add(user) + session.commit() + + # Assert + queried_user = session.query(User).filter(User.id == test_user_id).first() + assert queried_user.git_user_name is None + assert queried_user.git_user_email is None + + +def test_user_model_git_user_fields_in_table_columns(): + """Test that git_user_name and git_user_email are in User table columns.""" + # Arrange & Act + column_names = [c.name for c in User.__table__.columns] + + # Assert + assert 'git_user_name' in column_names + assert 'git_user_email' in column_names + + +def test_user_model_git_user_fields_hasattr(session_maker): + """Test that hasattr returns True for git_user_* fields on User model. + + This verifies the fix for SaasSettingsStore.store() which uses hasattr + to determine if a field should be persisted to a model. + """ + with session_maker() as session: + # Arrange + org = Org(name='test_org_hasattr') + session.add(org) + session.flush() + + user = User(id=uuid4(), current_org_id=org.id) + session.add(user) + session.flush() + + # Assert - hasattr must return True for store() to work + assert hasattr(user, 'git_user_name') + assert hasattr(user, 'git_user_email')