Completed
Branch master (71691c)
by Antoine
34s
created

retrieve_covered_methods_number()   A

Complexity

Conditions 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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