Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added delete tests #396

Closed
wants to merge 6 commits into from

Conversation

aleccohan
Copy link
Collaborator

Added new delete tests to match with the changes made to the delete method.

@aleccohan aleccohan requested a review from Glutexo August 13, 2019 18:46
@aleccohan aleccohan self-assigned this Aug 13, 2019
Copy link
Collaborator

@Glutexo Glutexo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to request some changes here as the test is can easily yield false positives. I also need to ask, whether the behavior described by this test is the intended one, or whether it’s just derived from the actual workings of #392.

test_api.py Outdated
host_wrapper.fqdn = host[2]
host_wrapper.mac_addresses = ["apple"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice. 😀 It’s not a valid MAC address though. It shouldn’t be a problem though, because we are not validating the format.

test_api.py Outdated
@@ -1110,7 +1110,14 @@ def create_hosts(self):
host_wrapper.account = ACCOUNT
host_wrapper.display_name = host[0]
host_wrapper.insights_id = host[1]
host_wrapper.rhel_machine_id = host[1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the IDs are the same. This can lead to false positives and in your implementation below, it actually does. Your assert assert rhel_machine_id in m.events[0] would pass even if the value weren’t actually there, because it’s the same as insights_id.

test_api.py Outdated
@@ -1210,6 +1217,16 @@ def test_invalid_host_id(self):
class DeleteHostsTestCase(PreCreatedHostsBaseTestCase):
def test_create_then_delete(self):
original_id = self.added_hosts[0].id
insights_id = self.added_hosts[0].insights_id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can’t we just use the values directly? When it was just the original_id, it was ok, but now its just too many lines without much added value.

test_api.py Outdated
@@ -1227,7 +1244,21 @@ def __call__(self, e):
with unittest.mock.patch("api.host.emit_event", new=MockEmitEvent()) as m:
self.delete(url, 200, return_response_as_json=False)
assert original_id in m.events[0]

assert insights_id in m.events[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These assertions are just vaguely testing the message format. The message here (m.events[0]) is a str and by these in asserts you are just checking that the value is present somewhere in the string. This approach has several flaws:

  • JSON values are not plain text and escaping may occur. Although for UUIDs this doesn’t matter and so it doesn’t for your non-arbitrary safe values.
  • The expected value may occur under a different key, it may be just a subset of a longer string etc. If it appears anywhere, the test passes.
  • Because you used the same value for all the IDs, you aren’t actually even checking that all values from the different fields are there.

The only way to do this properly I think is to deserialize the JSON message, check that it’s a dict and then check for the fields and their values. I’d just add to that that it’d be nice to check not only that there are right values under right keys, but also whether there isn’t anything more, a sort of negative test.

event = json.loads(m.events[0])

self.assertIsInstance(event, dict)
expected_keys = {
    "timestamp",
    "type",
    "id",
    "account",
    "insights_id",
    "rhel_machine_id",
    "subscription_manager_id",
    "satellite_id",
    "bios_uuid",
    "ip_addresses",
    "fqdn",
    "mac_addresses",
    "external_id",
}
self.assertEqual(set(event.keys()), expected_keys)

self.assertEqual(original_id, event["id"])
self.assertEqual(insights_id, event["insights_id"])
self.assertEqual(rhel_machine_id, event["rhel_machine_id"])
self.assertEqual(subscription_manager_id, event["subscription_manager_id"])
self.assertEqual(satellite_id, event["satellite_id"])
self.assertEqual(bios_uuid, event["bios_uuid"])
self.assertEqual(fqdn, event["fqdn"])
self.assertEqual(external_id, event["external_id"])
self.assertEqual(account, event["account"])

test_api.py Outdated
assert external_id in m.events[0]
assert account in m.events[0]

for ip in ip_addresses:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only this has the same problem as mentioned in my previous comment, but it’s lacking the negative test too. The test verifies whether all the expected IP addresses are there, but not whether there isn’t another IP that shouldn’t be there. This applies to the MAC address test below too.

Since the lists are encoded as ordered all the way around, it’s safe to plainly compare them.

self.assertEqual(ip_addresses, event["ip_addresses"])

If you’d want to compare them regardless of their order, you could do it like this:

from collections import Counter
self.assertEqual(Counter(ip_addresses), Counter(event["ip_addresses"]))

But that’s an unnecessary complexity here as these are always lists and never sets.

test_api.py Outdated

for mac in mac_addresses:
assert mac in m.events[0]

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re testing for all canonical facts and the account number. What fields are actually required in the message though? In the tickets I saw only requests for account number (RHCLOUD-1561) and machine ID (RHCLOUD-1995). But it makes sense to include all the canonical facts.

There is no check though to the type and timestamp fields.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, The only fields required in the message are the account number and machine ID. Derek suggested that I add all of the canonical facts in case someone else in the future needed them for something.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes perfect sense! It’d be nice to have this solution documented somewhere, at least in a JIRA ticket comment.

Copy link
Collaborator

@dehort dehort left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good.

I'd like to have this change included in PR#392 or vice versa.

}
self.assertEqual(set(event.keys()), expected_keys)

self.assertEqual(self.added_hosts[0].timestamp, event["timestamp"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HostWrapper does not have a timestamp field. I think its ok if you check for the existence of the timestamp field.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>           self.assertEqual(self.added_hosts[0].timestamp, event["timestamp"])
E           AttributeError: 'HostWrapper' object has no attribute 'timestamp'

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s also possible to patch the datetime.utcnow method. Like that it’s even possible to check its format.

@unittest.mock.patch("app.events.datetime", **{"utcnow.return_value": datetime.utcnow()})
def test_create_then_delete(self, datetime_mock):
    …
    with unittest.mock.patch("api.host.emit_event", new=MockEmitEvent()) as m:
        …
        timestamp_iso = datetime_mock.utcnow.return_value.isoformat()
        self.assertEqual(f"{timestamp_iso}+00:00", event["timestamp"])
        …

self.assertEqual(set(event.keys()), expected_keys)

self.assertEqual(self.added_hosts[0].timestamp, event["timestamp"])
self.assertEqual(self.added_hosts[0].type, event["type"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HostWrapper does not have a type field. You can verify that the "type" field is set to "delete".

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>           self.assertEqual(self.added_hosts[0].type, event["type"])
E           AttributeError: 'HostWrapper' object has no attribute 'type'

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.assertEqual(self.added_hosts[0].type, event["type"])
self.assertEqual("delete", event["type"])

@Glutexo
Copy link
Collaborator

Glutexo commented Aug 16, 2019

This can be closed, now when it’s merged into #392, right?

@aleccohan
Copy link
Collaborator Author

Yeah, this can be closed! I merged it in.

@aleccohan aleccohan closed this Aug 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants