|
2 | 2 |
|
3 | 3 | import os |
4 | 4 | import inspect |
| 5 | +import ast |
5 | 6 | from unittest.mock import Mock, patch |
6 | 7 | from lxml import etree |
7 | 8 | from typing import Dict, List, Any, Optional, Type |
@@ -104,6 +105,72 @@ def test_import(self): |
104 | 105 | """Test that service class can be imported.""" |
105 | 106 | assert self.SERVICE_CLASS is not None |
106 | 107 |
|
| 108 | + def test_no_duplicate_methods(self): |
| 109 | + """Test that there are no duplicate method implementations in service class.""" |
| 110 | + if not self.SERVICE_CLASS: |
| 111 | + return |
| 112 | + |
| 113 | + # Get the source file of the service class |
| 114 | + source_file = inspect.getsourcefile(self.SERVICE_CLASS) |
| 115 | + if not source_file: |
| 116 | + return |
| 117 | + |
| 118 | + # Parse the source file |
| 119 | + with open(source_file, "r", encoding="utf-8") as f: |
| 120 | + source_code = f.read() |
| 121 | + |
| 122 | + tree = ast.parse(source_code) |
| 123 | + |
| 124 | + # Find the class definition |
| 125 | + class_node = None |
| 126 | + for node in ast.walk(tree): |
| 127 | + if ( |
| 128 | + isinstance(node, ast.ClassDef) |
| 129 | + and node.name == self.SERVICE_CLASS.__name__ |
| 130 | + ): |
| 131 | + class_node = node |
| 132 | + break |
| 133 | + |
| 134 | + if not class_node: |
| 135 | + return |
| 136 | + |
| 137 | + # Collect all method names defined in the class |
| 138 | + method_names = [] |
| 139 | + method_line_numbers = {} |
| 140 | + |
| 141 | + for item in class_node.body: |
| 142 | + if isinstance(item, ast.FunctionDef): |
| 143 | + method_name = item.name |
| 144 | + # Skip private methods and __init__ |
| 145 | + if method_name.startswith("_") or method_name == "__init__": |
| 146 | + continue |
| 147 | + |
| 148 | + # Skip helper methods |
| 149 | + if method_name in ["type", "desc", "operations"]: |
| 150 | + continue |
| 151 | + |
| 152 | + method_names.append(method_name) |
| 153 | + |
| 154 | + # Track line numbers for better error reporting |
| 155 | + if method_name not in method_line_numbers: |
| 156 | + method_line_numbers[method_name] = [] |
| 157 | + method_line_numbers[method_name].append(item.lineno) |
| 158 | + |
| 159 | + # Find duplicates |
| 160 | + from collections import Counter |
| 161 | + |
| 162 | + method_counts = Counter(method_names) |
| 163 | + duplicates = [ |
| 164 | + f"{name}: defined {count} times at lines {method_line_numbers[name]}" |
| 165 | + for name, count in method_counts.items() |
| 166 | + if count > 1 |
| 167 | + ] |
| 168 | + |
| 169 | + assert not duplicates, ( |
| 170 | + f"Duplicate method implementations found in {self.SERVICE_CLASS.__name__}:\n" |
| 171 | + + "\n".join(duplicates) |
| 172 | + ) |
| 173 | + |
107 | 174 | def test_wsdl_operations_not_empty(self): |
108 | 175 | """Test that WSDL operations are successfully parsed and not empty.""" |
109 | 176 | wsdl_operations = self.get_wsdl_operations() |
|
0 commit comments