inital creation

This commit is contained in:
2025-07-10 10:27:00 -05:00
parent ef38109794
commit 3f3fa4b3d8
18 changed files with 1906 additions and 21 deletions

View File

@@ -0,0 +1,166 @@
# Generated by Django 5.2.4 on 2025-07-07 14:51
import core.models
import django.db.models.deletion
import django.utils.timezone
import uuid
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('email', models.EmailField(max_length=254, unique=True)),
('first_name', models.CharField(max_length=30)),
('last_name', models.CharField(max_length=30)),
('user_type', models.CharField(choices=[('property_owner', 'Property Owner'), ('vendor', 'Vendor'), ('admin', 'Admin')], max_length=20)),
('is_active', models.BooleanField(default=True)),
('is_staff', models.BooleanField(default=False)),
('date_joined', models.DateTimeField(default=django.utils.timezone.now)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Conversation',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
name='Property',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('address', models.CharField(max_length=200)),
('city', models.CharField(max_length=100)),
('state', models.CharField(max_length=2)),
('zip_code', models.CharField(max_length=10)),
('market_value', models.DecimalField(decimal_places=2, max_digits=12)),
('loan_amount', models.DecimalField(blank=True, decimal_places=2, max_digits=12, null=True)),
('loan_interest_rate', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)),
('loan_term', models.IntegerField(blank=True, null=True)),
('loan_start_date', models.DateField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='VideoCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField(blank=True, null=True)),
],
),
migrations.CreateModel(
name='PropertyOwner',
fields=[
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('phone_number', models.CharField(blank=True, max_length=20, null=True)),
],
),
migrations.CreateModel(
name='Vendor',
fields=[
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
('business_name', models.CharField(max_length=100)),
('business_type', models.CharField(choices=[('contractor', 'Contractor'), ('inspector', 'Inspector'), ('lender', 'Lender'), ('other', 'Other')], max_length=20)),
('phone_number', models.CharField(blank=True, max_length=20, null=True)),
('address', models.CharField(max_length=200)),
('city', models.CharField(max_length=100)),
('state', models.CharField(max_length=2)),
('zip_code', models.CharField(max_length=10)),
],
),
migrations.CreateModel(
name='Message',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
('attachment', models.FileField(blank=True, null=True, upload_to=core.models.message_file_path)),
('timestamp', models.DateTimeField(auto_now_add=True)),
('read', models.BooleanField(default=False)),
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='core.conversation')),
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sent_messages', to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='PasswordResetToken',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('expires_at', models.DateTimeField()),
('used', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='conversation',
name='property',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conversations', to='core.property'),
),
migrations.CreateModel(
name='Video',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True)),
('link', models.URLField()),
('duration', models.IntegerField(help_text='Duration in seconds')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='videos', to='core.videocategory')),
],
),
migrations.AddField(
model_name='property',
name='owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='properties', to='core.propertyowner'),
),
migrations.AddField(
model_name='conversation',
name='property_owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conversations', to='core.propertyowner'),
),
migrations.AddField(
model_name='conversation',
name='vendor',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='conversations', to='core.vendor'),
),
migrations.CreateModel(
name='UserVideoProgress',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('progress', models.IntegerField(default=0, help_text='Progress in seconds')),
('status', models.CharField(choices=[('not_started', 'Not Started'), ('in_progress', 'In Progress'), ('completed', 'Completed')], default='not_started', max_length=20)),
('last_watched', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='video_progress', to=settings.AUTH_USER_MODEL)),
('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_progress', to='core.video')),
],
options={
'unique_together': {('user', 'video')},
},
),
migrations.AlterUniqueTogether(
name='conversation',
unique_together={('property_owner', 'vendor', 'property')},
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.2.4 on 2025-07-09 19:42
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterUniqueTogether(
name='conversation',
unique_together=set(),
),
]

View File

@@ -151,8 +151,8 @@ class Conversation(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('property_owner', 'vendor', 'property')
# class Meta:
# unique_together = ('property_owner', 'vendor', 'property')
def __str__(self):
return f"Conversation between {self.property_owner} and {self.vendor} about {self.property}"

View File

@@ -106,25 +106,44 @@ class VendorSerializer(serializers.ModelSerializer):
class Meta:
model = Vendor
fields = ['user', 'business_name', 'business_type', 'phone_number',
'address', 'city', 'state', 'zip_code']
'address', 'city', 'state', 'zip_code']
def create(self, validated_data):
# Extract user data
user_data = validated_data.pop('user')
user = User.objects.create_user(**user_data)
# Get or create category
user, _ = User.objects.get_or_create(**user_data)
# Create video with the category
vendor = Vendor.objects.create(user=user, **validated_data)
return vendor
def update(self, instance, validated_data):
user_data = validated_data.pop('user', None)
# Update Vendor fields
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
# Update nested User fields if provided
if user_data:
user = instance.user
email = user_data.get('email', None)
# Only validate email uniqueness if it's being changed
if email and email != user.email:
if User.objects.filter(email=email).exists():
raise serializers.ValidationError({
'email': 'A user with this email already exists.'
})
for attr, value in user_data.items():
setattr(user, attr, value)
user.save()
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
class PropertySerializer(serializers.ModelSerializer):
@@ -148,24 +167,73 @@ class VideoSerializer(serializers.ModelSerializer):
model = Video
fields = ['id', 'category', 'title', 'description', 'link', 'duration', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
def create(self, validated_data):
# Extract category data
category_data = validated_data.pop('category')
# Get or create category
category, _ = VideoCategory.objects.get_or_create(**category_data)
# Create video with the category
video = Video.objects.create(category=category, **validated_data)
return video
def update(self, instance, validated_data):
# Handle category update if provided
category_data = validated_data.pop('category', None)
if category_data:
category, _ = VideoCategory.objects.get_or_create(**category_data)
instance.category = category
# Update other fields
for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
class UserVideoProgressSerializer(serializers.ModelSerializer):
video = VideoSerializer()
class Meta:
model = UserVideoProgress
fields = ['video', 'progress', 'status', 'last_watched']
fields = ['video', 'progress', 'status', 'last_watched', 'user']
read_only_fields = ['status', 'last_watched']
def create(self, validated_data):
# Extract video data
video_data = validated_data.pop('video')
# Get or create video
video_serializer = VideoSerializer(data=video_data)
video_serializer.is_valid(raise_exception=True)
video = video_serializer.save()
user = validated_data.pop('user')
# Create progress record
progress = UserVideoProgress.objects.create(
user=user,
video=video,
**validated_data
)
return progress
def update(self, instance, validated_data):
# Handle video update if provided
video_data = validated_data.pop('video', None)
if video_data:
video_serializer = VideoSerializer(instance.video, data=video_data, partial=True)
video_serializer.is_valid(raise_exception=True)
video_serializer.save()
# Update progress
instance.progress = validated_data.get('progress', instance.progress)
instance.save()
return instance
class ConversationSerializer(serializers.ModelSerializer):
property_owner = PropertyOwnerSerializer()
vendor = VendorSerializer()
property = PropertySerializer()
class Meta:
model = Conversation
@@ -173,7 +241,7 @@ class ConversationSerializer(serializers.ModelSerializer):
read_only_fields = ['id', 'created_at', 'updated_at']
class MessageSerializer(serializers.ModelSerializer):
sender = UserSerializer(read_only=True)
class Meta:
model = Message
@@ -181,8 +249,34 @@ class MessageSerializer(serializers.ModelSerializer):
read_only_fields = ['id', 'timestamp']
def create(self, validated_data):
message = Message.objects.create(**validated_data)
# Extract user data
sender = validated_data.pop('sender')
message = Message.objects.create(sender=sender, **validated_data)
return message
def update(self, instance, validated_data):
"""
Handle updates to message fields.
Note: sender and conversation are typically read-only in updates
"""
# Update text if provided
instance.text = validated_data.get('text', instance.text)
# Update read status if provided
if 'read' in validated_data:
instance.read = validated_data['read']
# Handle attachment updates (if needed)
if 'attachment' in validated_data:
# Delete old attachment if exists
if instance.attachment:
instance.attachment.delete()
instance.attachment = validated_data['attachment']
instance.save()
return instance
class PasswordResetRequestSerializer(serializers.Serializer):
email = serializers.EmailField()
@@ -210,6 +304,7 @@ class PasswordResetConfirmSerializer(serializers.Serializer):
new_password2 = serializers.CharField(write_only=True)
def validate(self, attrs):
try:
token = PasswordResetToken.objects.get(token=attrs['token'])
except PasswordResetToken.DoesNotExist:

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>Password Reset</title>
</head>
<body>
<p>Hello {{ user.first_name }},</p>
<p>You're receiving this email because you requested a password reset for your account.</p>
<p>Please click the following link to reset your password:</p>
<p><a href="{{ reset_url }}">{{ reset_url }}</a></p>
<p>If you didn't request this, please ignore this email.</p>
<p>Thanks,<br>
The Real Estate App Team</p>
</body>
</html>

View File

View File

@@ -0,0 +1,21 @@
from django.urls import path
from rest_framework.routers import DefaultRouter
from core.views import ConversationViewSet, MessageViewSet
router = DefaultRouter()
router.register(r'', ConversationViewSet, basename='conversation')
urlpatterns = [
path('<int:conversation_id>/messages/', MessageViewSet.as_view({
'get': 'list',
'post': 'create'
}), name='conversation-messages'),
path('<int:conversation_id>/messages/<int:pk>/', MessageViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
}), name='message-detail'),
]
urlpatterns += router.urls

View File

@@ -0,0 +1,8 @@
from django.urls import path
from rest_framework.routers import DefaultRouter
from core.views import PropertyViewSet
router = DefaultRouter()
router.register(r'', PropertyViewSet, basename='property')
urlpatterns = router.urls

View File

@@ -0,0 +1,8 @@
from django.urls import path
from rest_framework.routers import DefaultRouter
from core.views import PropertyOwnerViewSet
router = DefaultRouter()
router.register(r'', PropertyOwnerViewSet, basename='property-owner')
urlpatterns = router.urls

View File

@@ -0,0 +1,8 @@
from django.urls import path
from rest_framework.routers import DefaultRouter
from core.views import VendorViewSet
router = DefaultRouter()
router.register(r'', VendorViewSet, basename='vendor')
urlpatterns = router.urls

View File

@@ -0,0 +1,10 @@
from django.urls import path
from rest_framework.routers import DefaultRouter
from core.views import VideoCategoryViewSet, VideoViewSet, UserVideoProgressViewSet
router = DefaultRouter()
router.register(r'categories', VideoCategoryViewSet, basename='video-category')
router.register(r'', VideoViewSet, basename='video')
router.register(r'progress', UserVideoProgressViewSet, basename='video-progress')
urlpatterns = router.urls

View File

@@ -32,7 +32,8 @@ class UserRegisterView(generics.CreateAPIView):
permission_classes = [permissions.AllowAny]
class LogoutView(APIView):
permission_classes = [IsAuthenticated]
permission_classes = (permissions.AllowAny,)
authentication_classes = ()
def post(self, request):
try:
@@ -118,10 +119,11 @@ class VideoViewSet(viewsets.ModelViewSet):
filterset_fields = ['category']
class UserVideoProgressViewSet(viewsets.ModelViewSet):
queryset = UserVideoProgress.objects.all()
serializer_class = UserVideoProgressSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
def get_queryset(self):
return UserVideoProgress.objects.filter(user=self.request.user)
def perform_create(self, serializer):

View File

@@ -11,6 +11,10 @@ https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
from datetime import timedelta
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -25,7 +29,7 @@ SECRET_KEY = 'django-insecure-d!3*0+eki$pqv1n^)_v6&t^o@+-x2-i+8lzf5f%itsnngm3@y7
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']
# Application definition
@@ -37,6 +41,16 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party apps
'rest_framework',
'rest_framework_simplejwt',
'rest_framework_simplejwt.token_blacklist',
'channels',
'django_filters',
# Local apps
'core',
]
MIDDLEWARE = [
@@ -58,6 +72,7 @@ TEMPLATES = [
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
@@ -67,6 +82,7 @@ TEMPLATES = [
]
WSGI_APPLICATION = 'dta_service.wsgi.application'
ASGI_APPLICATION = 'config.asgi.application'
# Database
@@ -108,15 +124,89 @@ TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
FRONTEND_URL = config("FRONTEND_URL")
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/'
# Static files
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Custom user model
AUTH_USER_MODEL = 'core.User'
# REST Framework
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}
# JWT Settings
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'JWK_URL': None,
'LEEWAY': 0,
'AUTH_HEADER_TYPES': ('Bearer',),
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION',
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
# Email settings
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_HOST = config('SMTP2GO_HOST')
# EMAIL_PORT = config('SMTP2GO_PORT', cast=int)
# EMAIL_USE_TLS = True
# EMAIL_HOST_USER = config('SMTP2GO_USERNAME')
# EMAIL_HOST_PASSWORD = config('SMTP2GO_PASSWORD')
# DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL')
# CHANNEL_LAYERS = {
# 'default': {
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
# 'CONFIG': {
# "hosts": [(config('REDIS_HOST'), config('REDIS_PORT', cast=int))],
# },
# },
# }
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
TEST_DISCOVER_PATTERN = "test_*.py"

View File

@@ -21,8 +21,17 @@ from rest_framework_simplejwt.views import (
)
from core.views import (
CustomTokenObtainPairView, LogoutView,
PasswordResetRequestView, PasswordResetConfirmView
PasswordResetRequestView, PasswordResetConfirmView,
UserRegisterView
)
from rest_framework.routers import DefaultRouter
from core.views import VideoCategoryViewSet, VideoViewSet, UserVideoProgressViewSet, VendorViewSet
router = DefaultRouter()
router.register(r'categories', VideoCategoryViewSet, basename='video-category')
router.register(r'', VideoViewSet, basename='video')
router.register(r'progress', UserVideoProgressViewSet, basename='video-progress')
router.register(r'', VendorViewSet, basename='vendor')
urlpatterns = [
path('admin/', admin.site.urls),

View File

@@ -0,0 +1,4 @@
# core/tests/__init__.py
from .test_models import *
from .test_serializers import *
from .test_views import *

View File

@@ -0,0 +1,328 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from core.models import (
PropertyOwner, Vendor, Property, VideoCategory, Video,
UserVideoProgress, Conversation, Message, PasswordResetToken
)
from datetime import datetime, timedelta
from django.utils import timezone
User = get_user_model()
class UserModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='test@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
def test_user_creation(self):
self.assertEqual(self.user.email, 'test@example.com')
self.assertEqual(self.user.first_name, 'Test')
self.assertEqual(self.user.last_name, 'User')
self.assertEqual(self.user.user_type, 'property_owner')
self.assertTrue(self.user.check_password('testpass123'))
def test_user_str(self):
self.assertEqual(str(self.user), 'test@example.com')
class PropertyOwnerModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(
user=self.user,
phone_number='1234567890'
)
def test_property_owner_creation(self):
self.assertEqual(self.owner.user, self.user)
self.assertEqual(self.owner.phone_number, '1234567890')
def test_property_owner_str(self):
self.assertEqual(str(self.owner), 'Property Owner')
class VendorModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.user,
business_name='Test Vendor',
business_type='contractor',
phone_number='1234567890',
address='123 Test St',
city='Testville',
state='TS',
zip_code='12345'
)
def test_vendor_creation(self):
self.assertEqual(self.vendor.user, self.user)
self.assertEqual(self.vendor.business_name, 'Test Vendor')
self.assertEqual(self.vendor.business_type, 'contractor')
self.assertEqual(self.vendor.phone_number, '1234567890')
self.assertEqual(self.vendor.address, '123 Test St')
self.assertEqual(self.vendor.city, 'Testville')
self.assertEqual(self.vendor.state, 'TS')
self.assertEqual(self.vendor.zip_code, '12345')
def test_vendor_str(self):
self.assertEqual(str(self.vendor), 'Test Vendor')
class PropertyModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.user)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00,
loan_amount=400000.00,
loan_interest_rate=3.5,
loan_term=30,
loan_start_date='2020-01-01'
)
def test_property_creation(self):
self.assertEqual(self.property.owner, self.owner)
self.assertEqual(self.property.address, '123 Main St')
self.assertEqual(self.property.city, 'Anytown')
self.assertEqual(self.property.state, 'CA')
self.assertEqual(self.property.zip_code, '90210')
self.assertEqual(self.property.market_value, 500000.00)
self.assertEqual(self.property.loan_amount, 400000.00)
self.assertEqual(self.property.loan_interest_rate, 3.5)
self.assertEqual(self.property.loan_term, 30)
self.assertEqual(str(self.property.loan_start_date), '2020-01-01')
def test_property_str(self):
self.assertEqual(str(self.property), '123 Main St, Anytown, CA 90210')
class VideoModelTest(TestCase):
def setUp(self):
self.category = VideoCategory.objects.create(
name='Test Category',
description='Test Description'
)
self.video = Video.objects.create(
category=self.category,
title='Test Video',
description='Test Video Description',
link='https://example.com/video',
duration=300
)
def test_video_creation(self):
self.assertEqual(self.video.category, self.category)
self.assertEqual(self.video.title, 'Test Video')
self.assertEqual(self.video.description, 'Test Video Description')
self.assertEqual(self.video.link, 'https://example.com/video')
self.assertEqual(self.video.duration, 300)
def test_video_str(self):
self.assertEqual(str(self.video), 'Test Video')
class UserVideoProgressModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.category = VideoCategory.objects.create(name='Test Category')
self.video = Video.objects.create(
category=self.category,
title='Test Video',
link='https://example.com/video',
duration=300
)
self.progress = UserVideoProgress.objects.create(
user=self.user,
video=self.video,
progress=150
)
def test_progress_creation(self):
self.assertEqual(self.progress.user, self.user)
self.assertEqual(self.progress.video, self.video)
self.assertEqual(self.progress.progress, 150)
self.assertEqual(self.progress.status, 'in_progress')
def test_status_update(self):
# Test not started
self.progress.progress = 0
self.progress.save()
self.assertEqual(self.progress.status, 'not_started')
# Test completed
self.progress.progress = 300
self.progress.save()
self.assertEqual(self.progress.status, 'completed')
# Test in progress
self.progress.progress = 150
self.progress.save()
self.assertEqual(self.progress.status, 'in_progress')
def test_progress_str(self):
self.assertEqual(str(self.progress), 'user@example.com - Test Video - in_progress')
class ConversationModelTest(TestCase):
def setUp(self):
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
self.conversation = Conversation.objects.create(
property_owner=self.owner,
vendor=self.vendor,
property=self.property
)
def test_conversation_creation(self):
self.assertEqual(self.conversation.property_owner, self.owner)
self.assertEqual(self.conversation.vendor, self.vendor)
self.assertEqual(self.conversation.property, self.property)
def test_conversation_str(self):
expected_str = f"Conversation between {self.owner} and {self.vendor} about {self.property}"
self.assertEqual(str(self.conversation), expected_str)
class MessageModelTest(TestCase):
def setUp(self):
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
self.conversation = Conversation.objects.create(
property_owner=self.owner,
vendor=self.vendor,
property=self.property
)
self.message = Message.objects.create(
conversation=self.conversation,
sender=self.owner_user,
text='Test message'
)
def test_message_creation(self):
self.assertEqual(self.message.conversation, self.conversation)
self.assertEqual(self.message.sender, self.owner_user)
self.assertEqual(self.message.text, 'Test message')
self.assertFalse(self.message.read)
def test_message_str(self):
expected_str = f"Message from {self.owner_user} in {self.conversation}"
self.assertEqual(str(self.message), expected_str)
class PasswordResetTokenModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.token = PasswordResetToken.objects.create(
user=self.user,
expires_at=timezone.now() + timedelta(hours=24)
)
def test_token_creation(self):
self.assertEqual(self.token.user, self.user)
self.assertFalse(self.token.used)
self.assertTrue(self.token.is_valid())
def test_token_invalid_after_use(self):
self.token.used = True
self.token.save()
self.assertFalse(self.token.is_valid())
def test_token_invalid_after_expiry(self):
self.token.expires_at = timezone.now() - timedelta(hours=1)
self.token.save()
self.assertFalse(self.token.is_valid())
def test_token_str(self):
self.assertEqual(str(self.token), f"Password reset token for {self.user.email}")

View File

@@ -0,0 +1,511 @@
from django.test import TestCase
from django.contrib.auth import get_user_model
from rest_framework.exceptions import ValidationError
from core.serializers import (
UserRegisterSerializer, PropertyOwnerSerializer, VendorSerializer,
PropertySerializer, VideoSerializer, UserVideoProgressSerializer,
ConversationSerializer, MessageSerializer, PasswordResetRequestSerializer,
PasswordResetConfirmSerializer
)
from core.models import (
PropertyOwner, Vendor, Property, VideoCategory, Video,
UserVideoProgress, Conversation, Message, PasswordResetToken
)
import uuid
from datetime import datetime, timedelta
User = get_user_model()
class UserRegisterSerializerTest(TestCase):
def setUp(self):
self.valid_data = {
'email': 'test@example.com',
'first_name': 'Test',
'last_name': 'User',
'user_type': 'property_owner',
'password': 'testpass123',
'password2': 'testpass123'
}
def test_valid_serializer(self):
serializer = UserRegisterSerializer(data=self.valid_data)
self.assertTrue(serializer.is_valid())
user = serializer.save()
self.assertEqual(user.email, 'test@example.com')
self.assertEqual(user.first_name, 'Test')
self.assertEqual(user.last_name, 'User')
self.assertEqual(user.user_type, 'property_owner')
def test_password_mismatch(self):
invalid_data = self.valid_data.copy()
invalid_data['password2'] = 'differentpass'
serializer = UserRegisterSerializer(data=invalid_data)
with self.assertRaises(ValidationError):
serializer.is_valid(raise_exception=True)
class PropertyOwnerSerializerTest(TestCase):
def setUp(self):
self.user_data = {
'email': 'owner@example.com',
'first_name': 'Property',
'last_name': 'Owner',
'user_type': 'property_owner',
'password': 'testpass123'
}
self.owner_data = {
'user': self.user_data,
'phone_number': '1234567890'
}
def test_create_property_owner(self):
serializer = PropertyOwnerSerializer(data=self.owner_data)
self.assertTrue(serializer.is_valid())
owner = serializer.save()
self.assertEqual(owner.user.email, 'owner@example.com')
self.assertEqual(owner.phone_number, '1234567890')
def test_update_property_owner(self):
user = User.objects.create_user(**self.user_data)
owner = PropertyOwner.objects.create(user=user, phone_number='1234567890')
update_data = {
'user': {
'first_name': 'NewName',
'last_name': 'NewLast',
'email': 'newowner@example.com'
},
'phone_number': '9876543210'
}
serializer = PropertyOwnerSerializer(instance=owner, data=update_data, partial=True)
self.assertTrue(serializer.is_valid())
updated_owner = serializer.save()
self.assertEqual(updated_owner.user.first_name, 'NewName')
self.assertEqual(updated_owner.user.last_name, 'NewLast')
self.assertEqual(updated_owner.phone_number, '9876543210')
class VendorSerializerTest(TestCase):
def setUp(self):
self.user_data = {
'email': 'vendor@example.com',
'first_name': 'Vendor',
'last_name': 'User',
'user_type': 'vendor',
'password': 'testpass123'
}
self.vendor_data = {
'user': self.user_data,
'business_name': 'Test Vendor',
'business_type': 'contractor',
'phone_number': '1234567890',
'address': '123 Test St',
'city': 'Testville',
'state': 'TS',
'zip_code': '12345'
}
def test_create_vendor(self):
serializer = VendorSerializer(data=self.vendor_data)
self.assertTrue(serializer.is_valid())
vendor = serializer.save()
self.assertEqual(vendor.user.email, 'vendor@example.com')
self.assertEqual(vendor.business_name, 'Test Vendor')
self.assertEqual(vendor.business_type, 'contractor')
self.assertEqual(vendor.phone_number, '1234567890')
self.assertEqual(vendor.address, '123 Test St')
self.assertEqual(vendor.city, 'Testville')
self.assertEqual(vendor.state, 'TS')
self.assertEqual(vendor.zip_code, '12345')
def test_update_vendor(self):
user = User.objects.create_user(**self.user_data)
vendor = Vendor.objects.create(
user=user,
business_name='Test Vendor',
business_type='contractor',
phone_number='1234567890',
address='123 Test St',
city='Testville',
state='TS',
zip_code='12345'
)
update_data = {
'user': {
'first_name': 'NewVendor',
'last_name': 'NewUser',
'email': 'newvendor@example.com'
},
'business_name': 'Updated Vendor',
'phone_number': '9876543210'
}
serializer = VendorSerializer(instance=vendor, data=update_data, partial=True)
self.assertTrue(serializer.is_valid())
updated_vendor = serializer.save()
self.assertEqual(updated_vendor.user.first_name, 'NewVendor')
self.assertEqual(updated_vendor.user.last_name, 'NewUser')
self.assertEqual(updated_vendor.business_name, 'Updated Vendor')
self.assertEqual(updated_vendor.phone_number, '9876543210')
class PropertySerializerTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.user)
self.property_data = {
'owner': self.owner.pk,
'address': '123 Main St',
'city': 'Anytown',
'state': 'CA',
'zip_code': '90210',
'market_value': '500000.00',
'loan_amount': '400000.00',
'loan_interest_rate': '3.50',
'loan_term': 30,
'loan_start_date': '2020-01-01'
}
def test_create_property(self):
serializer = PropertySerializer(data=self.property_data)
self.assertTrue(serializer.is_valid())
property = serializer.save()
self.assertEqual(property.owner, self.owner)
self.assertEqual(property.address, '123 Main St')
self.assertEqual(property.city, 'Anytown')
self.assertEqual(property.state, 'CA')
self.assertEqual(property.zip_code, '90210')
self.assertEqual(str(property.market_value), '500000.00')
self.assertEqual(str(property.loan_amount), '400000.00')
self.assertEqual(str(property.loan_interest_rate), '3.50')
self.assertEqual(property.loan_term, 30)
self.assertEqual(str(property.loan_start_date), '2020-01-01')
def test_update_property(self):
property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
update_data = {
'address': '456 New St',
'city': 'Newtown',
'state': 'NY',
'zip_code': '10001',
'market_value': '600000.00'
}
serializer = PropertySerializer(instance=property, data=update_data, partial=True)
self.assertTrue(serializer.is_valid())
updated_property = serializer.save()
self.assertEqual(updated_property.address, '456 New St')
self.assertEqual(updated_property.city, 'Newtown')
self.assertEqual(updated_property.state, 'NY')
self.assertEqual(updated_property.zip_code, '10001')
self.assertEqual(str(updated_property.market_value), '600000.00')
class VideoSerializerTest(TestCase):
def setUp(self):
self.category = VideoCategory.objects.create(
name='Test Category',
description='Test Description'
)
self.video_data = {
'category': {
'id': self.category.id,
'name': self.category.name,
'description': self.category.description
},
'title': 'Test Video',
'description': 'Test Video Description',
'link': 'https://example.com/video',
'duration': 300
}
def test_video_serializer(self):
serializer = VideoSerializer(data=self.video_data)
self.assertTrue(serializer.is_valid())
video = serializer.save()
self.assertEqual(video.category, self.category)
self.assertEqual(video.title, 'Test Video')
self.assertEqual(video.description, 'Test Video Description')
self.assertEqual(video.link, 'https://example.com/video')
self.assertEqual(video.duration, 300)
class UserVideoProgressSerializerTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.category = VideoCategory.objects.create(name='Test Category')
self.video = Video.objects.create(
category=self.category,
title='Test Video',
link='https://example.com/video',
duration=300
)
self.progress_data = {
'video': {
'id': self.video.id,
'title': self.video.title,
'link': self.video.link,
'duration': self.video.duration,
'category': {
'id': self.category.id,
'name': self.category.name
}
},
'user': self.user.pk,
'progress': 150
}
def test_progress_serializer(self):
serializer = UserVideoProgressSerializer(data=self.progress_data)
self.assertTrue(serializer.is_valid())
progress = serializer.save(user=self.user)
self.assertEqual(progress.user, self.user)
self.assertEqual(progress.video.title, self.video.title)
self.assertEqual(progress.video.category.name, self.video.category.name)
self.assertEqual(progress.progress, 150)
self.assertEqual(progress.status, 'in_progress')
def test_many_progress_serializer(self):
categories = [VideoCategory.objects.create(name='Category One'),
VideoCategory.objects.create(name='Category Two')]
videos = [
Video.objects.create(
category=categories[0],
title='Test Video 1',
link='https://example.com/video1',
duration=300
),
Video.objects.create(
category=categories[0],
title='Test Video 2',
link='https://example.com/video2',
duration=300
),
Video.objects.create(
category=categories[0],
title='Test Video 3',
link='https://example.com/video3',
duration=300
),
Video.objects.create(
category=categories[1],
title='Test Video 4',
link='https://example.com/video4',
duration=300
)
]
progress_data = [
UserVideoProgress.objects.create(
video=videos[0],
user=self.user,
progress=100
),
UserVideoProgress.objects.create(
video=videos[1],
user=self.user,
progress=30
),
UserVideoProgress.objects.create(
video=videos[2],
user=self.user,
progress=0
),
UserVideoProgress.objects.create(
video=videos[3],
user=self.user,
progress=0
),
]
serializer = UserVideoProgressSerializer(UserVideoProgress.objects.filter(user=self.user), many=True)
self.assertEqual(len(serializer.data), len(progress_data))
for i in range(len(serializer.data)):
self.assertEqual(serializer.data[i]['video']['title'], progress_data[i].video.title)
self.assertEqual(serializer.data[i]['video']['description'], progress_data[i].video.description)
self.assertEqual(serializer.data[i]['video']['link'], progress_data[i].video.link)
self.assertEqual(serializer.data[i]['video']['duration'], progress_data[i].video.duration)
class ConversationSerializerTest(TestCase):
def setUp(self):
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
self.conversation_data = {
'property_owner': self.owner.pk,
'vendor': self.vendor.pk,
'property': self.property.pk
}
def test_conversation_serializer(self):
serializer = ConversationSerializer(data=self.conversation_data)
self.assertTrue(serializer.is_valid(), serializer.errors)
conversation = serializer.save()
self.assertEqual(conversation.property_owner, self.owner)
self.assertEqual(conversation.vendor, self.vendor)
self.assertEqual(conversation.property, self.property)
class MessageSerializerTest(TestCase):
def setUp(self):
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
self.conversation = Conversation.objects.create(
property_owner=self.owner,
vendor=self.vendor,
property=self.property
)
self.message_data = {
'text': 'Test message'
}
def test_message_serializer(self):
context = {'sender': self.owner_user.pk, 'conversation': self.conversation.pk, 'text': 'Test message'}
serializer = MessageSerializer(data=context)
self.assertTrue(serializer.is_valid(), serializer.errors)
message = serializer.save()
self.assertEqual(message.conversation, self.conversation)
self.assertEqual(message.sender, self.owner_user)
self.assertEqual(message.text, 'Test message')
self.assertFalse(message.read)
class PasswordResetRequestSerializerTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.valid_data = {'email': 'user@example.com'}
self.invalid_data = {'email': 'nonexistent@example.com'}
def test_valid_email(self):
serializer = PasswordResetRequestSerializer(data=self.valid_data)
self.assertTrue(serializer.is_valid())
def test_invalid_email(self):
serializer = PasswordResetRequestSerializer(data=self.invalid_data)
with self.assertRaises(ValidationError):
serializer.is_valid(raise_exception=True)
class PasswordResetConfirmSerializerTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.token = uuid.uuid4()
self.valid_data = {
'token': self.token,
'new_password': 'newpass123',
'new_password2': 'newpass123'
}
self.mismatch_data = {
'token': self.token,
'new_password': 'newpass123',
'new_password2': 'differentpass'
}
def test_valid_password_reset(self):
PasswordResetToken.objects.create(
user=self.user,
token=self.token,
expires_at=datetime.now() + timedelta(hours=24)
)
serializer = PasswordResetConfirmSerializer(data=self.valid_data)
self.assertTrue(serializer.is_valid())
def test_password_mismatch(self):
serializer = PasswordResetConfirmSerializer(data=self.mismatch_data)
with self.assertRaises(ValidationError):
serializer.is_valid(raise_exception=True)

View File

@@ -0,0 +1,588 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status
from django.contrib.auth import get_user_model
from core.models import (
PropertyOwner, Vendor, Property, VideoCategory, Video,
UserVideoProgress, Conversation, Message, PasswordResetToken
)
from datetime import datetime, timedelta
from django.utils import timezone
User = get_user_model()
class AuthenticationTests(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='test@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.login_url = reverse('token_obtain_pair')
self.refresh_url = reverse('token_refresh')
self.logout_url = reverse('logout')
self.register_url = reverse('register')
self.password_reset_url = reverse('password_reset')
self.password_reset_confirm_url = reverse('password_reset_confirm')
def test_user_login(self):
data = {
'email': 'test@example.com',
'password': 'testpass123'
}
response = self.client.post(self.login_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('access', response.data)
self.assertIn('refresh', response.data)
self.assertIn('user', response.data)
def test_user_login_invalid_credentials(self):
data = {
'email': 'test@example.com',
'password': 'wrongpassword'
}
response = self.client.post(self.login_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_token_refresh(self):
# First login to get refresh token
login_data = {
'email': 'test@example.com',
'password': 'testpass123'
}
login_response = self.client.post(self.login_url, login_data, format='json')
refresh_token = login_response.data['refresh']
# Now refresh the token
refresh_data = {
'refresh': refresh_token
}
response = self.client.post(self.refresh_url, refresh_data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('access', response.data)
def test_user_logout(self):
# First login to get tokens
login_data = {
'email': 'test@example.com',
'password': 'testpass123'
}
login_response = self.client.post(self.login_url, login_data, format='json')
self.assertEqual(login_response.status_code, status.HTTP_200_OK, login_response.text)
refresh_token = login_response.data['refresh']
# Now logout
logout_data = {
'refresh_token': refresh_token
}
response = self.client.post(self.logout_url, logout_data, format='json')
self.assertEqual(response.status_code, status.HTTP_205_RESET_CONTENT, response.text)
def test_user_registration(self):
data = {
'email': 'newuser@example.com',
'first_name': 'New',
'last_name': 'User',
'user_type': 'vendor',
'password': 'newpass123',
'password2': 'newpass123'
}
response = self.client.post(self.register_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertTrue(User.objects.filter(email='newuser@example.com').exists())
def test_password_reset_request(self):
data = {
'email': 'test@example.com'
}
response = self.client.post(self.password_reset_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(PasswordResetToken.objects.filter(user=self.user).exists())
def test_password_reset_confirm(self):
# First create a reset token
expires_at = timezone.now() + timedelta(hours=24)
token = PasswordResetToken.objects.create(
user=self.user,
expires_at=expires_at
)
data = {
'token': str(token.token),
'new_password': 'newpass123',
'new_password2': 'newpass123'
}
response = self.client.post(self.password_reset_confirm_url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
# Verify password was changed
self.user.refresh_from_db()
self.assertTrue(self.user.check_password('newpass123'))
# Verify token was marked as used
token.refresh_from_db()
self.assertTrue(token.used)
class PropertyOwnerViewTests(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.user, phone_number='1234567890')
self.other_user = User.objects.create_user(
email='other@example.com',
first_name='Other',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.other_owner = PropertyOwner.objects.create(user=self.other_user)
self.url = reverse('property-owner-list')
# Authenticate
self.client.force_authenticate(user=self.user)
def test_get_property_owners(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 2) # Both owners should be visible
def test_create_property_owner(self):
data = {
'user': {
'email': 'newowner@example.com',
'first_name': 'New',
'last_name': 'Owner',
'user_type': 'property_owner',
'password': 'testpass123'
},
'phone_number': '9876543210'
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text)
self.assertTrue(PropertyOwner.objects.filter(user__email='newowner@example.com').exists())
def test_update_property_owner(self):
url = f"{self.url}{self.owner.pk}/"
data = {
'user': {
'first_name': 'Updated',
'last_name': 'Owner',
'email': 'new_email@email.com',
'user_type': 'property_owner'
},
'phone_number': '9999999999'
}
response = self.client.put(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK, response.text)
self.owner.refresh_from_db()
self.assertEqual(self.owner.user.first_name, 'Updated')
self.assertEqual(self.owner.phone_number, '9999999999')
class VendorViewTests(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.user,
business_name='Test Vendor',
business_type='contractor',
phone_number='1234567890',
address='123 Test St',
city='Testville',
state='TS',
zip_code='12345'
)
self.url = reverse('vendor-list')
# Authenticate
self.client.force_authenticate(user=self.user)
def test_get_vendors(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_vendor(self):
data = {
'user': {
'email': 'newvendor@example.com',
'first_name': 'New',
'last_name': 'Vendor',
'user_type': 'vendor',
'password': 'testpass123'
},
'business_name': 'New Vendor',
'business_type': 'inspector',
'phone_number': '9876543210',
'address': '456 New St',
'city': 'Newtown',
'state': 'NS',
'zip_code': '54321'
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertTrue(Vendor.objects.filter(business_name='New Vendor').exists())
def test_update_vendor(self):
url = f"{self.url}{self.vendor.pk}/"
data = {
'user': {
'first_name': 'Updated',
'last_name': 'Vendor',
},
'business_name': 'Updated Vendor',
'phone_number': '9999999999'
}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK, response.text)
self.vendor.refresh_from_db()
self.assertEqual(self.vendor.user.first_name, 'Updated')
self.assertEqual(self.vendor.business_name, 'Updated Vendor')
self.assertEqual(self.vendor.phone_number, '9999999999')
class PropertyViewTests(TestCase):
def setUp(self):
self.client = APIClient()
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
self.url = reverse('property-list')
# Authenticate as property owner
self.client.force_authenticate(user=self.owner_user)
def test_get_properties(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_property(self):
data = {
'owner': self.owner.pk,
'address': '456 New St',
'city': 'Newtown',
'state': 'NY',
'zip_code': '10001',
'market_value': '600000.00'
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED,response.text)
self.assertTrue(Property.objects.filter(address='456 New St').exists())
def test_update_property(self):
url = f"{self.url}{self.property.pk}/"
data = {
'address': '789 Updated St',
'market_value': '550000.00'
}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.property.refresh_from_db()
self.assertEqual(self.property.address, '789 Updated St')
self.assertEqual(str(self.property.market_value), '550000.00')
class VideoViewTests(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.category = VideoCategory.objects.create(
name='Test Category',
description='Test Description'
)
self.video = Video.objects.create(
category=self.category,
title='Test Video',
description='Test Video Description',
link='https://example.com/video',
duration=300
)
self.url = reverse('video-list')
# Authenticate
self.client.force_authenticate(user=self.user)
def test_get_videos(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_video(self):
data = {
'category': {
"name": "New Category",
"description": "a description"
},
'title': 'New Video',
'description': 'New Video Description',
'link': 'https://example.com/new-video',
'duration': 240
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text)
self.assertTrue(Video.objects.filter(title='New Video').exists())
def test_update_video(self):
url = f"{self.url}{self.video.pk}/"
data = {
'title': 'Updated Video',
'duration': 360
}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK, response.text)
self.video.refresh_from_db()
self.assertEqual(self.video.title, 'Updated Video')
self.assertEqual(self.video.duration, 360)
class UserVideoProgressViewTests(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
email='user@example.com',
first_name='Test',
last_name='User',
user_type='property_owner',
password='testpass123'
)
self.category = VideoCategory.objects.create(name='Test Category')
self.video = Video.objects.create(
category=self.category,
title='Test Video',
link='https://example.com/video',
duration=300
)
self.video_1 = Video.objects.create(
category=self.category,
title='Test Video 1',
link='https://example.com/video_1',
duration=300
)
self.video_2 = Video.objects.create(
category=self.category,
title='Test Video 2',
link='https://example.com/video_2',
duration=300
)
self.progress = UserVideoProgress.objects.create(
user=self.user,
video=self.video,
progress=150
)
UserVideoProgress.objects.create(
user=self.user,
video=self.video_1,
progress=150
)
UserVideoProgress.objects.create(
user=self.user,
video=self.video_2,
progress=150
)
self.url = reverse('video-progress-list')
# Authenticate
self.client.force_authenticate(user=self.user)
def test_get_video_progress(self):
response = self.client.get(f"{self.url}", kwargs={'format','json'})
breakpoint()
self.assertEqual(response.status_code, status.HTTP_200_OK, response.text)
breakpoint()
self.assertEqual(len(response.data), 1)
def test_update_video_progress(self):
url = f"{self.url}{self.progress.pk}/"
data = {
'progress': 200
}
response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.progress.refresh_from_db()
self.assertEqual(self.progress.progress, 200)
self.assertEqual(self.progress.status, 'in_progress')
class ConversationViewTests(TestCase):
def setUp(self):
self.client = APIClient()
# Create owner user
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
# Create vendor user
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
# Create property
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
# Create conversation
self.conversation = Conversation.objects.create(
property_owner=self.owner,
vendor=self.vendor,
property=self.property
)
self.url = reverse('conversation-list')
# Authenticate as owner
self.client.force_authenticate(user=self.owner_user)
def test_get_conversations_as_owner(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_conversation(self):
data = {
'property_owner': self.owner.pk,
'vendor': self.vendor.pk,
'property': self.property.pk
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text)
self.assertTrue(Conversation.objects.filter(property_owner=self.owner, vendor=self.vendor, property=self.property).exists())
def test_get_conversations_as_vendor(self):
# Switch to vendor user
self.client.force_authenticate(user=self.vendor_user)
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
class MessageViewTests(TestCase):
def setUp(self):
self.client = APIClient()
# Create owner user
self.owner_user = User.objects.create_user(
email='owner@example.com',
first_name='Property',
last_name='Owner',
user_type='property_owner',
password='testpass123'
)
self.owner = PropertyOwner.objects.create(user=self.owner_user)
# Create vendor user
self.vendor_user = User.objects.create_user(
email='vendor@example.com',
first_name='Vendor',
last_name='User',
user_type='vendor',
password='testpass123'
)
self.vendor = Vendor.objects.create(
user=self.vendor_user,
business_name='Test Vendor',
business_type='contractor'
)
# Create property
self.property = Property.objects.create(
owner=self.owner,
address='123 Main St',
city='Anytown',
state='CA',
zip_code='90210',
market_value=500000.00
)
# Create conversation
self.conversation = Conversation.objects.create(
property_owner=self.owner,
vendor=self.vendor,
property=self.property
)
# Create message
self.message = Message.objects.create(
conversation=self.conversation,
sender=self.owner_user,
text='Test message'
)
self.url = reverse('conversation-messages', kwargs={'conversation_id': self.conversation.pk})
# Authenticate as owner
self.client.force_authenticate(user=self.owner_user)
def test_get_messages(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
def test_create_message(self):
data = {
'sender': self.owner.pk,
'text': 'New message',
'conversation': self.conversation.pk
}
response = self.client.post(self.url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.text)
self.assertTrue(Message.objects.filter(text='New message').exists())
def test_get_messages_as_vendor(self):
# Switch to vendor user
self.client.force_authenticate(user=self.vendor_user)
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)