diff --git a/.coveragerc b/.coveragerc index ac42c3853..511d53593 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,3 +5,6 @@ omit = /*/__init__.py /setup.py /*/migrations/* +source = openwisp_controller +parallel = true +concurrency = multiprocessing diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b6a60771..211f25c2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,14 @@ jobs: image: redis ports: - 6379:6379 + postgres: + image: mdillon/postgis:11-alpine + env: + POSTGRES_PASSWORD: openwisp2 + POSTGRES_USER: openwisp2 + POSTGRES_DB: openwisp2 + ports: + - 5432:5432 strategy: fail-fast: false @@ -73,7 +81,9 @@ jobs: ./run-qa-checks - name: Tests run: | - coverage run --source=openwisp_controller runtests.py + coverage run runtests.py --parallel + POSTGRESQL=1 coverage run runtests.py --parallel --keepdb + coverage combine # SAMPLE tests SAMPLE_APP=1 ./runtests.py --keepdb env: diff --git a/README.rst b/README.rst index fbfaff4f8..b9e973f0b 100644 --- a/README.rst +++ b/README.rst @@ -248,11 +248,11 @@ Navigate into the cloned repository: cd openwisp-controller/ -Launch Redis: +Launch Redis and PostgreSQL: .. code-block:: shell - docker-compose up -d redis + docker-compose up -d redis postgres Setup and activate a virtual-environment. (we'll be using `virtualenv `_) @@ -305,6 +305,8 @@ Run tests with: .. code-block:: shell ./runtests.py --parallel + # To run database tests against PostgreSQL backend + POSTGRESQL=1 ./runtests.py --parallel Run quality assurance tests with: diff --git a/docker-compose.yml b/docker-compose.yml index 5ee05a301..c181c26f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,3 +19,12 @@ services: ports: - "6379:6379" entrypoint: redis-server --appendonly yes + + postgres: + image: mdillon/postgis:11-alpine + environment: + POSTGRES_PASSWORD: openwisp2 + POSTGRES_USER: openwisp2 + POSTGRES_DB: openwisp2 + ports: + - 5432:5432 diff --git a/openwisp_controller/subnet_division/rule_types/base.py b/openwisp_controller/subnet_division/rule_types/base.py index 0ee8c5435..ceb6b6e8a 100644 --- a/openwisp_controller/subnet_division/rule_types/base.py +++ b/openwisp_controller/subnet_division/rule_types/base.py @@ -3,7 +3,7 @@ from operator import attrgetter from django.core.exceptions import ObjectDoesNotExist -from django.db import transaction +from django.db import connection, transaction from django.dispatch import Signal from django.utils.translation import gettext_lazy as _ from netaddr import IPNetwork @@ -164,11 +164,15 @@ def get_config(cls, instance): @staticmethod def get_max_subnet(master_subnet, division_rule): + # Only PostgreSQL supports ordering queryset using the "subnet" + # field. If the project is using any other database backend, then + # "created" field is used for ordering the queryset. + order_field = '-subnet' if connection.vendor == 'postgresql' else '-created' try: max_subnet = ( # Get the highest subnet created for this master_subnet Subnet.objects.filter(master_subnet_id=master_subnet.id) - .order_by('-created') + .order_by(order_field) .first() .subnet ) diff --git a/openwisp_controller/subnet_division/tests/test_rule.py b/openwisp_controller/subnet_division/tests/test_rule.py index aab091368..8c7433196 100644 --- a/openwisp_controller/subnet_division/tests/test_rule.py +++ b/openwisp_controller/subnet_division/tests/test_rule.py @@ -1,9 +1,15 @@ -from django.test import TestCase +from django.db import connection +from django.test import TestCase, tag +from openwisp_ipam.tests import CreateModelsMixin as SubnetIpamMixin +from swapper import load_model from ..rule_types.base import BaseSubnetDivisionRuleType +from ..rule_types.vpn import VpnSubnetDivisionRuleType +SubnetDivisionRule = load_model('subnet_division', 'SubnetDivisionRule') -class TestBaseSubnetDivisionRuleType(TestCase): + +class TestBaseSubnetDivisionRuleType(SubnetIpamMixin, TestCase): def test_should_create_subnets_ips(self): with self.assertRaises(NotImplementedError): BaseSubnetDivisionRuleType.should_create_subnets_ips(instance=None) @@ -11,3 +17,23 @@ def test_should_create_subnets_ips(self): def test_provision_for_existing_objects(self): with self.assertRaises(NotImplementedError): BaseSubnetDivisionRuleType.provision_for_existing_objects(rule_obj=None) + + @tag('db_tests') + def test_get_max_subnet(self): + rule = SubnetDivisionRule( + **{ + 'label': 'OW', + 'size': 28, + 'number_of_ips': 2, + 'number_of_subnets': 2, + 'type': VpnSubnetDivisionRuleType, + } + ) + master_subnet = self._create_subnet(subnet='10.0.0.0/16') + self._create_subnet(subnet='10.0.0.16/28', master_subnet=master_subnet) + self._create_subnet(subnet='10.0.0.0/28', master_subnet=master_subnet) + max_subnet = VpnSubnetDivisionRuleType.get_max_subnet(master_subnet, rule) + if connection.vendor == 'postgresql': + self.assertEqual(str(max_subnet), '10.0.0.16/28') + else: + self.assertEqual(str(max_subnet), '10.0.0.0/28') diff --git a/requirements-test.txt b/requirements-test.txt index 4b912a39e..db5fedf9f 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -9,3 +9,4 @@ django_redis~=4.12 mock-ssh-server~=0.9.1 responses~=0.12.1 selenium~=3.141.0 +psycopg2-binary~=2.8.0 diff --git a/runtests.py b/runtests.py index b3b3c29cc..8b200876b 100755 --- a/runtests.py +++ b/runtests.py @@ -8,7 +8,10 @@ if __name__ == '__main__': sys.path.insert(0, 'tests') - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') + if os.environ.get('POSTGRESQL', False): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.postgresql_settings') + else: + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') from django.core.management import execute_from_command_line args = sys.argv @@ -18,6 +21,10 @@ args.insert(2, 'openwisp_controller') else: args.insert(2, 'openwisp2') + + if os.environ.get('POSTGRESQL', False): + args.extend(['--tag', 'db_tests']) + execute_from_command_line(args) if not os.environ.get('SAMPLE_APP', False): diff --git a/tests/openwisp2/postgresql_settings.py b/tests/openwisp2/postgresql_settings.py new file mode 100644 index 000000000..e37f2a9f1 --- /dev/null +++ b/tests/openwisp2/postgresql_settings.py @@ -0,0 +1,12 @@ +from .settings import * + +DATABASES = { + 'default': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'NAME': 'openwisp2', + 'USER': 'openwisp2', + 'PASSWORD': 'openwisp2', + 'HOST': '127.0.0.1', + 'PORT': '5432', + }, +}