TestSwaggerCoverage.filter_rules()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
1
import json
2
import unittest
3
import warnings
4
5
from server import server
6
7
8
class TestSwaggerCoverage(unittest.TestCase):
9
    @classmethod
10
    def setUpClass(cls):
11
        cls.client = server.test_client()
12
        cls.BANNED_RULES = [
13
            '/spec',
14
            '/apidocs',
15
            '/static'
16
        ]
17
        cls.THRESHOLD = 100
18
19
    def test_swagger_coverage(self):
20
        """ The swagger coverage should be 100% """
21
22
        self.color_print('yellow', '\n##### SWAGGER COVERAGE #####')
23
24
        swagger_specs = self.retrieve_swagger_specs()
25
        rules = self.filter_rules(self.client.application.url_map.iter_rules())
26
27
        (
28
            covered_methods,
29
            methods_total
30
        ) = self.retrieve_covered_methods_number(rules, swagger_specs)
31
        coverage = int(100 * covered_methods / methods_total)
32
33
        end_color = 'green' if coverage == 100 else 'yellow'
34
        self.color_print(
35
            end_color,
36
            '%s of %s routes are swagged' % (
37
                str(covered_methods),
38
                str(methods_total)
39
            )
40
        )
41
42
        self.assertTrue(coverage >= self.THRESHOLD)
43
44
    def retrieve_swagger_specs(self):
45
        """ Fetch the swagger specs """
46
47
        # suppress flasgger unclosed file warnings
48
        with warnings.catch_warnings():
49
            warnings.filterwarnings("ignore", message="unclosed file")
50
            response = self.client.get('application/spec')
51
        response_json = json.loads(response.data.decode('utf-8'))
52
        swagger_specs = response_json['paths']
53
54
        return swagger_specs
55
56
    def retrieve_covered_methods_number(self, rules, swagger_specs):
57
        """ Return the number of covered methods and the number of methods """
58
        methods_total = 0
59
        covered_methods = 0
60
        for rule in rules:
61
            methods = self.filter_methods(rule.methods)
62
            parsed_rule = self.format_rule(rule)
63
            for method in methods:
64
                methods_total += 1
65
                try:
66
                    swagger_specs[parsed_rule][method.lower()]
67
                    covered_methods += 1
68
                except KeyError:
69
                    self.color_print(
70
                        'red',
71
                        'Uncovered: %s of route %s' % (method, parsed_rule)
72
                    )
73
                    continue
74
75
        return (covered_methods, methods_total)
76
77
    @staticmethod
78
    def filter_methods(methods):
79
        """ Filter methods OPTIONS and HEAD """
80
        return [
81
            method
82
            for method in methods
83
            if method not in ['OPTIONS', 'HEAD']
84
        ]
85
86
    @staticmethod
87
    def format_rule(rule):
88
        """
89
        Format the rule for swagger
90
        Replace '<' with '{' and '>' with '}'
91
        Remove arguments type
92
        """
93
        return (
94
            str(rule)
95
            .replace('<', '{')
96
            .replace('>', '}')
97
            .replace('string:', '')
98
            .replace('int:', '')
99
        )
100
101
    def filter_rules(self, rules):
102
        """ Filter rules that do not need to be documented """
103
        return [
104
            rule
105
            for rule in rules
106
            if not self.is_banned_rule(rule)
107
        ]
108
109
    def is_banned_rule(self, rule):
110
        """ Check if rule should be banned """
111
        return any(
112
            banned_rule in str(rule)
113
            for banned_rule in self.BANNED_RULES
114
        )
115
116
    @staticmethod
117
    def color_print(color, message):
118
        """ Print message with some nice color """
119
        colors = {
120
            'green': '\033[92m',
121
            'yellow': '\033[93m',
122
            'red': '\033[91m',
123
            'endc': '\033[0m',
124
        }
125
126
        print(colors[color] + message + colors['endc'])
127
128
129
if __name__ == '__main__':
130
    unittest.main()
131