Repository: USEPA/haztrak (python)

Issue:
Finalize Handler model to conform to USEPA/e-Manifest Site schema
# 🐞 Bug Report

This is a high level issue. 

We need to update the handler model so that is conforms to the [site schema](https://github.com/USEPA/e-manifest/blob/master/Services-Information/Schema/site.json) in the [USEPA/e-Manifest](https://github.com/USEPA/e-manifest) repo.

This will require a couple new models and accompanying serializers. I'll continue to add to this list as the problem domain continues to be better defined, but for now:
- [ ] contact information model and serializer
- [ ] electronic signature info model and serializer

also a couple fields
- [x] #170
    * [enum](https://github.com/USEPA/e-manifest/blob/5cc2e62794f469b25bd2e8e224628fe95a064c4c/Services-Information/Schema/site.json#L83)
<!--
Please provide a clear and concise description, steps to reproduce, and any other information you believe may be useful.
-->


Pull request:
169 contact and Phone modal
## Description
This PR finalizes the Handler serialization and deserialization process by adding a new Contact model and subsequent Phone model... sort of.

What needs to happen after this is a reorg of the serializer classes hierarchy. We need to eliminate redundant methods via inheritance. 


## Issue ticket number and link
<!-- Bonus points for using GitHub's keywords (e.g., closes #123) -->
closes #169 



## Checklist
<!-- We're so happy your contributing, before we do, there are some things we would like to know before merging your fabulous changes. -->

- [x] I have added tests that prove my fix is effective or that my feature works
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings



## Other Stuff



Patch:
diff --git a/client/src/services/HtApi.ts b/client/src/services/HtApi.ts
index 6a2897e6..939f4884 100644
--- a/client/src/services/HtApi.ts
+++ b/client/src/services/HtApi.ts
@@ -23,6 +23,7 @@ htApi.interceptors.request.use(
     config.headers = config.headers ?? {};
     const token = store.getState().user.token;
     if (token) {
+      // @ts-ignore
       config.headers['Authorization'] = `Bearer ${token}`;
     }
     return config;
diff --git a/server/apps/trak/admin.py b/server/apps/trak/admin.py
index 7d7a2876..798e5bb8 100644
--- a/server/apps/trak/admin.py
+++ b/server/apps/trak/admin.py
@@ -1,7 +1,7 @@
 from django.contrib import admin
 
-from .models import Address, Handler, Manifest, Site, Transporter, WasteLine, \
-    RcraProfile
+from .models import (Address, Contact, EpaPhone, Handler, Manifest,
+                     RcraProfile, Site, Transporter, WasteLine)
 
 # Register models here.
 admin.site.register(Manifest)
@@ -11,3 +11,5 @@ admin.site.register(WasteLine)
 admin.site.register(Transporter)
 admin.site.register(Address)
 admin.site.register(RcraProfile)
+admin.site.register(EpaPhone)
+admin.site.register(Contact)
diff --git a/server/apps/trak/migrations/0003_epaphone_and_contact.py b/server/apps/trak/migrations/0003_epaphone_and_contact.py
new file mode 100644
index 00000000..dfad4cfd
--- /dev/null
+++ b/server/apps/trak/migrations/0003_epaphone_and_contact.py
@@ -0,0 +1,43 @@
+# Generated by Django 4.1.4 on 2023-01-03 21:39
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+import apps.trak.models.contact
+
+
+class Migration(migrations.Migration):
+
+    replaces = [('trak', '0003_epaphone_contact'), ('trak', '0004_alter_handler_contact')]
+
+    dependencies = [
+        ('trak', '0002_rename_rcra_user_name_rcraprofile_rcra_username_and_more'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='EpaPhone',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('number', apps.trak.models.contact.EpaPhoneNumber(max_length=12)),
+                ('extension', models.CharField(blank=True, max_length=6, null=True)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Contact',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('first_name', models.CharField(blank=True, max_length=38, null=True)),
+                ('middle_initial', models.CharField(blank=True, max_length=1, null=True)),
+                ('last_name', models.CharField(blank=True, max_length=38, null=True)),
+                ('email', models.EmailField(max_length=254)),
+                ('company_name', models.CharField(blank=True, max_length=80, null=True)),
+                ('phone', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='trak.epaphone')),
+            ],
+        ),
+        migrations.AlterField(
+            model_name='handler',
+            name='contact',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='trak.contact', verbose_name='Contact Information'),
+        ),
+    ]
diff --git a/server/apps/trak/models/__init__.py b/server/apps/trak/models/__init__.py
index 9d90f220..e3032a1d 100644
--- a/server/apps/trak/models/__init__.py
+++ b/server/apps/trak/models/__init__.py
@@ -1,4 +1,5 @@
 from .address import Address
+from .contact import Contact, EpaPhone
 from .handler import Handler
 from .manifest import Manifest
 from .rcra_profile import RcraProfile
diff --git a/server/apps/trak/models/contact.py b/server/apps/trak/models/contact.py
new file mode 100644
index 00000000..672180a5
--- /dev/null
+++ b/server/apps/trak/models/contact.py
@@ -0,0 +1,74 @@
+import re
+
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+
+class EpaPhoneNumber(models.CharField):
+
+    def validate(self, value, model_instance):
+        if not re.match(r'^\d{3}-\d{3}-\d{4}$', value):
+            raise ValidationError(
+                _('%(value)s should be a phone with format ###-###-####'),
+                params={'value': value},
+            )
+
+
+class EpaPhone(models.Model):
+    number = EpaPhoneNumber(
+        max_length=12,
+    )
+    extension = models.CharField(
+        max_length=6,
+        null=True,
+        blank=True
+    )
+
+    def __str__(self):
+        if self.extension:
+            return f'{self.number} Ext. {self.extension}'
+        else:
+            return f'{self.number}'
+
+
+class Contact(models.Model):
+    first_name = models.CharField(
+        max_length=38,
+        null=True,
+        blank=True
+    )
+    middle_initial = models.CharField(
+        max_length=1,
+        null=True,
+        blank=True
+    )
+    last_name = models.CharField(
+        max_length=38,
+        null=True,
+        blank=True
+    )
+    phone = models.ForeignKey(
+        EpaPhone,
+        on_delete=models.CASCADE,
+        null=True,
+        blank=True,
+    )
+    email = models.EmailField()
+    company_name = models.CharField(
+        max_length=80,
+        null=True,
+        blank=True
+    )
+
+    def __str__(self):
+        try:
+            first = self.first_name if self.first_name else ''
+            middle = self.middle_initial if self.middle_initial else ''
+            last = self.last_name if self.last_name else ''
+            return f'contact {self.pk}: {first.capitalize()} {middle.capitalize()} {last.capitalize()}'
+        except AttributeError:
+            return f'contact {self.pk}: {self.first_name} {self.middle_initial} {self.last_name}'
+
+    def __repr__(self):
+        return f'Contact {self.pk}'
diff --git a/server/apps/trak/models/handler.py b/server/apps/trak/models/handler.py
index 7dd13b4b..3d598e72 100644
--- a/server/apps/trak/models/handler.py
+++ b/server/apps/trak/models/handler.py
@@ -1,5 +1,7 @@
 from django.db import models
 
+from apps.trak.models import Contact
+
 
 class Handler(models.Model):
     site_type = models.CharField(max_length=20,
@@ -35,8 +37,13 @@ class Handler(models.Model):
         null=True,
         blank=True,
     )
-    contact = models.JSONField(
-        verbose_name='Contact information')
+    contact = models.ForeignKey(
+        Contact,
+        on_delete=models.CASCADE,
+        verbose_name='Contact Information',
+    )
+    # contact = models.JSONField(
+    #     verbose_name='Contact information')
     emergency_phone = models.JSONField(
         null=True,
         blank=True,
diff --git a/server/apps/trak/serializers/contact.py b/server/apps/trak/serializers/contact.py
new file mode 100644
index 00000000..8a164d44
--- /dev/null
+++ b/server/apps/trak/serializers/contact.py
@@ -0,0 +1,54 @@
+from rest_framework import serializers
+
+from apps.trak.models import Contact
+from apps.trak.serializers.trak import TrakSerializer
+
+
+class EpaPhoneSerializer(TrakSerializer):
+    number = serializers.CharField()
+    extension = serializers.CharField(
+        required=False,
+    )
+
+    class Meta:
+        model = Contact
+        fields = [
+            'number',
+            'extension',
+        ]
+
+
+class ContactSerializer(TrakSerializer):
+    firstName = serializers.CharField(
+        source='first_name',
+        required=False,
+    )
+    middleInitial = serializers.CharField(
+        source='middle_initial',
+        required=False,
+    )
+    lastName = serializers.CharField(
+        source='last_name',
+        required=False,
+    )
+    phone = EpaPhoneSerializer(
+        required=False,
+    )
+    email = serializers.CharField(
+        required=False,
+    )
+    companyName = serializers.CharField(
+        source='company_name',
+        required=False,
+    )
+
+    class Meta:
+        model = Contact
+        fields = [
+            'firstName',
+            'middleInitial',
+            'lastName',
+            'phone',
+            'email',
+            'companyName',
+        ]
diff --git a/server/apps/trak/serializers/handler.py b/server/apps/trak/serializers/handler.py
index 858e7f2b..9ad62e99 100644
--- a/server/apps/trak/serializers/handler.py
+++ b/server/apps/trak/serializers/handler.py
@@ -1,8 +1,9 @@
 from rest_framework import serializers
 
-from apps.trak.models import Handler
+from apps.trak.models import Contact, EpaPhone, Handler
 from apps.trak.serializers import AddressSerializer
 
+from .contact import ContactSerializer
 from .trak import TrakSerializer
 
 
@@ -24,7 +25,7 @@ class HandlerSerializer(TrakSerializer):
     siteAddress = AddressSerializer(
         source='site_address',
     )
-    # contact
+    contact = ContactSerializer()
     emergencyPhone = serializers.JSONField(
         source='emergency_phone',
         allow_null=True,
@@ -62,7 +63,19 @@ class HandlerSerializer(TrakSerializer):
     )
 
     def create(self, validated_data):
-        new_handler = self.create_handler(**validated_data)
+        handler_contact = validated_data.pop('contact')
+        print(handler_contact)
+        if 'phone' in handler_contact:
+            phone_data = handler_contact.pop('phone')
+            new_phone = EpaPhone.objects.create(**phone_data)
+            new_contact = Contact.objects.create(**handler_contact, phone=new_phone)
+        else:
+            new_contact = Contact.objects.create(**handler_contact)
+        handler_dict = self.pop_addresses(**validated_data)
+        new_handler = Handler.objects.create(site_address=handler_dict['site_address'],
+                                             mail_address=handler_dict['mail_address'],
+                                             **handler_dict['handler_data'],
+                                             contact=new_contact)
         return new_handler
 
     class Meta:
diff --git a/server/apps/trak/serializers/trak.py b/server/apps/trak/serializers/trak.py
index 8229bca5..4deb0011 100644
--- a/server/apps/trak/serializers/trak.py
+++ b/server/apps/trak/serializers/trak.py
@@ -1,6 +1,7 @@
 from rest_framework import serializers
 
-from apps.trak.models import Address, Handler, Manifest, Transporter
+from apps.trak.models import (Address, Contact, EpaPhone, Handler, Manifest,
+                              Transporter)
 
 
 class TrakSerializer(serializers.ModelSerializer):
@@ -17,21 +18,38 @@ class TrakSerializer(serializers.ModelSerializer):
         return data
 
     def create_handler(self, **handler_data) -> Handler:
+        handler_contact = handler_data.pop('contact')
+        if 'phone' in handler_contact:
+            phone_data = handler_contact.pop('phone')
+            new_phone = EpaPhone.objects.create(**phone_data)
+            new_contact = Contact.objects.create(**handler_contact, phone=new_phone)
+        else:
+            new_contact = Contact.objects.create(**handler_contact)
         handler_dict = self.pop_addresses(**handler_data)
         new_handler = Handler.objects.create(site_address=handler_dict['site_address'],
                                              mail_address=handler_dict['mail_address'],
-                                             **handler_dict['handler_data'])
+                                             **handler_dict['handler_data'],
+                                             contact=new_contact)
         return new_handler
 
     def create_transporter(self, manifest: Manifest,
                            **transporter_data: dict) -> Transporter:
+        """ToDo(!!!) remove redundancies like this"""
+        handler_contact = transporter_data.pop('contact')
+        if 'phone' in handler_contact:
+            phone_data = handler_contact.pop('phone')
+            new_phone = EpaPhone.objects.create(**phone_data)
+            new_contact = Contact.objects.create(**handler_contact, phone=new_phone)
+        else:
+            new_contact = Contact.objects.create(**handler_contact)
         handler_parsed = self.pop_addresses(**transporter_data)
         new_transporter = Transporter.objects.create(manifest=manifest,
                                                      site_address=handler_parsed[
                                                          'site_address'],
                                                      mail_address=handler_parsed[
                                                          'mail_address'],
-                                                     **handler_parsed['handler_data'])
+                                                     **handler_parsed['handler_data'],
+                                                     contact=new_contact)
         return new_transporter
 
     @staticmethod
diff --git a/server/apps/trak/tasks/profile.py b/server/apps/trak/tasks/profile.py
index 3b51ab52..23819b02 100644
--- a/server/apps/trak/tasks/profile.py
+++ b/server/apps/trak/tasks/profile.py
@@ -7,7 +7,7 @@ from django.contrib.auth.models import User
 from emanifest import client as em
 from requests import RequestException
 
-from apps.trak.models import RcraProfile, Site, Handler
+from apps.trak.models import Handler, RcraProfile, Site
 from apps.trak.serializers import HandlerSerializer
 
 
@@ -22,14 +22,14 @@ def sync_user_sites(username: str) -> None:
     rcrainfo_env = os.getenv('HT_RCRAINFO_ENV', 'preprod')
     try:
         profile = RcraProfile.objects.get(user__username=username)
-        if not profile.rcra_user_name or not profile.rcra_api_id or not profile.rcra_api_key:
+        if not profile.rcra_username or not profile.rcra_api_id or not profile.rcra_api_key:
             raise Exception('missing attribute(s) from RcraProfile')
     except RcraProfile.DoesNotExist:
         RcraProfile.objects.create(user=User.objects.get(username=username))
         raise Exception('RcraProfile is non existent... creating. Needs attributes')
     rcra_client = em.new_client(rcrainfo_env)
     rcra_client.Auth(profile.rcra_api_id, profile.rcra_api_key)
-    data = request_user_date(profile.rcra_user_name, rcra_client.token)
+    data = request_user_date(profile.rcra_username, rcra_client.token)
     sites_ids = parse_site_ids(data)
     handlers = []
     sites = []


Test patch:
diff --git a/server/apps/trak/tests/conftest.py b/server/apps/trak/tests/conftest.py
index 6ec4b11e..1ce871e4 100644
--- a/server/apps/trak/tests/conftest.py
+++ b/server/apps/trak/tests/conftest.py
@@ -1,13 +1,20 @@
 import json
+import os
 from datetime import date, datetime
 
 import pytest
 from django.contrib.auth.models import User
 from rest_framework.test import APIClient
 
-from apps.trak.models import Address, Handler, Manifest, RcraProfile, Site
+from apps.trak.models import (Address, Contact, EpaPhone, Handler, Manifest,
+                              RcraProfile, Site)
 from apps.trak.serializers import WasteLineSerializer
-from apps.trak.tests.serializers.test_serializers import TEST_WASTE1_JSON
+from apps.trak.serializers.contact import ContactSerializer, EpaPhoneSerializer
+
+JSON_DIR = os.path.dirname(os.path.abspath(__file__)) + '/serializers/json'
+TEST_CONTACT_JSON = f'{JSON_DIR}/contact/good_contact.json'
+TEST_PHONE_JSON = f'{JSON_DIR}/contact/phone.json'
+TEST_WASTE1_JSON = f'{JSON_DIR}/test_wasteline1.json'
 
 
 @pytest.fixture
@@ -32,10 +39,28 @@ def address_123_main(db) -> Address:
 
 
 @pytest.fixture
-def generator001(db, address_123_main) -> Handler:
+def epa_phone(db) -> EpaPhone:
+    return EpaPhone.objects.create(number='123-123-1234', extension='123')
+
+
+@pytest.fixture
+def handler_contact(db, epa_phone) -> Contact:
+    return Contact.objects.create(first_name='test', middle_initial='M', last_name='User', email='testuser@haztrak.net',
+                                  phone=epa_phone)
+
+
+@pytest.fixture
+def generator001(db, address_123_main, handler_contact) -> Handler:
     return Handler.objects.create(epa_id='handler001', name='my_handler',
                                   site_type='Generator', site_address=address_123_main,
-                                  mail_address=address_123_main, contact="{}")
+                                  mail_address=address_123_main, contact=handler_contact)
+
+
+@pytest.fixture
+def tsd001(db, address_123_main, handler_contact) -> Handler:
+    return Handler.objects.create(epa_id='tsd001', name='my_tsd',
+                                  site_type='Tsd', site_address=address_123_main,
+                                  mail_address=address_123_main, contact=handler_contact)
 
 
 @pytest.fixture
@@ -48,13 +73,6 @@ def site_tsd001(db, tsd001) -> Site:
     return Site.objects.create(epa_site=tsd001, name=tsd001.name)
 
 
-@pytest.fixture
-def tsd001(db, address_123_main) -> Handler:
-    return Handler.objects.create(epa_id='tsd001', name='my_tsd',
-                                  site_type='Tsd', site_address=address_123_main,
-                                  mail_address=address_123_main, contact="{}")
-
-
 @pytest.fixture
 def waste_serializer(db) -> WasteLineSerializer:
     with open(TEST_WASTE1_JSON, 'r') as f:
@@ -62,6 +80,20 @@ def waste_serializer(db) -> WasteLineSerializer:
     return WasteLineSerializer(data=data)
 
 
+@pytest.fixture
+def contact_serializer(db) -> ContactSerializer:
+    with open(TEST_CONTACT_JSON, 'r') as f:
+        data = json.load(f)
+    return ContactSerializer(data=data)
+
+
+@pytest.fixture
+def phone_serializer(db) -> EpaPhoneSerializer:
+    with open(TEST_PHONE_JSON, 'r') as f:
+        data = json.load(f)
+    return EpaPhoneSerializer(data=data)
+
+
 @pytest.fixture
 def manifest_elc(db, address_123_main, generator001, tsd001) -> Manifest:
     return Manifest.objects.create(mtn='0123456789ELC',
diff --git a/server/apps/trak/tests/serializers/json/contact/good_contact.json b/server/apps/trak/tests/serializers/json/contact/good_contact.json
new file mode 100644
index 00000000..cd7f91fc
--- /dev/null
+++ b/server/apps/trak/tests/serializers/json/contact/good_contact.json
@@ -0,0 +1,10 @@
+{
+  "firstName": "Tester",
+  "middleInitial": "M",
+  "lastName": "User",
+  "phone": {
+    "number": "703-308-0023"
+  },
+  "email": "Testing@EPA.GOV",
+  "companyName": "Waste Inc."
+}
diff --git a/server/apps/trak/tests/serializers/json/contact/phone.json b/server/apps/trak/tests/serializers/json/contact/phone.json
new file mode 100644
index 00000000..68b567a0
--- /dev/null
+++ b/server/apps/trak/tests/serializers/json/contact/phone.json
@@ -0,0 +1,4 @@
+{
+  "number": "703-308-0023",
+  "extension": "12345"
+}
diff --git a/server/apps/trak/tests/serializers/test_contact_serializer.py b/server/apps/trak/tests/serializers/test_contact_serializer.py
new file mode 100644
index 00000000..5a8f5d47
--- /dev/null
+++ b/server/apps/trak/tests/serializers/test_contact_serializer.py
@@ -0,0 +1,11 @@
+class TestContactSerializer:
+    def test_contact_serializes(self, contact_serializer) -> None:
+        contact_serializer.is_valid()
+        # print('\n', json.dumps(contact_serializer.data, indent=2))
+        assert contact_serializer.is_valid() is True
+
+
+class TestEpaPhoneSerializer:
+    def test_phone_serializes(self, phone_serializer) -> None:
+        phone_serializer.is_valid()
+        assert phone_serializer.is_valid() is True
diff --git a/server/apps/trak/tests/serializers/test_serializers.py b/server/apps/trak/tests/serializers/test_serializers.py
index 260f6615..e3800ff2 100644
--- a/server/apps/trak/tests/serializers/test_serializers.py
+++ b/server/apps/trak/tests/serializers/test_serializers.py
@@ -15,7 +15,6 @@ JSON_DIR = os.path.dirname(os.path.abspath(__file__)) + '/json'
 TEST_MANIFEST_JSON = f'{JSON_DIR}/test_manifest.json'
 TEST_HANDLER_JSON = f'{JSON_DIR}/test_site.json'
 TEST_ADDRESS_JSON = f'{JSON_DIR}/test_address.json'
-TEST_WASTE1_JSON = f'{JSON_DIR}/test_wasteline1.json'
 
 
 class SerializerBaseTests(TestCase):
@@ -92,19 +91,6 @@ class HandlerSerializerTests(SerializerBaseTests):
         self.assertIsInstance(saved_site, Handler)
 
 
-class TestWasteLineSerializer:
-    def test_waste_line_json_deserializes(self, waste_serializer) -> None:
-        assert waste_serializer.is_valid() is True
-
-    def test_deserialized_waste_line_saves(self, waste_serializer) -> None:
-        # waste_serializer.is_valid()
-        # saved_waste_line = waste_serializer.save()
-        # assert type(saved_waste_line) is WasteLine
-        # ToDo: we need a manifest pytest.fuxture that we can assign to
-        #  manifest_id field on the wasteline
-        assert True
-
-
 def bytes_from_json(json_file: str) -> bytes:
     """
     utility function primarily used for loading test data from a file
diff --git a/server/apps/trak/tests/serializers/test_wl_serializer.py b/server/apps/trak/tests/serializers/test_wl_serializer.py
new file mode 100644
index 00000000..087305df
--- /dev/null
+++ b/server/apps/trak/tests/serializers/test_wl_serializer.py
@@ -0,0 +1,11 @@
+class TestWasteLineSerializer:
+    def test_waste_line_json_deserializes(self, waste_serializer) -> None:
+        assert waste_serializer.is_valid() is True
+
+    def test_deserialized_waste_line_saves(self, waste_serializer) -> None:
+        # waste_serializer.is_valid()
+        # saved_waste_line = waste_serializer.save()
+        # assert type(saved_waste_line) is WasteLine
+        # ToDo: we need a manifest pytest.fuxture that we can assign to
+        #  manifest_id field on the wasteline
+        assert True
diff --git a/server/apps/trak/tests/views/test_handler_api.py b/server/apps/trak/tests/views/test_handler_api.py
index 55afb3a1..505cfe2b 100644
--- a/server/apps/trak/tests/views/test_handler_api.py
+++ b/server/apps/trak/tests/views/test_handler_api.py
@@ -1,64 +1,21 @@
-from django.contrib.auth.models import User
-from rest_framework import status
-from rest_framework.test import APITestCase
+from rest_framework.response import Response
 
-from apps.trak.models import Address, Handler
+from apps.trak.tests.conftest import TestApiClient
 
-# test data (for UnitTest based unit tests)
-my_site_id = 'my_epa_id'
-handler_test_object = {
-    "site_type": 'Generator',
-    "epa_id": my_site_id,
-    "name": "VA TEST GEN 2021",
-    "modified": None,
-    "registered": None,
-    "contact": {
-        "phone": {
-            "number": "703-308-0023"
-        },
-        "email": "Testing@EPA.GOV"
-    },
-    "emergency_phone": {
-        "number": "703-308-0023"
-    },
-    "electronic_signatures_info": None,
-    "gis_primary": False,
-    "can_esign": None,
-    "limited_esign": None,
-    "registered_emanifest_user": None
-}
-handler_address = {
-    "street_number": "123",
-    "address1": "VA TEST GEN 2021 WAY",
-    "address2": None,
-    "city": "Arlington",
-    "state": "VA",
-    "country": "US",
-    "zip": "20022"
-}
 
-
-class HandlerAPITest(APITestCase):
+class TestHandlerEndpoints(TestApiClient):
     """
-    Test related to endpoints for the Handler model, serializers, and business logic
+    Tests the for the endpoints related to the handlers
     """
-    base_url = '/api/trak/handler/'
-
-    def setUp(self) -> None:
-        self.user = User.objects.create_user(username='testuser1', email='foo@bar.com',
-                                             password='password1')
-        self.client.force_authenticate(self.user)
-        self.address = Address.objects.create(**handler_address)
-        self.Handler = Handler.objects.create(**handler_test_object,
-                                              site_address=self.address,
-                                              mail_address=self.address)
+    url = '/api/trak/handler'
 
-    def test_valid_request_returns_ok(self):
-        handler_key = self.Handler.pk
-        response = self.client.get(f'{self.base_url}details/{handler_key}')
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
+    def test_get_handler_headers(self):
+        print(f'{self.url}/{self.generator.pk}')
+        response: Response = self.client.get(f'{self.url}/details/{self.generator.pk}')
+        assert response.headers['Content-Type'] == 'application/json'
+        assert response.status_code == 200
 
-    def test_valid_contains_serialized_handler(self):
-        handler_key = self.Handler.pk
-        response = self.client.get(f'{self.base_url}details/{handler_key}')
-        self.assertEqual(response.data['epaSiteId'], my_site_id)
+    def test_returns_serialized_handler(self):
+        print(f'{self.url}/{self.generator.pk}')
+        response: Response = self.client.get(f'{self.url}/details/{self.generator.pk}')
+        assert response.data['epaSiteId'] == self.generator.epa_id