@macaw-ai/client 0.0.0-stage → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +339 -0
- package/bin/install.js +422 -0
- package/package.json +37 -4
- package/payload/README.md +56 -0
- package/payload/secureAI/LICENSE +190 -0
- package/payload/secureAI/NOTICE +5 -0
- package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
- package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
- package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
- package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
- package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
- package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
- package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
- package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
- package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
- package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
- package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
- package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
- package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
- package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
- package/payload/secureAI/examples/README.md +244 -0
- package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
- package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
- package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
- package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
- package/payload/secureAI/examples/attestations/README.md +128 -0
- package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
- package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
- package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
- package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
- package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
- package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
- package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
- package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
- package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
- package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
- package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
- package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
- package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
- package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
- package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
- package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
- package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
- package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
- package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
- package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
- package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
- package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
- package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
- package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
- package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
- package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
- package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
- package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
- package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
- package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
- package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
- package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
- package/payload/secureAI/macaw_adapters/__init__.py +55 -0
- package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
- package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
- package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
- package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
- package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
- package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
- package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
- package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
- package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
- package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
- package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
- package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
- package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
- package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
- package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
- package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
- package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
- package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
- package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
- package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
- package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
- package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
- package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
- package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
- package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
- package/payload/secureAI/test_harness_quick.py +296 -0
- package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
- package/README.md +0 -3
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MACAW Quick Test Harness
|
|
4
|
+
|
|
5
|
+
Runs MCP + 1a examples only. No Identity Provider required.
|
|
6
|
+
For full test suite (including multi-user examples), use test_harness.py
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python3 test_harness_quick.py
|
|
10
|
+
python3 test_harness_quick.py --verbose
|
|
11
|
+
|
|
12
|
+
Prerequisites:
|
|
13
|
+
- MACAW SDK installed (pip install macaw-client macaw-adapters)
|
|
14
|
+
- OPENAI_API_KEY and/or ANTHROPIC_API_KEY for LLM examples
|
|
15
|
+
- MCP examples run without any API keys
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import subprocess
|
|
21
|
+
import time
|
|
22
|
+
import signal
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from typing import Optional, List
|
|
27
|
+
from enum import Enum
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestResult(Enum):
|
|
31
|
+
PASS = "PASS"
|
|
32
|
+
FAIL = "FAIL"
|
|
33
|
+
SKIP = "SKIP"
|
|
34
|
+
TIMEOUT = "TIMEOUT"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class TestCase:
|
|
39
|
+
name: str
|
|
40
|
+
script: Path
|
|
41
|
+
category: str
|
|
42
|
+
requires_server: Optional[Path] = None
|
|
43
|
+
requires_keys: List[str] = field(default_factory=list)
|
|
44
|
+
timeout: int = 120
|
|
45
|
+
result: TestResult = TestResult.SKIP
|
|
46
|
+
output: str = ""
|
|
47
|
+
error: str = ""
|
|
48
|
+
duration: float = 0
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Quick tests - no IdP required
|
|
52
|
+
QUICK_TESTS = {
|
|
53
|
+
# MCP examples - no API keys needed, just MACAW
|
|
54
|
+
"mcp/1a_simple_invocation.py": {
|
|
55
|
+
"requires_server": "mcp/securemcp_calculator.py",
|
|
56
|
+
"timeout": 120,
|
|
57
|
+
},
|
|
58
|
+
"mcp/1b_discovery_and_resources.py": {
|
|
59
|
+
"requires_server": "mcp/securemcp_calculator.py",
|
|
60
|
+
"timeout": 120,
|
|
61
|
+
},
|
|
62
|
+
"mcp/1c_logging_client.py": {
|
|
63
|
+
"requires_server": "mcp/securemcp_calculator.py",
|
|
64
|
+
"timeout": 120,
|
|
65
|
+
},
|
|
66
|
+
"mcp/1d_progress_client.py": {
|
|
67
|
+
"requires_server": "mcp/securemcp_calculator.py",
|
|
68
|
+
"timeout": 120,
|
|
69
|
+
},
|
|
70
|
+
"mcp/1e_sampling_client.py": {
|
|
71
|
+
"requires_server": "mcp/1e_sampling_server.py",
|
|
72
|
+
"timeout": 120,
|
|
73
|
+
},
|
|
74
|
+
"mcp/1g_roots_client.py": {
|
|
75
|
+
"requires_server": "mcp/1g_roots_server.py",
|
|
76
|
+
"timeout": 120,
|
|
77
|
+
},
|
|
78
|
+
# 1a examples - need API keys but no IdP
|
|
79
|
+
"openai/openai_1a_dropin_simple.py": {
|
|
80
|
+
"requires_keys": ["OPENAI_API_KEY"],
|
|
81
|
+
"timeout": 120,
|
|
82
|
+
},
|
|
83
|
+
"anthropic/anthropic_1a_dropin_simple.py": {
|
|
84
|
+
"requires_keys": ["ANTHROPIC_API_KEY"],
|
|
85
|
+
"timeout": 120,
|
|
86
|
+
},
|
|
87
|
+
"langchain/langchain_1a_dropin_simple.py": {
|
|
88
|
+
"requires_keys": ["OPENAI_API_KEY"],
|
|
89
|
+
"timeout": 120,
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class QuickTestHarness:
|
|
95
|
+
def __init__(self, examples_dir: Path, verbose: bool = False):
|
|
96
|
+
self.examples_dir = examples_dir
|
|
97
|
+
self.verbose = verbose
|
|
98
|
+
self.python = Path(sys.executable)
|
|
99
|
+
self.timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
100
|
+
self.results_dir = Path(f"/tmp/macaw-quick-{self.timestamp}")
|
|
101
|
+
self.test_cases: List[TestCase] = []
|
|
102
|
+
|
|
103
|
+
def discover_tests(self):
|
|
104
|
+
"""Discover quick test cases."""
|
|
105
|
+
for script_rel, config in QUICK_TESTS.items():
|
|
106
|
+
script_path = self.examples_dir / script_rel
|
|
107
|
+
if script_path.exists():
|
|
108
|
+
category = script_rel.split("/")[0]
|
|
109
|
+
server_path = None
|
|
110
|
+
if config.get("requires_server"):
|
|
111
|
+
server_path = self.examples_dir / config["requires_server"]
|
|
112
|
+
|
|
113
|
+
tc = TestCase(
|
|
114
|
+
name=script_rel,
|
|
115
|
+
script=script_path,
|
|
116
|
+
category=category,
|
|
117
|
+
requires_server=server_path,
|
|
118
|
+
requires_keys=config.get("requires_keys", []),
|
|
119
|
+
timeout=config.get("timeout", 60),
|
|
120
|
+
)
|
|
121
|
+
self.test_cases.append(tc)
|
|
122
|
+
|
|
123
|
+
def _check_keys(self, test: TestCase) -> tuple:
|
|
124
|
+
"""Check if required API keys are available."""
|
|
125
|
+
missing = []
|
|
126
|
+
for key in test.requires_keys:
|
|
127
|
+
if not os.environ.get(key):
|
|
128
|
+
missing.append(key)
|
|
129
|
+
return len(missing) == 0, missing
|
|
130
|
+
|
|
131
|
+
def _get_env(self, test: TestCase) -> dict:
|
|
132
|
+
"""Build environment variables for test execution."""
|
|
133
|
+
env = os.environ.copy()
|
|
134
|
+
env["PYTHONPATH"] = str(self.examples_dir.parent)
|
|
135
|
+
return env
|
|
136
|
+
|
|
137
|
+
def run_test(self, test: TestCase) -> TestCase:
|
|
138
|
+
"""Run a single test case."""
|
|
139
|
+
# Check for required keys
|
|
140
|
+
has_keys, missing = self._check_keys(test)
|
|
141
|
+
if not has_keys:
|
|
142
|
+
test.result = TestResult.SKIP
|
|
143
|
+
test.error = f"Missing: {', '.join(missing)}"
|
|
144
|
+
return test
|
|
145
|
+
|
|
146
|
+
env = self._get_env(test)
|
|
147
|
+
start_time = time.time()
|
|
148
|
+
server_proc = None
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
# Start server if required
|
|
152
|
+
if test.requires_server and test.requires_server.exists():
|
|
153
|
+
server_proc = subprocess.Popen(
|
|
154
|
+
[str(self.python), str(test.requires_server)],
|
|
155
|
+
env=env,
|
|
156
|
+
stdout=subprocess.PIPE,
|
|
157
|
+
stderr=subprocess.PIPE,
|
|
158
|
+
preexec_fn=os.setsid,
|
|
159
|
+
)
|
|
160
|
+
time.sleep(20) # Wait for server to register (AWS registration latency)
|
|
161
|
+
|
|
162
|
+
if server_proc.poll() is not None:
|
|
163
|
+
test.result = TestResult.FAIL
|
|
164
|
+
test.error = "Server failed to start"
|
|
165
|
+
return test
|
|
166
|
+
|
|
167
|
+
# Run the test
|
|
168
|
+
result = subprocess.run(
|
|
169
|
+
[str(self.python), str(test.script)],
|
|
170
|
+
env=env,
|
|
171
|
+
capture_output=True,
|
|
172
|
+
timeout=test.timeout,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
test.duration = time.time() - start_time
|
|
176
|
+
test.output = result.stdout.decode("utf-8", errors="replace")
|
|
177
|
+
test.error = result.stderr.decode("utf-8", errors="replace")
|
|
178
|
+
|
|
179
|
+
if result.returncode == 0:
|
|
180
|
+
test.result = TestResult.PASS
|
|
181
|
+
else:
|
|
182
|
+
test.result = TestResult.FAIL
|
|
183
|
+
|
|
184
|
+
except subprocess.TimeoutExpired:
|
|
185
|
+
test.result = TestResult.TIMEOUT
|
|
186
|
+
test.duration = test.timeout
|
|
187
|
+
test.error = f"Timeout after {test.timeout}s"
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
test.result = TestResult.FAIL
|
|
191
|
+
test.error = str(e)
|
|
192
|
+
test.duration = time.time() - start_time
|
|
193
|
+
|
|
194
|
+
finally:
|
|
195
|
+
# Cleanup server
|
|
196
|
+
if server_proc:
|
|
197
|
+
try:
|
|
198
|
+
os.killpg(os.getpgid(server_proc.pid), signal.SIGTERM)
|
|
199
|
+
server_proc.wait(timeout=5)
|
|
200
|
+
except Exception:
|
|
201
|
+
try:
|
|
202
|
+
os.killpg(os.getpgid(server_proc.pid), signal.SIGKILL)
|
|
203
|
+
except Exception:
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
return test
|
|
207
|
+
|
|
208
|
+
def run_all(self):
|
|
209
|
+
"""Run all test cases."""
|
|
210
|
+
self.results_dir.mkdir(parents=True, exist_ok=True)
|
|
211
|
+
|
|
212
|
+
for category in ["mcp", "openai", "anthropic", "langchain"]:
|
|
213
|
+
category_tests = [t for t in self.test_cases if t.category == category]
|
|
214
|
+
if not category_tests:
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
print(f"\n{category.upper()} ({len(category_tests)} tests)")
|
|
218
|
+
print("-" * 40)
|
|
219
|
+
|
|
220
|
+
for test in category_tests:
|
|
221
|
+
self.run_test(test)
|
|
222
|
+
status = test.result.value
|
|
223
|
+
if test.result == TestResult.PASS:
|
|
224
|
+
print(f" [PASS] {test.name} ({test.duration:.1f}s)")
|
|
225
|
+
elif test.result == TestResult.SKIP:
|
|
226
|
+
print(f" [SKIP] {test.name} - {test.error}")
|
|
227
|
+
elif test.result == TestResult.TIMEOUT:
|
|
228
|
+
print(f" [TIME] {test.name} - {test.error}")
|
|
229
|
+
else:
|
|
230
|
+
print(f" [FAIL] {test.name} ({test.duration:.1f}s)")
|
|
231
|
+
if self.verbose and test.error:
|
|
232
|
+
for line in test.error.strip().split("\n")[-5:]:
|
|
233
|
+
print(f" {line}")
|
|
234
|
+
|
|
235
|
+
def print_summary(self):
|
|
236
|
+
"""Print test summary."""
|
|
237
|
+
passed = len([t for t in self.test_cases if t.result == TestResult.PASS])
|
|
238
|
+
failed = len([t for t in self.test_cases if t.result == TestResult.FAIL])
|
|
239
|
+
skipped = len([t for t in self.test_cases if t.result == TestResult.SKIP])
|
|
240
|
+
timeout = len([t for t in self.test_cases if t.result == TestResult.TIMEOUT])
|
|
241
|
+
total = len(self.test_cases)
|
|
242
|
+
|
|
243
|
+
print("\n" + "=" * 60)
|
|
244
|
+
print("QUICK TEST SUMMARY")
|
|
245
|
+
print("=" * 60)
|
|
246
|
+
print(f" Passed: {passed}/{total}")
|
|
247
|
+
print(f" Failed: {failed}")
|
|
248
|
+
print(f" Skipped: {skipped}")
|
|
249
|
+
print(f" Timeout: {timeout}")
|
|
250
|
+
print("=" * 60)
|
|
251
|
+
|
|
252
|
+
if skipped > 0:
|
|
253
|
+
print("\nSkipped tests need API keys:")
|
|
254
|
+
print(" export OPENAI_API_KEY=sk-...")
|
|
255
|
+
print(" export ANTHROPIC_API_KEY=sk-ant-...")
|
|
256
|
+
|
|
257
|
+
if failed > 0 or timeout > 0:
|
|
258
|
+
print("\nFor full output, run with --verbose")
|
|
259
|
+
|
|
260
|
+
print(f"\nFor multi-user examples (requires IdP), run:")
|
|
261
|
+
print(f" python test_harness.py")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def main():
|
|
265
|
+
verbose = "--verbose" in sys.argv or "-v" in sys.argv
|
|
266
|
+
|
|
267
|
+
# Find examples directory
|
|
268
|
+
script_dir = Path(__file__).parent
|
|
269
|
+
examples_dir = script_dir / "examples"
|
|
270
|
+
|
|
271
|
+
if not examples_dir.exists():
|
|
272
|
+
print(f"Error: examples directory not found at {examples_dir}")
|
|
273
|
+
sys.exit(1)
|
|
274
|
+
|
|
275
|
+
print("=" * 60)
|
|
276
|
+
print("MACAW Quick Test Harness")
|
|
277
|
+
print("=" * 60)
|
|
278
|
+
print(f"Examples: {examples_dir}")
|
|
279
|
+
print(f"Python: {sys.executable}")
|
|
280
|
+
print(f"Tests: MCP + 1a examples (no IdP required)")
|
|
281
|
+
|
|
282
|
+
harness = QuickTestHarness(examples_dir, verbose=verbose)
|
|
283
|
+
harness.discover_tests()
|
|
284
|
+
|
|
285
|
+
print(f"\nFound {len(harness.test_cases)} quick tests")
|
|
286
|
+
|
|
287
|
+
harness.run_all()
|
|
288
|
+
harness.print_summary()
|
|
289
|
+
|
|
290
|
+
# Exit with error if any tests failed
|
|
291
|
+
failed = len([t for t in harness.test_cases if t.result == TestResult.FAIL])
|
|
292
|
+
sys.exit(1 if failed > 0 else 0)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
main()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/README.md
DELETED