From c0cef6fb9f25636919e27c2d39869b8c20394045 Mon Sep 17 00:00:00 2001 From: Alfonso Alongi Date: Wed, 8 Oct 2025 12:36:39 +0200 Subject: [PATCH 1/2] Add compatibility with python 3.7+ and replace legacy obsolete pyjq with jq library. --- README.md | 14 +++++--------- commands/amis.py | 2 +- commands/collect.py | 2 +- commands/prepare.py | 2 +- commands/sg_ips.py | 2 +- commands/weboftrust.py | 2 +- requirements.txt | 21 ++++++++++----------- shared/audit.py | 2 +- shared/common.py | 2 +- shared/find_unused.py | 9 +++++++-- shared/iam_audit.py | 2 +- shared/nodes.py | 2 +- shared/public.py | 20 +++++++++++++------- tests/unit/test_prepare.py | 2 +- 14 files changed, 45 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 28a4d5349..f3202ea87 100644 --- a/README.md +++ b/README.md @@ -49,16 +49,14 @@ If you want to add your own private commands, you can create a `private_commands ## Installation Requirements: -- python 3 (3.7.0rc1 is known to work), `pip`, and `virtualenv` -- You will also need `jq` (https://stedolan.github.io/jq/) and the library `pyjq` (https://github.com/doloopwhile/pyjq), which require some additional tools installed that will be shown. +- python 3 (3.7+ supported), `pip`, and `virtualenv` +- The project uses the `jq` library for JSON processing, which provides better Python 3 compatibility than the legacy `pyjq` On macOS: ``` # clone the repo git clone https://github.com/duo-labs/cloudmapper.git -# Install pre-reqs for pyjq -brew install autoconf automake awscli freetype jq libtool python3 cd cloudmapper/ python3 -m venv ./venv && source venv/bin/activate pip install --prefer-binary -r requirements.txt @@ -68,11 +66,9 @@ On Linux: ``` # clone the repo git clone https://github.com/duo-labs/cloudmapper.git -# (AWS Linux, Centos, Fedora, RedHat etc.): -# sudo yum install autoconf automake libtool python3-devel.x86_64 python3-tkinter python-pip jq awscli -# (Debian, Ubuntu etc.): -# You may additionally need "build-essential" -sudo apt-get install autoconf automake libtool python3.7-dev python3-tk jq awscli +# Install system dependencies (if needed): +# (RHEL/Fedora): sudo dnf install python3-devel python3-tkinter python-pip jq awscli +# (Debian/Ubuntu): sudo apt-get install python3-dev python3-tk jq awscli cd cloudmapper/ python3 -m venv ./venv && source venv/bin/activate pip install -r requirements.txt diff --git a/commands/amis.py b/commands/amis.py index 35c9e6ae5..580041fbb 100644 --- a/commands/amis.py +++ b/commands/amis.py @@ -2,7 +2,7 @@ import sys import json import argparse -import pyjq +import jq as pyjq import os.path from shared.nodes import Account, Region from shared.common import parse_arguments, query_aws diff --git a/commands/collect.py b/commands/collect.py index 073914f4e..35f615c0b 100644 --- a/commands/collect.py +++ b/commands/collect.py @@ -8,7 +8,7 @@ import time import boto3 import yaml -import pyjq +import jq as pyjq import urllib.parse from botocore.exceptions import ClientError, EndpointConnectionError, NoCredentialsError from shared.common import get_account, custom_serializer diff --git a/commands/prepare.py b/commands/prepare.py index 2ad315fd3..e8034758a 100644 --- a/commands/prepare.py +++ b/commands/prepare.py @@ -27,7 +27,7 @@ import operator import itertools import argparse -import pyjq +import jq as pyjq import copy import urllib.parse from netaddr import IPNetwork, IPAddress diff --git a/commands/sg_ips.py b/commands/sg_ips.py index f4da0eec6..1cfaa906f 100644 --- a/commands/sg_ips.py +++ b/commands/sg_ips.py @@ -1,7 +1,7 @@ from collections import OrderedDict from os import path from netaddr import IPNetwork -import pyjq +import jq as pyjq from shared.common import ( parse_arguments, diff --git a/commands/weboftrust.py b/commands/weboftrust.py index d5391803f..85297ad7c 100644 --- a/commands/weboftrust.py +++ b/commands/weboftrust.py @@ -2,7 +2,7 @@ from os import path, listdir import json import yaml -import pyjq +import jq as pyjq import urllib.parse from shared.common import ( diff --git a/requirements.txt b/requirements.txt index 3e5bce870..ea83520a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ astroid==2.8.4 autoflake==1.4 autopep8==1.6.0 -boto3==1.19.10 -botocore==1.22.10 +boto3~=1.28.85 +botocore~=1.31.85 certifi==2023.7.22 chardet==4.0.0 charset-normalizer==2.0.7 @@ -18,32 +18,31 @@ kiwisolver==1.3.2 kwonly-args==1.0.10 lazy-object-proxy==1.6.0 MarkupSafe==2.0.1 -matplotlib==3.4.3 +matplotlib>=3.5.0,<3.9.0 mccabe==0.6.1 mock==4.0.3 netaddr==0.8.0 nose==1.3.7 -numpy==1.22.0 -pandas==1.3.4 +pandas>=1.3.4 parliament==1.5.2 -Pillow==10.0.1 +Pillow>=9.5.0,<10.0.0; python_version<'3.8' +Pillow>=10.0.1,<11.0.0; python_version>='3.8' platformdirs==2.4.0 policyuniverse==1.4.0.20210819 pycodestyle==2.8.0 pyflakes==2.4.0 -pyjq==2.4.0 +jq>=1.6.0 pylint==2.11.1 pyparsing==3.0.4 python-dateutil==2.8.2 pytz==2021.3 -PyYAML==6.0 +PyYAML>=6.0.1 requests==2.26.0 -s3transfer==0.5.0 -scipy==1.10.0 +s3transfer>=0.7.0,<0.8.0 seaborn==0.11.2 six==1.16.0 toml==0.10.2 -typed-ast==1.4.3 + typing-extensions==3.10.0.2 urllib3==1.26.18 wrapt==1.13.3 diff --git a/shared/audit.py b/shared/audit.py index 0154a7020..fd389c908 100644 --- a/shared/audit.py +++ b/shared/audit.py @@ -1,7 +1,7 @@ import json import yaml from os.path import exists -import pyjq +import jq as pyjq import traceback import re import pkgutil diff --git a/shared/common.py b/shared/common.py index f10338361..55f3a732e 100644 --- a/shared/common.py +++ b/shared/common.py @@ -2,7 +2,7 @@ import argparse import json import datetime -import pyjq +import jq as pyjq import yaml import sys from netaddr import IPNetwork diff --git a/shared/find_unused.py b/shared/find_unused.py index 9e407f8ca..330ac2d56 100644 --- a/shared/find_unused.py +++ b/shared/find_unused.py @@ -1,4 +1,4 @@ -import pyjq +import jq as pyjq from shared.common import query_aws, get_regions, get_parameter_file from shared.nodes import Account, Region @@ -130,7 +130,12 @@ def find_unused_elastic_load_balancers(region): "describe-target-health", target_group["TargetGroupArn"], ) - instances = pyjq.one(".TargetHealthDescriptions? | length", target_healths) + try: + instances = pyjq.first( + ".TargetHealthDescriptions? | length", target_healths + ) + except StopIteration: + instances = 0 if instances > 0: unused_elastic_load_balancers.pop() break diff --git a/shared/iam_audit.py b/shared/iam_audit.py index a9a33c714..8dab3a431 100644 --- a/shared/iam_audit.py +++ b/shared/iam_audit.py @@ -1,6 +1,6 @@ import json from datetime import datetime -import pyjq +import jq as pyjq import traceback import re import os.path diff --git a/shared/nodes.py b/shared/nodes.py index 8f16e629c..042c1fddc 100644 --- a/shared/nodes.py +++ b/shared/nodes.py @@ -22,7 +22,7 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- """ -import pyjq +import jq as pyjq from abc import ABCMeta from netaddr import IPNetwork, IPAddress from six import add_metaclass diff --git a/shared/public.py b/shared/public.py index 5e290cff9..456e049b4 100644 --- a/shared/public.py +++ b/shared/public.py @@ -1,7 +1,7 @@ from __future__ import print_function import json import os -import pyjq +import jq as pyjq from shared.nodes import Account, Region, is_public_ip from commands.prepare import build_data_structure @@ -97,9 +97,12 @@ def get_public_nodes(account, config, use_cache=False): # Find the node at the other end of this edge target = {"arn": edge["target"], "account": account["name"]} - target_node = pyjq.first( - '.[].data|select(.id=="{}")'.format(target["arn"]), network, {} - ) + try: + target_node = pyjq.first( + '.[].data|select(.id=="{}")'.format(target["arn"]), network + ) + except StopIteration: + target_node = {} # Depending on the type of node, identify what the IP or hostname is if target_node["type"] == "elb": @@ -141,9 +144,12 @@ def get_public_nodes(account, config, use_cache=False): # Check if any protocol is allowed (indicated by IpProtocol == -1) ingress = pyjq.all(".[]", edge.get("node_data", {})) - sg_group_allowing_all_protocols = pyjq.first( - '.[]|select(.IpPermissions[]?|.IpProtocol=="-1")|.GroupId', ingress, None - ) + try: + sg_group_allowing_all_protocols = pyjq.first( + '.[]|select(.IpPermissions[]?|.IpProtocol=="-1")|.GroupId', ingress + ) + except StopIteration: + sg_group_allowing_all_protocols = None public_sgs = {} if sg_group_allowing_all_protocols is not None: warnings.append( diff --git a/tests/unit/test_prepare.py b/tests/unit/test_prepare.py index 6a093fe22..d72f10a31 100644 --- a/tests/unit/test_prepare.py +++ b/tests/unit/test_prepare.py @@ -26,7 +26,7 @@ import unittest from mock import patch from nose.tools import assert_equal, assert_true, assert_false -import pyjq +import jq as pyjq from commands.prepare import is_external_cidr, get_ec2s, get_vpcs, build_data_structure from shared.nodes import Account, Region, Subnet, Vpc From 08f3b60e2d4144a7fcc32633575f494b7be0b154 Mon Sep 17 00:00:00 2001 From: Alfonso Alongi Date: Wed, 8 Oct 2025 12:55:16 +0200 Subject: [PATCH 2/2] Migrate unit tests to nose2 for Python 3.12 compatibility --- .coveragerc | 14 +++++++++++ requirements.txt | 2 +- tests/scripts/unit_tests.sh | 8 +------ tests/unit/test_audit.py | 3 +-- tests/unit/test_common.py | 11 ++++----- tests/unit/test_find_unused.py | 13 +++++----- tests/unit/test_iam_audit.py | 3 +-- tests/unit/test_nodes.py | 27 ++++++++++----------- tests/unit/test_prepare.py | 43 +++++++++++++++++----------------- tests/unit/test_public.py | 23 +++++++++--------- unittest.cfg | 9 +++++++ 11 files changed, 83 insertions(+), 73 deletions(-) create mode 100644 .coveragerc create mode 100644 unittest.cfg diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..ab8ebdbd3 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,14 @@ +[run] +source = + shared + commands.prepare + +[report] +fail_under = 60 +show_missing = True +include = + commands/prepare.py + shared/* + +[html] +directory = htmlcov diff --git a/requirements.txt b/requirements.txt index ea83520a3..e9e047ff6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,7 +22,7 @@ matplotlib>=3.5.0,<3.9.0 mccabe==0.6.1 mock==4.0.3 netaddr==0.8.0 -nose==1.3.7 +nose2>=0.13.0,<0.14.0 pandas>=1.3.4 parliament==1.5.2 Pillow>=9.5.0,<10.0.0; python_version<'3.8' diff --git a/tests/scripts/unit_tests.sh b/tests/scripts/unit_tests.sh index 280f180bc..ec9eb249e 100755 --- a/tests/scripts/unit_tests.sh +++ b/tests/scripts/unit_tests.sh @@ -3,10 +3,4 @@ if [ -f .coverage ]; then rm .coverage fi -python -m nose tests/unit \ ---with-coverage \ ---cover-package=commands \ ---cover-package=shared \ ---cover-min-percentage=60 \ ---cover-html \ ---cover-html-dir=htmlcov \ No newline at end of file +python -m nose2 --config unittest.cfg -v diff --git a/tests/unit/test_audit.py b/tests/unit/test_audit.py index a12413db9..1941740aa 100644 --- a/tests/unit/test_audit.py +++ b/tests/unit/test_audit.py @@ -1,6 +1,5 @@ import unittest import json -from nose.tools import assert_equal, assert_true, assert_false from shared.common import parse_arguments from shared.audit import audit @@ -18,7 +17,7 @@ def test_audit(self): print(finding) issue_ids.add(finding.issue_id) - assert_equal( + self.assertEqual( issue_ids, set( [ diff --git a/tests/unit/test_common.py b/tests/unit/test_common.py index bf44ec733..ca071d095 100644 --- a/tests/unit/test_common.py +++ b/tests/unit/test_common.py @@ -1,6 +1,5 @@ import unittest import argparse -from nose.tools import assert_equal, assert_true, assert_false from shared.common import ( make_list, @@ -14,8 +13,8 @@ class TestCommon(unittest.TestCase): def test_make_list(self): - assert_equal(["hello"], make_list("hello")) - assert_equal(["hello"], make_list(["hello"])) + self.assertEqual(["hello"], make_list("hello")) + self.assertEqual(["hello"], make_list(["hello"])) def test_parse_arguments(self): parser = argparse.ArgumentParser() @@ -29,17 +28,17 @@ def test_parse_arguments(self): ["--json", "--accounts", "demo", "--config", "config.json.demo"], parser ) - assert_equal(args.json, True) + self.assertEqual(args.json, True) def test_get_account_stats(self): account = get_account("demo") stats = get_account_stats(account, True) - assert_equal(stats["EC2 instances"]["us-east-1"], 3) + self.assertEqual(stats["EC2 instances"]["us-east-1"], 3) def test_get_collection_date(self): account = get_account("demo") - assert_equal("2019-05-07T15:40:22+00:00", get_collection_date(account)) + self.assertEqual("2019-05-07T15:40:22+00:00", get_collection_date(account)) # def test_get_access_advisor_active_counts(self): # account = get_account("demo") diff --git a/tests/unit/test_find_unused.py b/tests/unit/test_find_unused.py index a1c1a0f5d..1ea523f17 100644 --- a/tests/unit/test_find_unused.py +++ b/tests/unit/test_find_unused.py @@ -4,7 +4,6 @@ from unittest import TestCase, mock from unittest.mock import MagicMock -from nose.tools import assert_equal, assert_true, assert_false class TestFindUnused(TestCase): @@ -41,7 +40,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_elastic_ips - assert_equal(find_unused_elastic_ips(self.mock_region), []) + self.assertEqual(find_unused_elastic_ips(self.mock_region), []) def test_find_unused_elastic_ips(self): def mocked_query_side_effect(account, query, region): @@ -74,7 +73,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_elastic_ips - assert_equal( + self.assertEqual( find_unused_elastic_ips(self.mock_region), [{"id": "eipalloc-2", "ip": "2.3.4.5"}], ) @@ -137,7 +136,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_volumes - assert_equal(find_unused_volumes(self.mock_region), [{"id": "vol-2222"}]) + self.assertEqual(find_unused_volumes(self.mock_region), [{"id": "vol-2222"}]) def test_find_unused_security_groups(self): def mocked_query_side_effect(account, query, region): @@ -198,7 +197,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_security_groups - assert_equal( + self.assertEqual( find_unused_security_groups(self.mock_region), [ { @@ -271,7 +270,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_network_interfaces - assert_equal( + self.assertEqual( find_unused_network_interfaces(self.mock_region), [{"id": "eni-00000001"}], ) @@ -355,7 +354,7 @@ def mocked_query_side_effect(account, query, region): mock_query.side_effect = mocked_query_side_effect from shared.find_unused import find_unused_elastic_load_balancers - assert_equal( + self.assertEqual( find_unused_elastic_load_balancers(self.mock_region), [{"LoadBalancerName": "some-elb", 'Type': 'classic'}], ) diff --git a/tests/unit/test_iam_audit.py b/tests/unit/test_iam_audit.py index f4473934f..71b6139ac 100644 --- a/tests/unit/test_iam_audit.py +++ b/tests/unit/test_iam_audit.py @@ -1,6 +1,5 @@ import unittest import json -from nose.tools import assert_equal, assert_true, assert_false from shared.iam_audit import find_admins from shared.common import parse_arguments @@ -15,4 +14,4 @@ def test_find_admins(self): findings = set() admins = find_admins(accounts, args, findings) - assert_equal(admins, [{"account": "demo", "name": "bad_role", "type": "role"}]) + self.assertEqual(admins, [{"account": "demo", "name": "bad_role", "type": "role"}]) diff --git a/tests/unit/test_nodes.py b/tests/unit/test_nodes.py index 7a2a4f345..80b827d7d 100644 --- a/tests/unit/test_nodes.py +++ b/tests/unit/test_nodes.py @@ -24,7 +24,6 @@ """ import unittest -from nose.tools import assert_equal, assert_true, assert_false from shared.nodes import truncate, get_name, is_public_ip, Account @@ -33,28 +32,28 @@ class TestNodes(unittest.TestCase): """Test class for nodes""" def test_truncate(self): - assert_equal("hello", truncate("hello")) - assert_equal( + self.assertEqual("hello", truncate("hello")) + self.assertEqual( "012345678900123456789001234567890012345..", truncate("0123456789001234567890012345678900123456789001234567890"), ) def test_is_public_ip(self): - assert_true(is_public_ip("1.1.1.1")) - assert_false(is_public_ip("10.0.0.0")) + self.assertTrue(is_public_ip("1.1.1.1")) + self.assertFalse(is_public_ip("10.0.0.0")) def test_Account(self): json_blob = {u"id": 111111111111, u"name": u"prod"} account = Account(None, json_blob) - assert_equal(111111111111, account.local_id) - assert_equal("prod", account.name) - assert_equal("account", account.node_type) - assert_equal("arn:aws:::111111111111:", account.arn) - assert_false(account.isLeaf) - assert_equal("prod", get_name(json_blob, "name")) - assert_false(account.has_leaves) - assert_equal([], account.leaves) - assert_equal( + self.assertEqual(111111111111, account.local_id) + self.assertEqual("prod", account.name) + self.assertEqual("account", account.node_type) + self.assertEqual("arn:aws:::111111111111:", account.arn) + self.assertFalse(account.isLeaf) + self.assertEqual("prod", get_name(json_blob, "name")) + self.assertFalse(account.has_leaves) + self.assertEqual([], account.leaves) + self.assertEqual( { "data": { "node_data": {"id": 111111111111, "name": "prod"}, diff --git a/tests/unit/test_prepare.py b/tests/unit/test_prepare.py index d72f10a31..443269d90 100644 --- a/tests/unit/test_prepare.py +++ b/tests/unit/test_prepare.py @@ -25,7 +25,6 @@ import unittest from mock import patch -from nose.tools import assert_equal, assert_true, assert_false import jq as pyjq from commands.prepare import is_external_cidr, get_ec2s, get_vpcs, build_data_structure @@ -36,8 +35,8 @@ class TestPrepare(unittest.TestCase): """Test class for prepare""" def test_is_external_cidr(self): - assert_true(is_external_cidr("1.1.1.1/32")) - assert_false(is_external_cidr("10.0.0.0/32")) + self.assertTrue(is_external_cidr("1.1.1.1/32")) + self.assertFalse(is_external_cidr("10.0.0.0/32")) def test_get_vpcs(self): # This actually uses the demo data files provided @@ -47,7 +46,7 @@ def test_get_vpcs(self): account, {"Endpoint": "ec2.us-east-1.amazonaws.com", "RegionName": "us-east-1"}, ) - assert_equal( + self.assertEqual( [ { "VpcId": "vpc-12345678", @@ -111,46 +110,46 @@ def test_build_data_structure(self): # Now check it # Check number of connections - assert_equal( + self.assertEqual( 35, len(pyjq.all('.[].data|select(.type == "edge")|keys', cytoscape_json)) ) # Check number of nodes - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "ip")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "rds")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 3, len(pyjq.all('.[].data|select(.type == "ec2")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "elb")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "elbv2")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 4, len(pyjq.all('.[].data|select(.type == "subnet")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "region")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "vpc")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "sqs")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "s3")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "redshift")|keys', cytoscape_json)), ) - assert_equal( + self.assertEqual( 1, len( pyjq.all( @@ -158,10 +157,10 @@ def test_build_data_structure(self): ) ), ) - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "lambda")|keys', cytoscape_json)) ) - assert_equal( + self.assertEqual( 1, len(pyjq.all('.[].data|select(.type == "ecs")|keys', cytoscape_json)) ) @@ -170,7 +169,7 @@ def test_build_data_structure(self): cytoscape_json = build_data_structure(json_blob, config, outputfilter) # Check number of connections - assert_equal( + self.assertEqual( 19, len(pyjq.all('.[].data|select(.type == "edge")|keys', cytoscape_json)) ) @@ -180,7 +179,7 @@ def test_build_data_structure(self): cytoscape_json = build_data_structure(json_blob, config, outputfilter) # Check number of connections - assert_equal( + self.assertEqual( 2, len(pyjq.all('.[].data|select(.type == "az")|keys', cytoscape_json)) ) @@ -190,6 +189,6 @@ def test_build_data_structure(self): cytoscape_json = build_data_structure(json_blob, config, outputfilter) # Check number of connections - assert_equal( + self.assertEqual( 3, len(pyjq.all('.[].data|select(.type == "ec2")|keys', cytoscape_json)) ) diff --git a/tests/unit/test_public.py b/tests/unit/test_public.py index 75ba5522a..b881b7a37 100644 --- a/tests/unit/test_public.py +++ b/tests/unit/test_public.py @@ -1,6 +1,5 @@ import unittest import json -from nose.tools import assert_equal, assert_true, assert_false from shared.public import regroup_ranges, port_ranges_string, get_public_nodes from shared.common import parse_arguments @@ -8,13 +7,13 @@ class TestPublic(unittest.TestCase): def test_regroup_ranges(self): - assert_equal(regroup_ranges([[80, 80], [80, 80]]), [(80, 80)]) - assert_equal(regroup_ranges([[80, 80], [0, 65000]]), [(0, 65000)]) - assert_equal(regroup_ranges([]), []) - assert_equal(regroup_ranges([[80, 80], [443, 443]]), [[80, 80], [443, 443]]) + self.assertEqual(regroup_ranges([[80, 80], [80, 80]]), [(80, 80)]) + self.assertEqual(regroup_ranges([[80, 80], [0, 65000]]), [(0, 65000)]) + self.assertEqual(regroup_ranges([]), []) + self.assertEqual(regroup_ranges([[80, 80], [443, 443]]), [[80, 80], [443, 443]]) def test_port_ranges_string(self): - assert_equal(port_ranges_string([[80, 80], [443, 445]]), "80,443-445") + self.assertEqual(port_ranges_string([[80, 80], [443, 445]]), "80,443-445") def test_get_public_nodes(self): args, accounts, config = parse_arguments( @@ -23,10 +22,10 @@ def test_get_public_nodes(self): public_nodes, warnings = get_public_nodes(accounts[0], config) - assert_equal(len(public_nodes), 3) + self.assertEqual(len(public_nodes), 3) # Check sizes of other items - assert_equal(len(public_nodes[0]), 6) + self.assertEqual(len(public_nodes[0]), 6) public_nodes[0] @@ -42,8 +41,8 @@ def test_get_public_nodes(self): if node["type"] == "elbv2": elbv2_nodes.append(node) - assert_equal(len(ecs_nodes), 1) - assert_equal(ecs_nodes[0]["ports"], "443") + self.assertEqual(len(ecs_nodes), 1) + self.assertEqual(ecs_nodes[0]["ports"], "443") - assert_equal(len(elb_nodes), 1) - assert_equal(len(elbv2_nodes), 1) + self.assertEqual(len(elb_nodes), 1) + self.assertEqual(len(elbv2_nodes), 1) diff --git a/unittest.cfg b/unittest.cfg new file mode 100644 index 000000000..4041b763d --- /dev/null +++ b/unittest.cfg @@ -0,0 +1,9 @@ +[unittest] +start-dir = tests/unit +plugins = nose2.plugins.coverage + +[coverage] +always-on = True +coverage-report = + term-missing + html