-
Notifications
You must be signed in to change notification settings - Fork 0
Properties (#7) #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| [run] | ||
| source = myrealestate | ||
| omit = | ||
| */migrations/* | ||
| */tests/* | ||
| */admin.py | ||
| */apps.py | ||
| */urls.py | ||
| */__init__.py | ||
| */asgi.py | ||
| */wsgi.py | ||
| manage.py | ||
| */settings.py | ||
|
|
||
|
|
||
| [report] | ||
| exclude_lines = | ||
| pragma: no cover | ||
| def __str__ | ||
| def __repr__ | ||
| raise NotImplementedError | ||
| if settings.DEBUG | ||
| pass | ||
| raise ImportError | ||
| if __name__ == .__main__.: | ||
|
|
||
| [html] | ||
| directory = coverage_html |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend | ||
| EMAIL_HOST=127.0.0.1 | ||
| EMAIL_PORT=1025 # Mailpit default SMTP port | ||
| EMAIL_USE_TLS=False | ||
| [email protected] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,2 +1,42 @@ | ||||||||||||||||||
| # MyRealEstate | ||||||||||||||||||
| A property management application | ||||||||||||||||||
|
|
||||||||||||||||||
| ## Setup and Installation | ||||||||||||||||||
|
|
||||||||||||||||||
| ### Prerequisites | ||||||||||||||||||
| - Python 3.8+ | ||||||||||||||||||
| - Node.js and npm | ||||||||||||||||||
| - Docker (for MinIO) | ||||||||||||||||||
|
|
||||||||||||||||||
| ### Initial Setup | ||||||||||||||||||
|
|
||||||||||||||||||
| 1. Create and activate virtual environment: | ||||||||||||||||||
| ``` | ||||||||||||||||||
| python | ||||||||||||||||||
| python -m venv venv | ||||||||||||||||||
| source venv/bin/activate # On Windows: venv\Scripts\activate | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| 1. Install dependencies: | ||||||||||||||||||
| ``` | ||||||||||||||||||
| pip install -r requirements.txt | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| 1. Configure environment variables: | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
Comment on lines
+25
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Consider referencing the .env.example file in the environment setup section Since there's a .env.example file in the repository, it would be helpful to instruct users to copy it to .env as part of the setup process
Suggested change
|
||||||||||||||||||
| # No variables needed for now | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| 1. Run database migrations: | ||||||||||||||||||
| ``` | ||||||||||||||||||
| python manage.py migrate | ||||||||||||||||||
| ``` | ||||||||||||||||||
| ### Tailwind CSS Setup | ||||||||||||||||||
|
|
||||||||||||||||||
| 1. Install Tailwind dependencies: | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: The command for installing Tailwind dependencies is missing Please add the specific command needed to install the Tailwind dependencies |
||||||||||||||||||
|
|
||||||||||||||||||
| 1. Run the development server: | ||||||||||||||||||
| ``` | ||||||||||||||||||
| python manage.py runserver | ||||||||||||||||||
| ``` | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Generated by Django 5.1.3 on 2024-12-07 14:47 | ||
|
|
||
| import uuid | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('accounts', '0002_usercompanyaccess_user_companies'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name='user', | ||
| name='email_verification_token', | ||
| field=models.UUIDField(default=uuid.uuid4, null=True), | ||
| ), | ||
| migrations.AddField( | ||
| model_name='user', | ||
| name='email_verified', | ||
| field=models.BooleanField(default=False), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Generated by Django 5.1.3 on 2024-12-07 14:52 | ||
|
|
||
| import uuid | ||
| from django.db import migrations | ||
|
|
||
|
|
||
| def gen_uuid(apps, schema_editor): | ||
| User = apps.get_model("accounts", "User") | ||
| for row in User.objects.all(): | ||
| row.email_verification_token = uuid.uuid4() | ||
| row.save(update_fields=["email_verification_token"]) | ||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('accounts', '0003_user_email_verification_token_user_email_verified'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.RunPython(gen_uuid, migrations.RunPython.noop), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Generated by Django 5.1.3 on 2024-12-07 14:52 | ||
|
|
||
| import uuid | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('accounts', '0004_populate_uuid_fields'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name='user', | ||
| name='email_verification_token', | ||
| field=models.UUIDField(default=uuid.uuid4, unique=True), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import factory | ||
| from django.contrib.auth import get_user_model | ||
| from ..models import UserCompanyAccess, UserTypeEnums | ||
|
|
||
| class UserFactory(factory.django.DjangoModelFactory): | ||
| class Meta: | ||
| model = get_user_model() | ||
| django_get_or_create = ('email',) | ||
|
|
||
| email = factory.Sequence(lambda n: f'user{n}@example.com') | ||
| username = factory.Sequence(lambda n: f'username{n}') | ||
| password = factory.PostGenerationMethodCall('set_password', 'testpass123') | ||
| is_active = True | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| from django.test import TestCase | ||
| from django.urls import reverse | ||
| from ..forms import CustomUserCreationForm, CustomAuthenticationForm | ||
| from ..models import User, UserTypeEnums | ||
| from .factories import UserFactory | ||
| from myrealestate.companies.tests.factories import CompanyFactory | ||
| from myrealestate.companies.models import Company | ||
|
|
||
| class TestCustomUserCreationForm(TestCase): | ||
| def setUp(self): | ||
| self.valid_data = { | ||
| 'email': '[email protected]', | ||
| 'username': 'testuser', | ||
| 'password1': 'StrongPass123!', | ||
| 'password2': 'StrongPass123!', | ||
| 'company_name': 'Test Company' | ||
| } | ||
|
|
||
| def test_form_valid_data(self): | ||
| """Test form with valid data""" | ||
| form = CustomUserCreationForm(data=self.valid_data) | ||
| self.assertTrue(form.is_valid()) | ||
|
|
||
| def test_form_creates_user_and_company(self): | ||
| """Test that form creates both user and company with correct relationship""" | ||
| form = CustomUserCreationForm(data=self.valid_data) | ||
| self.assertTrue(form.is_valid()) | ||
| user = form.save() | ||
|
|
||
| # Check user was created | ||
| self.assertIsInstance(user, User) | ||
| self.assertEqual(user.email, self.valid_data['email']) | ||
| self.assertEqual(user.username, self.valid_data['username']) | ||
|
|
||
| # Check company was created and linked | ||
| company = Company.objects.get(name=self.valid_data['company_name']) | ||
| self.assertTrue(user.companies.filter(id=company.id).exists()) | ||
|
|
||
| # Check user is company owner | ||
| user_company = user.usercompanyaccess_set.get(company=company) | ||
| self.assertEqual(user_company.access_level, UserTypeEnums.COMPANY_OWNER) | ||
|
|
||
| def test_duplicate_email(self): | ||
| """Test form validation with duplicate email""" | ||
| # Create a user first | ||
| UserFactory(email=self.valid_data['email']) | ||
|
|
||
| form = CustomUserCreationForm(data=self.valid_data) | ||
| self.assertFalse(form.is_valid()) | ||
| self.assertIn('email', form.errors) | ||
| self.assertEqual(form.errors['email'][0], 'This email is already registered.') | ||
|
|
||
| def test_duplicate_username(self): | ||
| """Test form validation with duplicate username""" | ||
| UserFactory(username=self.valid_data['username']) | ||
|
|
||
| form = CustomUserCreationForm(data=self.valid_data) | ||
| self.assertFalse(form.is_valid()) | ||
| self.assertIn('username', form.errors) | ||
| self.assertEqual(form.errors['username'][0], 'This username is already taken.') | ||
|
|
||
| def test_password_mismatch(self): | ||
| """Test form validation with mismatched passwords""" | ||
| data = self.valid_data.copy() | ||
| data['password2'] = 'DifferentPass123!' | ||
|
|
||
| form = CustomUserCreationForm(data=data) | ||
| self.assertFalse(form.is_valid()) | ||
| self.assertIn('password2', form.errors) | ||
|
|
||
|
|
||
| class TestCustomAuthenticationForm(TestCase): | ||
| def setUp(self): | ||
| self.user = UserFactory( | ||
| email='[email protected]', | ||
| username='testuser' | ||
| ) | ||
| self.user.set_password('TestPass123!') | ||
| self.user.save() | ||
|
|
||
| def test_login_with_email(self): | ||
| """Test authentication using email""" | ||
| form = CustomAuthenticationForm(data={ | ||
| 'username': '[email protected]', | ||
| 'password': 'TestPass123!' | ||
| }) | ||
| self.assertTrue(form.is_valid()) | ||
|
|
||
| def test_login_with_username(self): | ||
| """Test authentication using username""" | ||
| form = CustomAuthenticationForm(data={ | ||
| 'username': 'testuser', | ||
| 'password': 'TestPass123!' | ||
| }) | ||
| self.assertTrue(form.is_valid()) | ||
|
|
||
| def test_login_with_invalid_credentials(self): | ||
| """Test authentication with wrong password""" | ||
| form = CustomAuthenticationForm(data={ | ||
| 'username': '[email protected]', | ||
| 'password': 'WrongPass123!' | ||
| }) | ||
| self.assertFalse(form.is_valid()) | ||
| self.assertIn('__all__', form.errors) | ||
| self.assertEqual( | ||
| form.errors['__all__'][0], | ||
| 'Please enter a correct email/username and password.' | ||
| ) | ||
|
|
||
| def test_login_with_inactive_user(self): | ||
| """Test authentication with inactive user""" | ||
| self.user.is_active = False | ||
| self.user.save() | ||
|
|
||
| form = CustomAuthenticationForm(data={ | ||
| 'username': '[email protected]', | ||
| 'password': 'TestPass123!' | ||
| }) | ||
| self.assertFalse(form.is_valid()) | ||
| self.assertIn('__all__', form.errors) | ||
| self.assertEqual( | ||
| form.errors['__all__'][0], | ||
| 'This account is inactive.' | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| from django.test import TestCase | ||
| from django.core.exceptions import ValidationError | ||
| from ..models import User, UserTypeEnums | ||
| from .factories import UserFactory | ||
|
|
||
| class TestUserModel(TestCase): | ||
| def setUp(self): | ||
| """Set up data for all test methods""" | ||
| self.user = UserFactory() | ||
|
|
||
| def test_create_user(self): | ||
| """Test creating a regular user""" | ||
| self.assertIsNotNone(self.user.pk) | ||
| self.assertIsNotNone(self.user.email) | ||
| self.assertIsNotNone(self.user.username) | ||
| self.assertTrue(self.user.check_password('testpass123')) | ||
| self.assertFalse(self.user.is_staff) | ||
| self.assertFalse(self.user.is_superuser) | ||
|
|
||
| def test_create_superuser(self): | ||
| """Test creating a superuser""" | ||
| superuser = User.objects.create_superuser( | ||
| email='[email protected]', | ||
| password='adminpass123' | ||
| ) | ||
| self.assertTrue(superuser.is_staff) | ||
| self.assertTrue(superuser.is_superuser) | ||
| self.assertEqual(superuser.email, '[email protected]') | ||
|
|
||
| def test_create_user_without_email_raises_error(self): | ||
| """Test that creating a user without email raises error""" | ||
| with self.assertRaisesMessage(ValueError, 'Email is required'): | ||
| User.objects.create_user(email='', password='testpass123') | ||
|
|
||
| def test_user_str_method(self): | ||
| """Test the string representation of User model""" | ||
| user = UserFactory(email='[email protected]') | ||
| self.assertEqual(str(user), '[email protected]') | ||
|
|
||
| def test_email_is_normalized(self): | ||
| """Test email normalization""" | ||
| email = '[email protected]' | ||
| user = User.objects.create_user(email=email, password='testpass123') | ||
| self.assertEqual(user.email, '[email protected]') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (testing): Add tests for email verification token methods The User model has new email verification features but there are no tests for generate_verification_token() method or the email_verified field behavior. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Remove this superfluous line as it serves no purpose in the instructions
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate