mirror of
https://github.com/peeringdb/peeringdb.git
synced 2024-05-11 05:55:09 +00:00
* New Field "Health Check" #512 * Add status_dashboard to fields #512 * RS Peer Checkbox also visible on IX Site #727 * Add "Management" search field to Advanced Search of Exchanges #506 * wrap correctly on mobile #881 * missing delete button for user #653 * Removed cruft from IX view template #881 * To force or not to force www, that is a question #916 * add health check fields to entity creation forms (fac, ix, net) (#512) * status_dashboard should accept null values #512 * Sort usergroup names in https://peeringdb.com/cp/peeringdb_server/userpermission/xxxxx numerically #656 * fix issue with deleted entities showing up in search results #1042 * 2FA Backup Tokens language doesn't seem correct #908 * linting * poetry relock * fix user permission save trying to create a user * add status_dashboard to mock data * docs * add api doc regen to gen_docs call fix issue with run_tests if BASE_URL env var is set * fix generate_schema typo * linting Co-authored-by: David Poarch <dpoarch@20c.com> Co-authored-by: Stefan Pratter <stefan@20c.com>
368 lines
12 KiB
Python
368 lines
12 KiB
Python
"""
|
|
Unit-tests for quick search functionality - note that advanced search is not
|
|
tested here as that is using the PDB API entirely.
|
|
"""
|
|
import datetime
|
|
import re
|
|
|
|
import pytest
|
|
import unidecode
|
|
from django.core.management import call_command
|
|
from django.test import RequestFactory, TestCase
|
|
|
|
import peeringdb_server.models as models
|
|
import peeringdb_server.search as search
|
|
import peeringdb_server.views as views
|
|
|
|
|
|
class SearchTests(TestCase):
|
|
"""
|
|
Test quick-search functionality
|
|
"""
|
|
|
|
@classmethod
|
|
def create_instance(cls, model, org, asn=1, prefix="Test", accented=False):
|
|
|
|
kwargs = {}
|
|
if model.handleref.tag == "net":
|
|
kwargs = {"asn": asn}
|
|
|
|
kwargs.update(status="ok", name=f"{prefix} {model.handleref.tag}")
|
|
|
|
if accented:
|
|
kwargs.update(name=f"ãccented {model.handleref.tag}")
|
|
|
|
if model.handleref.tag != "org":
|
|
kwargs.update(org=org)
|
|
|
|
instance = model.objects.create(**kwargs)
|
|
|
|
if model.handleref.tag == "org":
|
|
instance.org_id = instance.id
|
|
|
|
return instance
|
|
|
|
@classmethod
|
|
def setUpTestData(cls):
|
|
|
|
cls.instances = {}
|
|
cls.instances_accented = {}
|
|
cls.instances_sponsored = {}
|
|
|
|
# create an instance of each searchable model, so we have something
|
|
# to search for
|
|
cls.org = models.Organization.objects.create(name="Parent org")
|
|
for model in search.autocomplete_models:
|
|
cls.instances[model.handleref.tag] = cls.create_instance(model, cls.org)
|
|
cls.instances_accented[model.handleref.tag] = cls.create_instance(
|
|
model, cls.org, asn=2, accented=True
|
|
)
|
|
|
|
# we also need to test that sponsor ship status comes through
|
|
# accordingly
|
|
cls.org_w_sponsorship = models.Organization.objects.create(
|
|
name="Sponsor Parent org", status="ok"
|
|
)
|
|
|
|
now = datetime.datetime.now().replace(tzinfo=models.UTC())
|
|
|
|
cls.sponsorship = models.Sponsorship.objects.create(
|
|
start_date=now - datetime.timedelta(days=1),
|
|
end_date=now + datetime.timedelta(days=1),
|
|
level=1,
|
|
)
|
|
models.SponsorshipOrganization.objects.create(
|
|
org=cls.org_w_sponsorship, sponsorship=cls.sponsorship
|
|
)
|
|
|
|
for model in search.autocomplete_models:
|
|
cls.instances_sponsored[model.handleref.tag] = cls.create_instance(
|
|
model, cls.org_w_sponsorship, asn=3, prefix="Sponsor"
|
|
)
|
|
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
def test_search(self):
|
|
"""
|
|
search for entities containing 'Test' - this should return all
|
|
instances we created during setUp
|
|
"""
|
|
|
|
rv = search.search("Test")
|
|
for k, inst in list(self.instances.items()):
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert rv[k][0]["name"] == inst.search_result_name
|
|
assert rv[k][0]["org_id"] == inst.org_id
|
|
|
|
# test that term order does not matter
|
|
|
|
for k, inst in list(self.instances.items()):
|
|
rv = search.search(f"Test {k}")
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert rv[k][0]["name"] == inst.search_result_name
|
|
assert rv[k][0]["org_id"] == inst.org_id
|
|
|
|
rv = search.search(f"{k} Test")
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert rv[k][0]["name"] == inst.search_result_name
|
|
assert rv[k][0]["org_id"] == inst.org_id
|
|
|
|
rv = search.search("as1")
|
|
assert len(rv["net"]) == 1
|
|
assert rv["net"][0]["name"] == self.instances["net"].search_result_name
|
|
assert rv["net"][0]["org_id"] == self.instances["net"].org_id
|
|
|
|
rv = search.search("asn1")
|
|
assert len(rv["net"]) == 1
|
|
assert rv["net"][0]["name"] == self.instances["net"].search_result_name
|
|
assert rv["net"][0]["org_id"] == self.instances["net"].org_id
|
|
|
|
def test_sponsor_badges(self):
|
|
"""
|
|
Test that the sponsor badges show up in search result
|
|
"""
|
|
|
|
factory = RequestFactory()
|
|
request = factory.get("/search", {"q": "Sponsor"})
|
|
response = views.request_search(request)
|
|
m = re.findall(
|
|
re.escape('<a href="/sponsors" class="sponsor silver">'),
|
|
response.content.decode(),
|
|
)
|
|
|
|
assert len(m) == 4
|
|
|
|
def test_search_case(self):
|
|
"""
|
|
search for entities containing 'test' - this should return all
|
|
instances we created during setUp since matching is case-insensitive
|
|
"""
|
|
rv = search.search("test")
|
|
for k, inst in list(self.instances.items()):
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert rv[k][0]["name"] == inst.search_result_name
|
|
|
|
def test_search_unaccent(self):
|
|
"""
|
|
search for entities containing 'ãccented' using accented and unaccented
|
|
terms
|
|
"""
|
|
rv = search.search("accented")
|
|
for k, inst in list(self.instances_accented.items()):
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert unidecode.unidecode(rv[k][0]["name"]) == unidecode.unidecode(
|
|
inst.search_result_name
|
|
)
|
|
|
|
rv = search.search("ãccented")
|
|
for k, inst in list(self.instances_accented.items()):
|
|
assert k in rv
|
|
assert len(rv[k]) == 1
|
|
assert unidecode.unidecode(rv[k][0]["name"]) == unidecode.unidecode(
|
|
inst.search_result_name
|
|
)
|
|
|
|
def test_search_asn_match(self):
|
|
"""
|
|
Test that exact numeric match on an ASN
|
|
always appears at the top of the results (#232)
|
|
"""
|
|
|
|
# network with asn 633 - this should be the first
|
|
# resut when searching for `633`
|
|
|
|
net_1 = models.Network.objects.create(
|
|
name="Test ASN Matching", asn=633, org=self.org, status="ok"
|
|
)
|
|
|
|
# network with asn 6333, this should match, but not
|
|
# be the first result
|
|
|
|
net_2 = models.Network.objects.create(
|
|
name="Test ASN Matching 2", asn=6333, org=self.org, status="ok"
|
|
)
|
|
|
|
# network with asn 6334 and 633 as part of its name
|
|
# this should score high, but should not be the first
|
|
# result
|
|
|
|
net_3 = models.Network.objects.create(
|
|
name="Test ASN 633 Matching", asn=6334, org=self.org, status="ok"
|
|
)
|
|
|
|
# rebuild the index
|
|
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
rv = search.search("633")
|
|
|
|
assert rv["net"][0]["id"] == net_1.id
|
|
|
|
# clean up
|
|
|
|
net_1.delete(hard=True)
|
|
net_2.delete(hard=True)
|
|
net_3.delete(hard=True)
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
def test_search_asn_direct(self):
|
|
"""
|
|
Test that the new fast matching methods of asn and ip address
|
|
comparisons do not return deleted objects
|
|
"""
|
|
|
|
org_1 = models.Organization.objects.create(name="Test org 1")
|
|
net_1 = models.Network.objects.create(
|
|
org_id=org_1.id, asn=34532, name="Net 1", status="ok"
|
|
)
|
|
|
|
rv = search.search("34532")
|
|
|
|
assert rv["net"][0]["id"] == net_1.id
|
|
|
|
# soft-delete network, should disappear from results
|
|
|
|
net_1.delete()
|
|
rv = search.search("34532")
|
|
|
|
assert not rv["net"]
|
|
|
|
def test_search_ipv4(self):
|
|
"""
|
|
This will test a search for a partial ipv4 address
|
|
"""
|
|
org_1 = models.Organization.objects.create(name="Test org 1", status="ok")
|
|
org_2 = models.Organization.objects.create(name="Test org 2", status="ok")
|
|
net_1 = models.Network.objects.create(
|
|
org_id=org_1.id, asn=34532, name="Net 1", status="ok"
|
|
)
|
|
net_2 = models.Network.objects.create(
|
|
org_id=org_2.id, asn=2432, name="Net 2", status="ok"
|
|
)
|
|
ix_1 = models.InternetExchange.objects.create(
|
|
org_id=org_1.id, name="IX 1", status="ok"
|
|
)
|
|
ix_2 = models.InternetExchange.objects.create(
|
|
org_id=org_2.id, name="IX 2", status="ok"
|
|
)
|
|
next_id = models.IXLan.objects.all().order_by("-id").first().id + 1
|
|
ixlan_1 = models.IXLan(id=next_id, ix=ix_1)
|
|
ixlan_2 = models.IXLan(id=next_id + 1, ix=ix_2)
|
|
ixlan_1.save()
|
|
ixlan_2.save()
|
|
netixlan_1 = models.NetworkIXLan.objects.create(
|
|
ipaddr4="8.8.4.4",
|
|
asn=34532,
|
|
ixlan=ixlan_1,
|
|
network=net_1,
|
|
speed=1000,
|
|
status="ok",
|
|
)
|
|
|
|
netixlan_2 = models.NetworkIXLan.objects.create(
|
|
ipaddr4="8.8.8.8",
|
|
asn=2432,
|
|
ixlan=ixlan_2,
|
|
network=net_2,
|
|
speed=1000,
|
|
status="ok",
|
|
)
|
|
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
rv = search.search("8.8.4")
|
|
|
|
assert rv["net"][0]["id"] == net_1.id
|
|
|
|
# soft-delete network, should disappear from results
|
|
net_1.delete()
|
|
rv = search.search("8.8.4")
|
|
assert not rv["net"]
|
|
|
|
# clean up
|
|
netixlan_1.delete(hard=True)
|
|
netixlan_2.delete(hard=True)
|
|
ix_1.delete(hard=True)
|
|
ix_2.delete(hard=True)
|
|
ixlan_1.delete(hard=True)
|
|
ixlan_2.delete(hard=True)
|
|
net_1.delete(hard=True)
|
|
net_2.delete(hard=True)
|
|
org_1.delete(hard=True)
|
|
org_2.delete(hard=True)
|
|
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
def test_search_ipv6(self):
|
|
"""
|
|
This will test a search for a partial ipv6 address.
|
|
"""
|
|
org_1 = models.Organization.objects.create(name="Test org 1", status="ok")
|
|
org_2 = models.Organization.objects.create(name="Test org 2", status="ok")
|
|
net_1 = models.Network.objects.create(
|
|
org_id=org_1.id, asn=34532, name="Net 1", status="ok"
|
|
)
|
|
net_2 = models.Network.objects.create(
|
|
org_id=org_2.id, asn=2432, name="Net 2", status="ok"
|
|
)
|
|
ix_1 = models.InternetExchange.objects.create(
|
|
org_id=org_1.id, name="IX 1", status="ok"
|
|
)
|
|
ix_2 = models.InternetExchange.objects.create(
|
|
org_id=org_2.id, name="IX 2", status="ok"
|
|
)
|
|
next_id = models.IXLan.objects.all().order_by("-id").first().id + 1
|
|
ixlan_1 = models.IXLan(id=next_id, ix=ix_1)
|
|
ixlan_2 = models.IXLan(id=next_id + 1, ix=ix_2)
|
|
ixlan_1.save()
|
|
ixlan_2.save()
|
|
netixlan_1 = models.NetworkIXLan.objects.create(
|
|
ipaddr6="2001:4888:456:2::",
|
|
asn=34532,
|
|
ixlan=ixlan_1,
|
|
network=net_1,
|
|
speed=1000,
|
|
status="ok",
|
|
)
|
|
|
|
netixlan_2 = models.NetworkIXLan.objects.create(
|
|
ipaddr6="2001:4888:432:2::",
|
|
asn=2432,
|
|
ixlan=ixlan_2,
|
|
network=net_2,
|
|
speed=1000,
|
|
status="ok",
|
|
)
|
|
|
|
call_command("rebuild_index", "--noinput")
|
|
|
|
rv = search.search("2001:4888:456")
|
|
|
|
assert rv["net"][0]["id"] == net_1.id
|
|
|
|
# soft-delete network, should disappear from results
|
|
|
|
net_1.delete()
|
|
rv = search.search("2001:4888:456")
|
|
|
|
assert not rv["net"]
|
|
|
|
# clean up
|
|
netixlan_1.delete(hard=True)
|
|
netixlan_2.delete(hard=True)
|
|
ix_1.delete(hard=True)
|
|
ix_2.delete(hard=True)
|
|
ixlan_1.delete(hard=True)
|
|
ixlan_2.delete(hard=True)
|
|
net_1.delete(hard=True)
|
|
net_2.delete(hard=True)
|
|
org_1.delete(hard=True)
|
|
org_2.delete(hard=True)
|
|
|
|
call_command("rebuild_index", "--noinput")
|