Skip to content

Commit

Permalink
Add tests and docstrings to observation-related items
Browse files Browse the repository at this point in the history
  • Loading branch information
ColonelThirtyTwo committed Nov 3, 2023
1 parent 065a0c0 commit f478b34
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 1 deletion.
36 changes: 36 additions & 0 deletions ghostwriter/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,23 @@ def tags(self, create, extracted, **kwargs):
self.tags.add(tag)


class ObservationFactory(factory.django.DjangoModelFactory):
class Meta:
model = "reporting.Observation"

title = factory.Sequence(lambda n: "Observation %s" % n)
description = Faker("paragraph")

@factory.post_generation
def tags(self, create, extracted, **kwargs):
if not create:
return

if extracted:
for tag in extracted:
self.tags.add(tag)


class DocTypeFactory(factory.django.DjangoModelFactory):
class Meta:
model = "reporting.DocType"
Expand Down Expand Up @@ -393,6 +410,25 @@ def tags(self, create, extracted, **kwargs):
self.tags.add(tag)


class ReportObservationLinkFactory(factory.django.DjangoModelFactory):
class Meta:
model = "reporting.ReportObservationLink"

title = factory.Sequence(lambda n: "Local Observation %s" % n)
position = 1
description = Faker("paragraph")
added_as_blank = Faker("boolean")

@factory.post_generation
def tags(self, create, extracted, **kwargs):
if not create:
return

if extracted:
for tag in extracted:
self.tags.add(tag)


class BlankReportFindingLinkFactory(factory.django.DjangoModelFactory):
class Meta:
model = "reporting.ReportFindingLink"
Expand Down
6 changes: 6 additions & 0 deletions ghostwriter/reporting/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ def __init__(self, *args, **kwargs):

class ObservationFilter(django_filters.FilterSet):
"""
Filter :model:`reporting.Observation` model for searching.
**Fields**
``title``
Case insensitive search of the title field contents.
"""

title = django_filters.CharFilter(
Expand Down
2 changes: 2 additions & 0 deletions ghostwriter/reporting/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,8 @@ def clean_color(self, *args, **kwargs):


class ObservationForm(forms.ModelForm):
"""Save an individual :model:`reporting.Observation`."""

class Meta:
model = Observation
fields = "__all__"
Expand Down
6 changes: 5 additions & 1 deletion ghostwriter/reporting/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,11 @@ def __str__(self):


class Observation(models.Model):
"""A positive observation"""
"""
An observation.
Similar to a finding, but more generic. Can be used for positive observations or other things.
"""

title = models.CharField(
"Title",
Expand Down
54 changes: 54 additions & 0 deletions ghostwriter/reporting/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
FindingFactory,
FindingNoteFactory,
LocalFindingNoteFactory,
ObservationFactory,
ProjectAssignmentFactory,
ProjectFactory,
ReportFactory,
ReportFindingLinkFactory,
ReportObservationLinkFactory,
ReportTemplateFactory,
SeverityFactory,
UserFactory,
Expand All @@ -24,8 +26,10 @@
FindingForm,
FindingNoteForm,
LocalFindingNoteForm,
ObservationForm,
ReportFindingLinkUpdateForm,
ReportForm,
ReportObservationLinkUpdateForm,
ReportTemplateForm,
SelectReportTemplateForm,
SeverityForm,
Expand Down Expand Up @@ -94,6 +98,25 @@ def test_duplicate_title(self):
self.assertEqual(errors[0].code, "unique")


class ObservationFormTest(TestCase):
"""Collection of tests for :form:`reporting.ObservationForm`."""

@classmethod
def setUpTestData(cls):
cls.observation = ObservationFactory()

def test_valid_data(self):
self.observation.title = "New Title"
form = ObservationForm(data=self.observation.__dict__)
self.assertTrue(form.is_valid())

def test_duplicate_title(self):
form = ObservationForm(data=self.observation.__dict__)
errors = form["title"].errors.as_data()
self.assertEqual(len(errors), 1)
self.assertEqual(errors[0].code, "unique")


class ReportFormTests(TestCase):
"""Collection of tests for :form:`reporting.ReportForm`."""

Expand Down Expand Up @@ -241,6 +264,37 @@ def test_complete_field(self):
self.assertTrue(self.complete_finding.complete)


class ReportObservationLinkUpdateFormTests(TestCase):
"""Collection of tests for :form:`reporting.ReportObservationLinkForm`."""

@classmethod
def setUpTestData(cls):
cls.observation = ReportObservationLinkFactory()
cls.blank_observation = ReportObservationLinkFactory(added_as_blank=True)

def test_valid_data(self):
data = self.observation.__dict__.copy()
data["instance"] = self.observation
form = ReportObservationLinkUpdateForm(data=data)
self.assertTrue(form.is_valid())

def test_blank_assigned_to(self):
self.observation.assigned_to = None

data = self.observation.__dict__.copy()
data["instance"] = self.observation
form = ReportObservationLinkUpdateForm(data)
self.assertTrue(form.is_valid())

def test_added_as_blank_field(self):
data = self.observation.__dict__.copy()
data["instance"] = self.blank_observation
form = ReportObservationLinkUpdateForm(data=data)
self.assertTrue(form.is_valid())
form.save()
self.assertTrue(self.blank_observation.added_as_blank)


class EvidenceFormTests(TestCase):
"""Collection of tests for :form:`reporting.EvidenceForm`."""

Expand Down
61 changes: 61 additions & 0 deletions ghostwriter/reporting/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
ReportDocxTemplateFactory,
ReportFactory,
ReportFindingLinkFactory,
ReportObservationLinkFactory,
ReportPptxTemplateFactory,
ReportTemplateFactory,
SeverityFactory,
Expand Down Expand Up @@ -1215,6 +1216,66 @@ def test_custom_context_exists(self):
)


# Tests related to :model:`reporting.ReportFindingLink`


class ReportObservationLinkUpdateViewTests(TestCase):
"""Collection of tests for :view:`reporting.ReportObservationLinkUpdate`."""

@classmethod
def setUpTestData(cls):
cls.report = ReportFactory(
docx_template=ReportDocxTemplateFactory(),
pptx_template=ReportPptxTemplateFactory(),
)

cls.high_severity = SeverityFactory(severity="High", weight=1)
cls.critical_severity = SeverityFactory(severity="Critical", weight=0)

cls.user = UserFactory(password=PASSWORD)
cls.mgr_user = UserFactory(password=PASSWORD, role="manager")
cls.new_user = UserFactory(password=PASSWORD)

cls.num_of_observations = 10
cls.observations = []
for observation_id in range(cls.num_of_observations):
title = f"observation {observation_id}"
cls.observations.append(ReportObservationLinkFactory(title=title, report=cls.report))

cls.uri = reverse("reporting:local_observation_edit", kwargs={"pk": cls.observations[0].pk})

def setUp(self):
self.client = Client()
self.client_auth = Client()
self.client_mgr = Client()
self.assertTrue(self.client_auth.login(username=self.user.username, password=PASSWORD))
self.assertTrue(self.client_mgr.login(username=self.mgr_user.username, password=PASSWORD))

def test_view_uri_exists_at_desired_location(self):
response = self.client_mgr.get(self.uri)
self.assertEqual(response.status_code, 200)

def test_view_requires_login_and_permissions(self):
response = self.client.get(self.uri)
self.assertEqual(response.status_code, 302)

response = self.client_auth.get(self.uri)
self.assertEqual(response.status_code, 302)

def test_view_uses_correct_template(self):
response = self.client_mgr.get(self.uri)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "reporting/local_observation_edit.html")

def test_custom_context_exists(self):
response = self.client_mgr.get(self.uri)
self.assertIn("cancel_link", response.context)
self.assertEqual(
response.context["cancel_link"],
reverse("reporting:report_detail", kwargs={"pk": self.report.pk}),
)


# Tests related to :model:`reporting.Evidence`


Expand Down
51 changes: 51 additions & 0 deletions ghostwriter/reporting/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2828,10 +2828,31 @@ def get(self, request, *args, **kwarg):


class ObservationDetailView(RoleBasedAccessControlMixin, DetailView):
"""
Display an individual :model:`reporting.Observation`.
**Template**
:template:`reporting/observation_detail.html`
"""

model = Observation


class ObservationCreate(RoleBasedAccessControlMixin, CreateView):
"""
Create an individual instance of :model:`reporting.Observation`.
**Context**
``cancel_link``
Link for the form's Cancel button to return to the observation list page
**Template**
:template:`reporting/observation_form.html`
"""

model = Observation
form_class = ObservationForm

Expand All @@ -2857,6 +2878,19 @@ def get_success_url(self):


class ObservationUpdate(RoleBasedAccessControlMixin, UpdateView):
"""
Update an individual instance of :model:`reporting.Observation`.
**Context**
``cancel_link``
Link for the form's Cancel button to return to the observations list page
**Template**
:template:`reporting/observation_form.html`
"""

model = Observation
form_class = ObservationForm

Expand All @@ -2882,6 +2916,23 @@ def get_success_url(self):


class ObservationDelete(RoleBasedAccessControlMixin, DeleteView):
"""
Delete an individual instance of :model:`reporting.Observation`.
**Context**
``object_type``
String describing what is to be deleted
``object_to_be_deleted``
To-be-deleted instance of :model:`reporting.Observation`
``cancel_link``
Link for the form's Cancel button to return to observation list page
**Template**
:template:`confirm_delete.html`
"""

model = Observation
template_name = "confirm_delete.html"

Expand Down

0 comments on commit f478b34

Please sign in to comment.