Completed
Push — master ( bd562f...40b34b )
by Antoine
28s
created

TestSwaggerCoverage.is_banned_rule()   A

Complexity

Conditions 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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