Re-upload: CI infrastructure issue resolved, all tests verified passing
This commit is contained in:
185
http_log_explorer/analyzers/diff_engine.py
Normal file
185
http_log_explorer/analyzers/diff_engine.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""Diff engine for comparing HTTP entries."""
|
||||
|
||||
import difflib
|
||||
|
||||
from http_log_explorer.models import DiffResult, HTTPEntry
|
||||
|
||||
|
||||
class DiffEngine:
|
||||
"""Engine for comparing HTTP request/response pairs."""
|
||||
|
||||
def diff(self, entry1: HTTPEntry, entry2: HTTPEntry) -> DiffResult:
|
||||
"""Compare two HTTP entries.
|
||||
|
||||
Args:
|
||||
entry1: First HTTPEntry
|
||||
entry2: Second HTTPEntry
|
||||
|
||||
Returns:
|
||||
DiffResult with differences
|
||||
"""
|
||||
result = DiffResult(
|
||||
entry1_id=entry1.id,
|
||||
entry2_id=entry2.id,
|
||||
)
|
||||
|
||||
result.url_changed = entry1.request.url != entry2.request.url
|
||||
|
||||
result.status_changed = entry1.response.status != entry2.response.status
|
||||
result.status1 = entry1.response.status
|
||||
result.status2 = entry2.response.status
|
||||
|
||||
result.request_headers_diff = self.headers_diff(
|
||||
entry1.request.headers,
|
||||
entry2.request.headers,
|
||||
)
|
||||
|
||||
result.response_headers_diff = self.headers_diff(
|
||||
entry1.response.headers,
|
||||
entry2.response.headers,
|
||||
)
|
||||
|
||||
result.request_body_diff = self.body_diff(
|
||||
entry1.request.body,
|
||||
entry2.request.body,
|
||||
)
|
||||
|
||||
result.response_body_diff = self.body_diff(
|
||||
entry1.response.body,
|
||||
entry2.response.body,
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def headers_diff(
|
||||
self, headers1: dict[str, str], headers2: dict[str, str]
|
||||
) -> list[str]:
|
||||
"""Compare two header dictionaries.
|
||||
|
||||
Args:
|
||||
headers1: First headers dict
|
||||
headers2: Second headers dict
|
||||
|
||||
Returns:
|
||||
List of diff lines
|
||||
"""
|
||||
all_keys = set(headers1.keys()) | set(headers2.keys())
|
||||
diff_lines: list[str] = []
|
||||
|
||||
for key in sorted(all_keys):
|
||||
val1 = headers1.get(key)
|
||||
val2 = headers2.get(key)
|
||||
|
||||
if val1 != val2:
|
||||
if val1 is None:
|
||||
diff_lines.append(f"+ {key}: {val2}")
|
||||
elif val2 is None:
|
||||
diff_lines.append(f"- {key}: {val1}")
|
||||
else:
|
||||
diff_lines.append(f"- {key}: {val1}")
|
||||
diff_lines.append(f"+ {key}: {val2}")
|
||||
|
||||
return diff_lines
|
||||
|
||||
def body_diff(
|
||||
self, body1: str | None, body2: str | None
|
||||
) -> list[str]:
|
||||
"""Compare two body strings.
|
||||
|
||||
Args:
|
||||
body1: First body
|
||||
body2: Second body
|
||||
|
||||
Returns:
|
||||
List of diff lines (unified format)
|
||||
"""
|
||||
if body1 == body2:
|
||||
return []
|
||||
|
||||
b1 = body1 or ""
|
||||
b2 = body2 or ""
|
||||
|
||||
lines1 = b1.splitlines(keepends=True)
|
||||
lines2 = b2.splitlines(keepends=True)
|
||||
|
||||
if not lines1 and not lines2:
|
||||
return []
|
||||
|
||||
diff = list(difflib.unified_diff(
|
||||
lines1,
|
||||
lines2,
|
||||
fromfile="before",
|
||||
tofile="after",
|
||||
lineterm="",
|
||||
))
|
||||
|
||||
return diff
|
||||
|
||||
def unified_diff_output(self, diff_result: DiffResult) -> str:
|
||||
"""Generate a human-readable unified diff output.
|
||||
|
||||
Args:
|
||||
diff_result: The diff result
|
||||
|
||||
Returns:
|
||||
Formatted string with all differences
|
||||
"""
|
||||
lines: list[str] = []
|
||||
lines.append(f"=== Diff: {diff_result.entry1_id} vs {diff_result.entry2_id} ===")
|
||||
lines.append("")
|
||||
|
||||
if diff_result.url_changed:
|
||||
lines.append(f"URL changed: {diff_result.url_changed}")
|
||||
|
||||
if diff_result.status_changed:
|
||||
lines.append(f"Status: {diff_result.status1} -> {diff_result.status2}")
|
||||
|
||||
if diff_result.request_headers_diff:
|
||||
lines.append("")
|
||||
lines.append("--- Request Headers ---")
|
||||
lines.extend(diff_result.request_headers_diff)
|
||||
|
||||
if diff_result.request_body_diff:
|
||||
lines.append("")
|
||||
lines.append("--- Request Body ---")
|
||||
lines.extend(diff_result.request_body_diff)
|
||||
|
||||
if diff_result.response_headers_diff:
|
||||
lines.append("")
|
||||
lines.append("--- Response Headers ---")
|
||||
lines.extend(diff_result.response_headers_diff)
|
||||
|
||||
if diff_result.response_body_diff:
|
||||
lines.append("")
|
||||
lines.append("--- Response Body ---")
|
||||
lines.extend(diff_result.response_body_diff)
|
||||
|
||||
if not any([
|
||||
diff_result.url_changed,
|
||||
diff_result.status_changed,
|
||||
diff_result.request_headers_diff,
|
||||
diff_result.request_body_diff,
|
||||
diff_result.response_headers_diff,
|
||||
diff_result.response_body_diff,
|
||||
]):
|
||||
lines.append("No differences found.")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def has_differences(self, diff_result: DiffResult) -> bool:
|
||||
"""Check if there are any differences.
|
||||
|
||||
Args:
|
||||
diff_result: The diff result
|
||||
|
||||
Returns:
|
||||
True if there are any differences
|
||||
"""
|
||||
return bool(
|
||||
diff_result.url_changed
|
||||
or diff_result.status_changed
|
||||
or diff_result.request_headers_diff
|
||||
or diff_result.request_body_diff
|
||||
or diff_result.response_headers_diff
|
||||
or diff_result.response_body_diff
|
||||
)
|
||||
Reference in New Issue
Block a user