222 lines
8.7 KiB
Python
222 lines
8.7 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
|
|
from .models import Business, BusinessMembership, Customer, Job, JobMedia, ProofCard, ReviewRequest
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class TrustForgeFlowTests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(
|
|
username='owner@example.com',
|
|
email='owner@example.com',
|
|
password='StrongPass123!',
|
|
)
|
|
self.business = Business.objects.create(
|
|
name='Forge Roofing',
|
|
slug='forge-roofing',
|
|
industry='Roofing',
|
|
primary_city='Austin',
|
|
primary_state='TX',
|
|
google_review_url='https://example.com/google-review',
|
|
)
|
|
self.membership = BusinessMembership.objects.create(
|
|
user=self.user,
|
|
business=self.business,
|
|
role=BusinessMembership.Role.OWNER,
|
|
)
|
|
self.customer = Customer.objects.create(
|
|
business=self.business,
|
|
full_name='Jordan Lee',
|
|
email='jordan@example.com',
|
|
city='Austin',
|
|
state='TX',
|
|
)
|
|
self.job = Job.objects.create(
|
|
business=self.business,
|
|
customer=self.customer,
|
|
service_type='Roof repair',
|
|
city='Austin',
|
|
state='TX',
|
|
)
|
|
JobMedia.objects.create(
|
|
job=self.job,
|
|
media_type=JobMedia.MediaType.BEFORE,
|
|
file=SimpleUploadedFile('before-sample.jpg', b'before-image-bytes', content_type='image/jpeg'),
|
|
)
|
|
JobMedia.objects.create(
|
|
job=self.job,
|
|
media_type=JobMedia.MediaType.AFTER,
|
|
file=SimpleUploadedFile('after-sample.jpg', b'after-image-bytes', content_type='image/jpeg'),
|
|
)
|
|
self.proof_card = ProofCard.objects.create(job=self.job, customer_display_name='Verified homeowner')
|
|
self.review_request = ReviewRequest.objects.create(job=self.job)
|
|
|
|
def test_home_loads(self):
|
|
response = self.client.get(reverse('home'))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'TrustForge')
|
|
|
|
def test_dashboard_requires_login(self):
|
|
response = self.client.get(reverse('dashboard'))
|
|
self.assertEqual(response.status_code, 302)
|
|
self.assertIn(reverse('login'), response.url)
|
|
|
|
def test_login_with_email_redirects_to_dashboard(self):
|
|
response = self.client.post(
|
|
reverse('login'),
|
|
{'username': 'owner@example.com', 'password': 'StrongPass123!'},
|
|
)
|
|
self.assertRedirects(response, reverse('dashboard'))
|
|
|
|
def test_logged_in_user_without_membership_redirects_to_onboarding(self):
|
|
user = User.objects.create_user(
|
|
username='solo@example.com',
|
|
email='solo@example.com',
|
|
password='StrongPass123!',
|
|
)
|
|
self.client.force_login(user)
|
|
response = self.client.get(reverse('dashboard'))
|
|
self.assertRedirects(response, reverse('business_onboarding'))
|
|
|
|
def test_dashboard_only_shows_active_business_data(self):
|
|
other_business = Business.objects.create(
|
|
name='Hidden Plumbing',
|
|
slug='hidden-plumbing',
|
|
industry='Plumbing',
|
|
primary_city='Dallas',
|
|
primary_state='TX',
|
|
)
|
|
other_customer = Customer.objects.create(
|
|
business=other_business,
|
|
full_name='Taylor Shade',
|
|
city='Dallas',
|
|
state='TX',
|
|
)
|
|
Job.objects.create(
|
|
business=other_business,
|
|
customer=other_customer,
|
|
service_type='Leak repair',
|
|
city='Dallas',
|
|
state='TX',
|
|
)
|
|
|
|
self.client.force_login(self.user)
|
|
response = self.client.get(reverse('dashboard'))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Forge Roofing proof momentum')
|
|
self.assertNotContains(response, 'Leak repair')
|
|
|
|
def test_technician_cannot_edit_proof_card(self):
|
|
technician = User.objects.create_user(
|
|
username='tech@example.com',
|
|
email='tech@example.com',
|
|
password='StrongPass123!',
|
|
)
|
|
BusinessMembership.objects.create(
|
|
user=technician,
|
|
business=self.business,
|
|
role=BusinessMembership.Role.TECHNICIAN,
|
|
)
|
|
self.client.force_login(technician)
|
|
response = self.client.get(reverse('proof_card_edit', args=[self.proof_card.id]))
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
def test_public_review_positive_feedback_publishes_proof(self):
|
|
response = self.client.post(
|
|
reverse('review_request', args=[self.review_request.token]),
|
|
{'experience': 'great', 'testimonial': 'They showed up on time and the roof looks incredible.'},
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.proof_card.refresh_from_db()
|
|
self.assertEqual(self.proof_card.status, 'published')
|
|
self.assertEqual(self.proof_card.rating, 5)
|
|
|
|
|
|
def test_public_gallery_only_shows_published_cards_for_requested_business(self):
|
|
self.proof_card.status = ProofCard.Status.PUBLISHED
|
|
self.proof_card.is_featured = True
|
|
self.proof_card.testimonial_quote = 'Published proof for Forge Roofing.'
|
|
self.proof_card.save(update_fields=['status', 'is_featured', 'testimonial_quote'])
|
|
|
|
other_business = Business.objects.create(
|
|
name='Quiet Electric',
|
|
slug='quiet-electric',
|
|
industry='Electrical',
|
|
primary_city='Denver',
|
|
primary_state='CO',
|
|
)
|
|
other_customer = Customer.objects.create(
|
|
business=other_business,
|
|
full_name='Morgan Bright',
|
|
city='Denver',
|
|
state='CO',
|
|
)
|
|
other_job = Job.objects.create(
|
|
business=other_business,
|
|
customer=other_customer,
|
|
service_type='Panel upgrade',
|
|
city='Denver',
|
|
state='CO',
|
|
)
|
|
ProofCard.objects.create(
|
|
job=other_job,
|
|
customer_display_name='Verified homeowner',
|
|
status=ProofCard.Status.PUBLISHED,
|
|
testimonial_quote='This should not appear in Forge Roofing gallery.',
|
|
)
|
|
|
|
draft_customer = Customer.objects.create(
|
|
business=self.business,
|
|
full_name='Casey Draft',
|
|
city='Austin',
|
|
state='TX',
|
|
)
|
|
draft_job = Job.objects.create(
|
|
business=self.business,
|
|
customer=draft_customer,
|
|
service_type='Draft-only repair',
|
|
city='Austin',
|
|
state='TX',
|
|
)
|
|
ProofCard.objects.create(
|
|
job=draft_job,
|
|
customer_display_name='Hidden draft',
|
|
status=ProofCard.Status.DRAFT,
|
|
testimonial_quote='Draft cards should stay private.',
|
|
)
|
|
|
|
response = self.client.get(reverse('public_proof_gallery', args=[self.business.slug]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Forge Roofing completed work')
|
|
self.assertContains(response, 'Published proof for Forge Roofing.')
|
|
self.assertContains(response, self.job.before_media.file.url)
|
|
self.assertContains(response, self.job.after_media.file.url)
|
|
self.assertNotContains(response, 'This should not appear in Forge Roofing gallery.')
|
|
self.assertNotContains(response, 'Draft cards should stay private.')
|
|
|
|
|
|
def test_workspace_proof_detail_renders_uploaded_media(self):
|
|
self.client.force_login(self.user)
|
|
response = self.client.get(reverse('proof_card_detail', args=[self.proof_card.id]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, self.job.before_media.file.url)
|
|
self.assertContains(response, self.job.after_media.file.url)
|
|
|
|
def test_public_proof_detail_requires_published_status(self):
|
|
response = self.client.get(reverse('public_proof_detail', args=[self.business.slug, self.proof_card.id]))
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
self.proof_card.status = ProofCard.Status.PUBLISHED
|
|
self.proof_card.testimonial_quote = 'Proof card is now public.'
|
|
self.proof_card.save(update_fields=['status', 'testimonial_quote'])
|
|
|
|
response = self.client.get(reverse('public_proof_detail', args=[self.business.slug, self.proof_card.id]))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, 'Proof card is now public.')
|
|
self.assertContains(response, self.job.before_media.file.url)
|
|
self.assertContains(response, self.job.after_media.file.url)
|