feat: initial release, refs NOISSUE
Some checks failed
Upload Python Package / Create Release (push) Successful in 37s
Upload Python Package / deploy (push) Failing after 38s

This commit is contained in:
2026-04-02 01:42:26 +02:00
parent 0b1384279d
commit e824475872
44 changed files with 4435 additions and 30 deletions

View File

@@ -0,0 +1,400 @@
"""Test logging utility for validating agent responses and system outputs."""
import re
from typing import Optional, Dict, Any, List
from datetime import datetime
# Color codes for terminal output
class Colors:
GREEN = '\033[92m'
RED = '\033[91m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
CYAN = '\033[96m'
RESET = '\033[0m'
class TestLogger:
"""Utility class for logging test results and assertions."""
def __init__(self):
self.assertions: List[Dict[str, Any]] = []
self.errors: List[Dict[str, Any]] = []
self.logs: List[str] = []
def log(self, message: str, level: str = 'INFO') -> None:
"""Log an informational message."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
formatted = f"[{timestamp}] [{level}] {message}"
self.logs.append(formatted)
print(formatted)
def success(self, message: str) -> None:
"""Log a success message with green color."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
formatted = f"{Colors.GREEN}[{timestamp}] [✓ PASS] {message}{Colors.RESET}"
self.logs.append(formatted)
print(formatted)
def error(self, message: str) -> None:
"""Log an error message with red color."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
formatted = f"{Colors.RED}[{timestamp}] [✗ ERROR] {message}{Colors.RESET}"
self.logs.append(formatted)
print(formatted)
def warning(self, message: str) -> None:
"""Log a warning message with yellow color."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
formatted = f"{Colors.YELLOW}[{timestamp}] [!] WARN {message}{Colors.RESET}"
self.logs.append(formatted)
print(formatted)
def info(self, message: str) -> None:
"""Log an info message with blue color."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
formatted = f"{Colors.BLUE}[{timestamp}] [ INFO] {message}{Colors.RESET}"
self.logs.append(formatted)
print(formatted)
def assert_contains(self, text: str, expected: str, message: str = '') -> bool:
"""Assert that text contains expected substring."""
try:
contains = expected in text
if contains:
self.success(f"'{expected}' found in text")
self.assertions.append({
'type': 'assert_contains',
'result': 'pass',
'expected': expected,
'message': message or f"'{expected}' in text"
})
return True
else:
self.error(f"✗ Expected '{expected}' not found in text")
self.assertions.append({
'type': 'assert_contains',
'result': 'fail',
'expected': expected,
'message': message or f"'{expected}' in text"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_contains',
'result': 'error',
'expected': expected,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_not_contains(self, text: str, unexpected: str, message: str = '') -> bool:
"""Assert that text does not contain expected substring."""
try:
contains = unexpected in text
if not contains:
self.success(f"'{unexpected}' not found in text")
self.assertions.append({
'type': 'assert_not_contains',
'result': 'pass',
'unexpected': unexpected,
'message': message or f"'{unexpected}' not in text"
})
return True
else:
self.error(f"✗ Unexpected '{unexpected}' found in text")
self.assertions.append({
'type': 'assert_not_contains',
'result': 'fail',
'unexpected': unexpected,
'message': message or f"'{unexpected}' not in text"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_not_contains',
'result': 'error',
'unexpected': unexpected,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_equal(self, actual: str, expected: str, message: str = '') -> bool:
"""Assert that two strings are equal."""
try:
if actual == expected:
self.success(f"✓ Strings equal")
self.assertions.append({
'type': 'assert_equal',
'result': 'pass',
'expected': expected,
'message': message or f"actual == expected"
})
return True
else:
self.error(f"✗ Strings not equal. Expected: '{expected}', Got: '{actual}'")
self.assertions.append({
'type': 'assert_equal',
'result': 'fail',
'expected': expected,
'actual': actual,
'message': message or "actual == expected"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_equal',
'result': 'error',
'expected': expected,
'actual': actual,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_starts_with(self, text: str, prefix: str, message: str = '') -> bool:
"""Assert that text starts with expected prefix."""
try:
starts_with = text.startswith(prefix)
if starts_with:
self.success(f"✓ Text starts with '{prefix}'")
self.assertions.append({
'type': 'assert_starts_with',
'result': 'pass',
'prefix': prefix,
'message': message or f"text starts with '{prefix}'"
})
return True
else:
self.error(f"✗ Text does not start with '{prefix}'")
self.assertions.append({
'type': 'assert_starts_with',
'result': 'fail',
'prefix': prefix,
'message': message or f"text starts with '{prefix}'"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_starts_with',
'result': 'error',
'prefix': prefix,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_ends_with(self, text: str, suffix: str, message: str = '') -> bool:
"""Assert that text ends with expected suffix."""
try:
ends_with = text.endswith(suffix)
if ends_with:
self.success(f"✓ Text ends with '{suffix}'")
self.assertions.append({
'type': 'assert_ends_with',
'result': 'pass',
'suffix': suffix,
'message': message or f"text ends with '{suffix}'"
})
return True
else:
self.error(f"✗ Text does not end with '{suffix}'")
self.assertions.append({
'type': 'assert_ends_with',
'result': 'fail',
'suffix': suffix,
'message': message or f"text ends with '{suffix}'"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_ends_with',
'result': 'error',
'suffix': suffix,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_regex(self, text: str, pattern: str, message: str = '') -> bool:
"""Assert that text matches a regex pattern."""
try:
if re.search(pattern, text):
self.success(f"✓ Regex pattern matched")
self.assertions.append({
'type': 'assert_regex',
'result': 'pass',
'pattern': pattern,
'message': message or f"text matches regex '{pattern}'"
})
return True
else:
self.error(f"✗ Regex pattern did not match")
self.assertions.append({
'type': 'assert_regex',
'result': 'fail',
'pattern': pattern,
'message': message or f"text matches regex '{pattern}'"
})
return False
except re.error as e:
self.error(f"✗ Invalid regex pattern: {e}")
self.assertions.append({
'type': 'assert_regex',
'result': 'error',
'pattern': pattern,
'message': message or f"Invalid regex: {e}"
})
return False
except Exception as ex:
self.error(f"Assertion failed with exception: {ex}")
self.assertions.append({
'type': 'assert_regex',
'result': 'error',
'pattern': pattern,
'message': message or f"Assertion failed: {ex}"
})
return False
def assert_length(self, text: str, expected_length: int, message: str = '') -> bool:
"""Assert that text has expected length."""
try:
length = len(text)
if length == expected_length:
self.success(f"✓ Length is {expected_length}")
self.assertions.append({
'type': 'assert_length',
'result': 'pass',
'expected_length': expected_length,
'message': message or f"len(text) == {expected_length}"
})
return True
else:
self.error(f"✗ Length is {length}, expected {expected_length}")
self.assertions.append({
'type': 'assert_length',
'result': 'fail',
'expected_length': expected_length,
'actual_length': length,
'message': message or f"len(text) == {expected_length}"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_length',
'result': 'error',
'expected_length': expected_length,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_key_exists(self, text: str, key: str, message: str = '') -> bool:
"""Assert that a key exists in a JSON-like text."""
try:
if f'"{key}":' in text or f"'{key}':" in text:
self.success(f"✓ Key '{key}' exists")
self.assertions.append({
'type': 'assert_key_exists',
'result': 'pass',
'key': key,
'message': message or f"key '{key}' exists"
})
return True
else:
self.error(f"✗ Key '{key}' not found")
self.assertions.append({
'type': 'assert_key_exists',
'result': 'fail',
'key': key,
'message': message or f"key '{key}' exists"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_key_exists',
'result': 'error',
'key': key,
'message': message or f"Assertion failed: {e}"
})
return False
def assert_substring_count(self, text: str, substring: str, count: int, message: str = '') -> bool:
"""Assert that substring appears count times in text."""
try:
actual_count = text.count(substring)
if actual_count == count:
self.success(f"✓ Substring appears {count} time(s)")
self.assertions.append({
'type': 'assert_substring_count',
'result': 'pass',
'substring': substring,
'expected_count': count,
'actual_count': actual_count,
'message': message or f"'{substring}' appears {count} times"
})
return True
else:
self.error(f"✗ Substring appears {actual_count} time(s), expected {count}")
self.assertions.append({
'type': 'assert_substring_count',
'result': 'fail',
'substring': substring,
'expected_count': count,
'actual_count': actual_count,
'message': message or f"'{substring}' appears {count} times"
})
return False
except Exception as e:
self.error(f"Assertion failed with exception: {e}")
self.assertions.append({
'type': 'assert_substring_count',
'result': 'error',
'substring': substring,
'expected_count': count,
'message': message or f"Assertion failed: {e}"
})
return False
def get_assertion_count(self) -> int:
"""Get total number of assertions made."""
return len(self.assertions)
def get_failure_count(self) -> int:
"""Get number of failed assertions."""
return sum(1 for assertion in self.assertions if assertion.get('result') == 'fail')
def get_success_count(self) -> int:
"""Get number of passed assertions."""
return sum(1 for assertion in self.assertions if assertion.get('result') == 'pass')
def get_logs(self) -> List[str]:
"""Get all log messages."""
return self.logs.copy()
def get_errors(self) -> List[Dict[str, Any]]:
"""Get all error records."""
return self.errors.copy()
def clear(self) -> None:
"""Clear all logs and assertions."""
self.assertions.clear()
self.errors.clear()
self.logs.clear()
def __enter__(self):
"""Context manager entry."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit."""
return False
# Convenience function for context manager usage
def test_logger():
"""Create and return a TestLogger instance."""
return TestLogger()