HarshitaSuri commited on
Commit
5a7bf3a
Β·
verified Β·
1 Parent(s): f9489dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +257 -28
app.py CHANGED
@@ -1,36 +1,265 @@
1
  import gradio as gr
2
- import json
3
  import requests
4
- from jsonschema import validate, ValidationError
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- def run_tests(file):
7
- results = []
8
- with open(file.name, "r") as f:
9
- test_cases = json.load(f)
 
 
 
 
 
 
 
10
 
11
- for case in test_cases:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  try:
13
- response = requests.request(
14
- method=case["method"],
15
- url=f"http://your-api-domain.com{case['endpoint']}", # Replace with your deployed .NET API URL
16
- json=case["payload"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  )
18
- validate(instance=response.json(), schema=case["expected_schema"])
19
- results.append(f"{case['endpoint']} βœ… Passed")
20
- except ValidationError as e:
21
- results.append(f"{case['endpoint']} ❌ Failed: {e.message}")
22
- except Exception as e:
23
- results.append(f"{case['endpoint']} ❌ Error: {str(e)}")
24
-
25
- return "\n".join(results)
26
-
27
- demo = gr.Interface(
28
- fn=run_tests,
29
- inputs=gr.File(label="Upload Test Case JSON"),
30
- outputs="text",
31
- title="AI-Powered Test Runner",
32
- description="Upload a JSON file with test cases to validate your .NET/React API."
33
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
 
35
  if __name__ == "__main__":
36
- demo.launch()
 
1
  import gradio as gr
2
+ import pandas as pd
3
  import requests
4
+ import json
5
+ from sentence_transformers import SentenceTransformer
6
+ from sklearn.metrics.pairwise import cosine_similarity
7
+ import numpy as np
8
+ from typing import Dict, List, Tuple
9
+ import io
10
+
11
+ # Load semantic similarity model
12
+ model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
13
+
14
+ def calculate_semantic_similarity(text1: str, text2: str) -> float:
15
+ """Calculate semantic similarity between two texts using AI embeddings"""
16
+ embeddings = model.encode([text1, text2])
17
+ similarity = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0]
18
+ return float(similarity)
19
+
20
+ def is_empty_response(response):
21
+ """Check if response is empty or null"""
22
+ if response is None or response == '':
23
+ return True
24
+ if isinstance(response, str):
25
+ return response.strip() in ['', 'null', 'undefined']
26
+ return False
27
 
28
+ def validate_json_response(expected, actual) -> Tuple[bool, str]:
29
+ """Validate if JSON responses match"""
30
+ try:
31
+ expected_json = json.loads(expected) if isinstance(expected, str) else expected
32
+ actual_json = json.loads(actual) if isinstance(actual, str) else actual
33
+
34
+ if expected_json == actual_json:
35
+ return True, "Exact match"
36
+ return False, "JSON structure mismatch"
37
+ except:
38
+ return False, "Invalid JSON"
39
 
40
+ def run_single_test(endpoint: str, method: str, bearer_token: str,
41
+ expected_status: int, expected_body: str) -> Dict:
42
+ """Run a single API test with AI-powered semantic validation"""
43
+ result = {
44
+ 'Endpoint': endpoint,
45
+ 'Method': method,
46
+ 'Expected Status': expected_status,
47
+ 'Actual Status': None,
48
+ 'Expected Body': expected_body[:100] + '...' if len(expected_body) > 100 else expected_body,
49
+ 'Actual Body': None,
50
+ 'Outcome': 'FAIL',
51
+ 'Similarity': None,
52
+ 'Notes': ''
53
+ }
54
+
55
+ try:
56
+ # Prepare request
57
+ headers = {}
58
+ if bearer_token and bearer_token.strip():
59
+ headers['Authorization'] = f'Bearer {bearer_token.strip()}'
60
+
61
+ # Make API call
62
+ if method.upper() == 'GET':
63
+ response = requests.get(endpoint, headers=headers, timeout=10)
64
+ elif method.upper() == 'POST':
65
+ response = requests.post(endpoint, headers=headers, timeout=10)
66
+ elif method.upper() == 'PUT':
67
+ response = requests.put(endpoint, headers=headers, timeout=10)
68
+ elif method.upper() == 'DELETE':
69
+ response = requests.delete(endpoint, headers=headers, timeout=10)
70
+ else:
71
+ result['Notes'] = f'Unsupported method: {method}'
72
+ return result
73
+
74
+ result['Actual Status'] = response.status_code
75
+ actual_body = response.text
76
+ result['Actual Body'] = actual_body[:100] + '...' if len(actual_body) > 100 else actual_body
77
+
78
+ # Check status code
79
+ status_match = (response.status_code == expected_status)
80
+
81
+ if not status_match:
82
+ result['Outcome'] = 'FAIL'
83
+ result['Notes'] = f'Status mismatch: expected {expected_status}, got {response.status_code}'
84
+ return result
85
+
86
+ # Status matches - now check body
87
+ if not expected_body or is_empty_response(expected_body):
88
+ result['Outcome'] = 'PASS'
89
+ result['Notes'] = 'Status matches, no body validation'
90
+ return result
91
+
92
+ if is_empty_response(actual_body):
93
+ result['Outcome'] = 'UNCERTAIN'
94
+ result['Similarity'] = 0.0
95
+ result['Notes'] = 'Response is empty (manual review needed)'
96
+ return result
97
+
98
+ # Try JSON validation first
99
+ json_match, json_reason = validate_json_response(expected_body, actual_body)
100
+ if json_match:
101
+ result['Outcome'] = 'PASS'
102
+ result['Similarity'] = 1.0
103
+ result['Notes'] = 'Exact JSON match'
104
+ return result
105
+
106
+ # Use AI semantic similarity
107
  try:
108
+ similarity = calculate_semantic_similarity(expected_body, actual_body)
109
+ result['Similarity'] = round(similarity, 3)
110
+
111
+ if similarity >= 0.75:
112
+ result['Outcome'] = 'PASS'
113
+ result['Notes'] = f'High semantic similarity: {similarity:.1%}'
114
+ elif similarity >= 0.5:
115
+ result['Outcome'] = 'UNCERTAIN'
116
+ result['Notes'] = f'Medium semantic similarity: {similarity:.1%} (manual review needed)'
117
+ else:
118
+ result['Outcome'] = 'UNCERTAIN'
119
+ result['Notes'] = f'Low semantic similarity: {similarity:.1%} (manual review needed)'
120
+ except Exception as sim_error:
121
+ result['Outcome'] = 'UNCERTAIN'
122
+ result['Notes'] = f'Cannot compute similarity: {str(sim_error)}'
123
+
124
+ return result
125
+
126
+ except requests.exceptions.Timeout:
127
+ result['Notes'] = 'Request timeout'
128
+ return result
129
+ except requests.exceptions.ConnectionError:
130
+ result['Notes'] = 'Connection error'
131
+ return result
132
+ except Exception as e:
133
+ result['Notes'] = f'Error: {str(e)}'
134
+ return result
135
+
136
+ def process_excel_file(file, bearer_token: str) -> Tuple[pd.DataFrame, str]:
137
+ """Process uploaded Excel file and run all tests"""
138
+ try:
139
+ # Read Excel file
140
+ df = pd.read_excel(file.name)
141
+
142
+ # Validate columns
143
+ required_columns = ['Endpoint', 'Method', 'Expected Status Code', 'Expected Response Body']
144
+ missing_columns = [col for col in required_columns if col not in df.columns]
145
+
146
+ if missing_columns:
147
+ return None, f"❌ Missing columns: {', '.join(missing_columns)}"
148
+
149
+ # Run tests
150
+ results = []
151
+ for idx, row in df.iterrows():
152
+ result = run_single_test(
153
+ endpoint=str(row['Endpoint']),
154
+ method=str(row['Method']),
155
+ bearer_token=bearer_token,
156
+ expected_status=int(row['Expected Status Code']),
157
+ expected_body=str(row['Expected Response Body'])
158
+ )
159
+ results.append(result)
160
+
161
+ # Create results DataFrame
162
+ results_df = pd.DataFrame(results)
163
+
164
+ # Calculate summary
165
+ total = len(results_df)
166
+ passed = len(results_df[results_df['Outcome'] == 'PASS'])
167
+ failed = len(results_df[results_df['Outcome'] == 'FAIL'])
168
+ uncertain = len(results_df[results_df['Outcome'] == 'UNCERTAIN'])
169
+
170
+ summary = f"""
171
+ πŸ“Š **Test Summary**
172
+ - Total Tests: {total}
173
+ - βœ… Passed: {passed}
174
+ - ❌ Failed: {failed}
175
+ - ⚠️ Uncertain: {uncertain}
176
+ """
177
+
178
+ return results_df, summary
179
+
180
+ except Exception as e:
181
+ return None, f"❌ Error processing file: {str(e)}"
182
+
183
+ def download_results(df):
184
+ """Convert DataFrame to Excel for download"""
185
+ if df is None or df.empty:
186
+ return None
187
+
188
+ # Create Excel file in memory
189
+ output = io.BytesIO()
190
+ with pd.ExcelWriter(output, engine='openpyxl') as writer:
191
+ df.to_excel(writer, index=False, sheet_name='Test Results')
192
+
193
+ output.seek(0)
194
+ return output
195
+
196
+ # Create Gradio interface
197
+ with gr.Blocks(title="API Test Runner with AI", theme=gr.themes.Soft()) as app:
198
+ gr.Markdown("""
199
+ # πŸš€ API Test Runner with AI-Powered Validation
200
+
201
+ Upload an Excel file with your API test cases and get automated validation with semantic similarity analysis.
202
+
203
+ **Excel Format Required:**
204
+ - `Endpoint`: API URL
205
+ - `Method`: GET, POST, PUT, DELETE
206
+ - `Expected Status Code`: HTTP status code (e.g., 200, 404)
207
+ - `Expected Response Body`: Expected response text/JSON
208
+ """)
209
+
210
+ with gr.Row():
211
+ with gr.Column(scale=2):
212
+ file_input = gr.File(label="Upload Excel File (.xlsx)", file_types=['.xlsx'])
213
+ token_input = gr.Textbox(
214
+ label="Bearer Token (Optional)",
215
+ placeholder="Enter your API bearer token if required",
216
+ type="password"
217
  )
218
+ run_button = gr.Button("πŸ” Run Tests", variant="primary", size="lg")
219
+
220
+ with gr.Column(scale=1):
221
+ summary_output = gr.Markdown(label="Summary")
222
+
223
+ with gr.Row():
224
+ results_output = gr.Dataframe(
225
+ label="Test Results",
226
+ headers=['Endpoint', 'Method', 'Expected Status', 'Actual Status',
227
+ 'Outcome', 'Similarity', 'Notes'],
228
+ interactive=False
229
+ )
230
+
231
+ with gr.Row():
232
+ download_button = gr.Button("πŸ’Ύ Download Results")
233
+ download_output = gr.File(label="Download Excel Report")
234
+
235
+ # Event handlers
236
+ run_button.click(
237
+ fn=process_excel_file,
238
+ inputs=[file_input, token_input],
239
+ outputs=[results_output, summary_output]
240
+ )
241
+
242
+ download_button.click(
243
+ fn=download_results,
244
+ inputs=[results_output],
245
+ outputs=[download_output]
246
+ )
247
+
248
+ # Examples
249
+ gr.Markdown("""
250
+ ### πŸ“ Example Excel Format:
251
+
252
+ | Endpoint | Method | Expected Status Code | Expected Response Body |
253
+ |----------|--------|---------------------|------------------------|
254
+ | https://api.example.com/users | GET | 200 | [{"id":1,"name":"John"}] |
255
+ | https://api.example.com/posts | POST | 201 | {"success":true} |
256
+
257
+ ### 🎯 Test Outcomes:
258
+ - **PASS** (Green): Status matches & high similarity (β‰₯75%)
259
+ - **UNCERTAIN** (Yellow): Status matches but body similarity is medium (50-75%) or low (<50%)
260
+ - **FAIL** (Red): Status code mismatch
261
+ """)
262
 
263
+ # Launch the app
264
  if __name__ == "__main__":
265
+ app.launch()