Skip to content

Commit d3a58f2

Browse files
authored
Initial version of unittest (#10)
* Initial version of unittest * Unittest of methods defined in main.py * Removed the unnecessary dependency on an external file: tf_validate.json. * Removed the unnecessary dependency on an external file: tf_validate.json. * Removed the unnecessary dependency on an external file: tf_validate.json. * Added 'pip install' to workflow * 'pip install pytest' added * 'pip install parameterized' added to workflow * str removeprefix() in main.py is not supprted in Python 3.8 * /tmp does not exist in windows
1 parent 6d974b5 commit d3a58f2

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

.github/workflows/unittest.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Run Tests
2+
3+
on: [push, pull_request]
4+
5+
jobs:
6+
unittest:
7+
runs-on: ${{ matrix.os }}
8+
strategy:
9+
matrix:
10+
os: [ubuntu-latest, windows-latest]
11+
python: ["3.9", "3.10", "3.11", "3.12"]
12+
13+
steps:
14+
- uses: actions/[email protected]
15+
- name: Setup Python
16+
uses: actions/[email protected]
17+
with:
18+
python-version: ${{ matrix.python }}
19+
- name: Install pytest and parameterized
20+
run: pip install pytest parameterized
21+
- name: Run test
22+
run: python -m unittest test_tf.py

test_tf.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import os
2+
import subprocess
3+
import unittest
4+
import pytest
5+
from parameterized import parameterized
6+
from main import get_policy_check_type, get_required_inputs, get_optional_inputs, get_sub_command
7+
from main import get_treat_findings_as_non_blocking_flag, build_command, execute_command, set_output
8+
9+
from main import CLI_POLICY_VALIDATOR, POLICY_CHECK_TYPE, VALIDATE_POLICY, CHECK_NO_NEW_ACCESS, CHECK_ACCESS_NOT_GRANTED
10+
from main import COMMON_REQUIRED_INPUTS, TREAT_FINDINGS_AS_NON_BLOCKING
11+
12+
from unittest.mock import patch
13+
14+
INVALID_POLICY_CHECK = "INVALID_POLICY_CHECK"
15+
16+
class TfpvTest(unittest.TestCase):
17+
# case 1: test_get_type_ENVIRON_NOT_SET: failure expected because required os.environ[] are not set
18+
def test_get_type_ENVIRON_NOT_SET(self):
19+
self.assertRaises(KeyError, get_policy_check_type)
20+
21+
# case 2: test_get_type_INVALID_POLICY_CHECK: failure expected because os.environ[] is set to an invalid value
22+
def test_get_type_INVALID_POLICY_CHECK(self):
23+
os.environ[POLICY_CHECK_TYPE] = INVALID_POLICY_CHECK
24+
self.assertRaises(ValueError, get_policy_check_type)
25+
26+
# case 3, 4, 5: test_get_type_WITH_VALIDATE_POLICY: success with valid POLICY_CHECK_TYPE
27+
@parameterized.expand([VALIDATE_POLICY, CHECK_NO_NEW_ACCESS, CHECK_ACCESS_NOT_GRANTED])
28+
def test_get_type_WITH_VALID_POLICY_CHECK(self, policy_check_type):
29+
os.environ[POLICY_CHECK_TYPE] = policy_check_type
30+
policy_type = get_policy_check_type()
31+
self.assertEqual(policy_type, policy_check_type)
32+
33+
# case 6: test_get_required_input_INVALID_POLICY_CHECK: failure expected because an invalid policy_check_type is provided
34+
@unittest.expectedFailure
35+
def test_get_required_input_INVALID_POLICY_CHECK(self):
36+
policy_check = INVALID_POLICY_CHECK
37+
self.assertEqual(get_required_inputs(policy_check), "")
38+
39+
# case 7, 8, 9: test_get_required_input_WITH_VALIDATE_POLICY: success as a valid POLICY_CHECK_TYPE is provided: VALIDATE_POLICY
40+
@parameterized.expand([VALIDATE_POLICY, CHECK_NO_NEW_ACCESS, CHECK_ACCESS_NOT_GRANTED])
41+
def test_get_required_input_WITH_VALID_POLICY_CHECK(self, policy_check_type):
42+
result = get_required_inputs(policy_check_type)
43+
if policy_check_type == VALIDATE_POLICY:
44+
self.assertEqual(result, {"INPUT_TEMPLATE-PATH", "INPUT_REGION"})
45+
elif policy_check_type == CHECK_NO_NEW_ACCESS:
46+
self.assertEqual(result, {"INPUT_TEMPLATE-PATH", "INPUT_REGION", "INPUT_REFERENCE-POLICY", "INPUT_REFERENCE-POLICY-TYPE"})
47+
else:
48+
self.assertEqual(result, {"INPUT_TEMPLATE-PATH", "INPUT_REGION", "INPUT_ACTIONS"})
49+
50+
# case 10: test_get_optional_input_INVALID_POLICY_CHECK: failure expected because an invalid policy_check_type is provided
51+
@unittest.expectedFailure
52+
def test_get_optional_input_INVALID_POLICY_CHECK(self):
53+
policy_check = INVALID_POLICY_CHECK
54+
result = get_optional_inputs(policy_check)
55+
assertEqual(result, {"INPUT_IGNORE-FINDING", "INPUT_ALLOW-DYNAMIC-REF-WITHOUT-VERSION", "INPUT_EXCLUDE-RESOURCE-TYPES"})
56+
57+
# case 11, 12, 13: test_get_optional_input_WITH_VALID_POLICY_CHECK: success as a valid POLICY_CHECK_TYPE is provided
58+
@parameterized.expand([VALIDATE_POLICY, CHECK_NO_NEW_ACCESS, CHECK_ACCESS_NOT_GRANTED])
59+
def test_get_optional_input_WITH_VALID_POLICY_CHECK(self, policy_check_type):
60+
result = get_optional_inputs(policy_check_type)
61+
if policy_check_type == VALIDATE_POLICY:
62+
self.assertEqual(result, {"INPUT_ALLOW-EXTERNAL-PRINCIPALS", "INPUT_TREAT-FINDING-TYPE-AS-BLOCKING", "INPUT_IGNORE-FINDING",
63+
"INPUT_ALLOW-DYNAMIC-REF-WITHOUT-VERSION", "INPUT_EXCLUDE-RESOURCE-TYPES"})
64+
elif policy_check_type == CHECK_NO_NEW_ACCESS:
65+
self.assertEqual(result, {"INPUT_IGNORE-FINDING", "INPUT_ALLOW-DYNAMIC-REF-WITHOUT-VERSION", "INPUT_EXCLUDE-RESOURCE-TYPES"})
66+
else:
67+
self.assertEqual(result, {"INPUT_IGNORE-FINDING", "INPUT_ALLOW-DYNAMIC-REF-WITHOUT-VERSION", "INPUT_EXCLUDE-RESOURCE-TYPES"})
68+
69+
# case 14: test_get_sub_command_ENVIRON_NOT_SET: failure expected because required os.environ[]s are not set
70+
def test_get_sub_command_ENVIRON_NOT_SET(self):
71+
os.environ['INPUT_REGION'] = ''
72+
required = ['INPUT_REGION']
73+
self.assertRaises(ValueError, get_sub_command, required, True)
74+
75+
# case 15: test_get_sub_command_MEET_REQUIRED_INPUTS: success as valid inputs are provided.
76+
def test_get_sub_command_MEET_REQUIRED_INPUTS(self):
77+
tft = os.getcwd() + './main.py'
78+
os.environ['INPUT_TEMPLATE-PATH'] = tft
79+
os.environ['INPUT_REGION'] = 'us-west-2'
80+
required = {"INPUT_TEMPLATE-PATH", "INPUT_REGION"}
81+
expected = ['--template-path', tft, '--region', 'us-west-2']
82+
flags = get_sub_command(required, True)
83+
self.assertEqual(set(flags), set(expected))
84+
85+
# case 16: test_get_treat_findings_as_non_blocking_flag_ENVIRON_NOT_SET: failure expected because os.environ[TREAT_FINDINGS_AS_NON_BLOCKING] is not set
86+
def test_get_treat_findings_as_non_blocking_flag_ENVIRON_NOT_SET(self):
87+
os.environ[TREAT_FINDINGS_AS_NON_BLOCKING] = ''
88+
policy_check = CHECK_NO_NEW_ACCESS
89+
self.assertRaises(ValueError, get_treat_findings_as_non_blocking_flag, policy_check)
90+
91+
# case 17: test_get_treat_findings_as_non_blocking_flag_VALIDATE_POLICY: pass
92+
def test_get_treat_findings_as_non_blocking_flag_VALIDATE_POLICY(self):
93+
policy_check = VALIDATE_POLICY
94+
result = get_treat_findings_as_non_blocking_flag(policy_check)
95+
self.assertEqual(result, "")
96+
97+
# case 18: test_get_treat_findings_as_non_blocking_flag_CHECK_ACCESS_NOT_GRANTED_True: pass, a string of flag returned
98+
def test_get_treat_findings_as_non_blocking_flag_CHECK_ACCESS_NOT_GRANTED_True(self):
99+
policy_check = CHECK_ACCESS_NOT_GRANTED
100+
os.environ[TREAT_FINDINGS_AS_NON_BLOCKING] = 'True'
101+
result = get_treat_findings_as_non_blocking_flag(policy_check)
102+
self.assertEqual(result, ['--treat-findings-as-non-blocking'])
103+
104+
# case 19: test_get_treat_findings_as_non_blocking_flag_CHECK_ACCESS_NOT_GRANTED_False: pass, an empty string returned
105+
def test_get_treat_findings_as_non_blocking_flag_CHECK_ACCESS_NOT_GRANTED_False(self):
106+
policy_check = CHECK_ACCESS_NOT_GRANTED
107+
os.environ[TREAT_FINDINGS_AS_NON_BLOCKING] = 'False'
108+
result = get_treat_findings_as_non_blocking_flag(policy_check)
109+
self.assertEqual(result, "")
110+
111+
# case 20: test_build_command_VALIDATE_POLICY: pass with correct parameters.
112+
def test_build_command_VALIDATE_POLICY(self):
113+
policy_check = VALIDATE_POLICY
114+
tft = os.getcwd() + './main.py'
115+
os.environ['INPUT_TEMPLATE-PATH'] = tft
116+
os.environ['INPUT_REGION'] = 'us-west-2'
117+
os.environ['TREAT_FINDINGS_AS_NON_BLOCKING'] = 'False'
118+
os.environ['INPUT_IGNORE-FINDING'] = 'PASS_ROLE_WITH_STAR_IN_RESOURCE'
119+
required = {"INPUT_TEMPLATE-PATH", "INPUT_REGION"}
120+
optional = {"INPUT_IGNORE-FINDING"}
121+
command = build_command(policy_check, required, optional)
122+
expected = set([CLI_POLICY_VALIDATOR, 'validate', '--template-path', tft, '--region', 'us-west-2', '--ignore-finding', 'PASS_ROLE_WITH_STAR_IN_RESOURCE'])
123+
command_set = set(command)
124+
self.assertEqual(command_set, expected)
125+
126+
# case 21: test_execute_command_EXCEPTION: exception raised
127+
@patch("subprocess.run", side_effect=Exception("invalid-input"))
128+
def test_execute_command_EXCEPTION(self, mock_run):
129+
with pytest.raises(Exception) as exc:
130+
result = execute_command()
131+
assertNotEqual(str(exc.value).find("invalid-input"), -1)
132+
133+
# case 21: test_execute_command_VALIDATE: pass with mockrun
134+
@patch("main.subprocess.run")
135+
def test_execute_command_VALIDATE(self, mock_run):
136+
command = [CLI_POLICY_VALIDATOR, 'validate']
137+
expected = "pass"
138+
139+
completed_process = subprocess.CompletedProcess(args=['command', 'args'], returncode=0, stdout=expected, stderr=b'error')
140+
mock_run.return_value = completed_process
141+
142+
result = execute_command(command)
143+
self.assertEqual(result, expected)
144+
145+
# case 23: test_set_output: pass
146+
def test_set_output_WRITE_TO_FILE(self):
147+
os.environ['GITHUB_OUTPUT'] = 'o1'
148+
val = '{"BlockingFindings": [],"NonBlockingFindings": []}'
149+
set_output(val)
150+
res = subprocess.run(["grep", "BlockingFindings", "o1"], check=True, capture_output=True, encoding="utf-8").stdout
151+
subprocess.run(["rm", "o1"])
152+
self.assertNotEqual(res.find("BlockingFindings"), -1)
153+

0 commit comments

Comments
 (0)