ai-testing-swarm 0.1.11__tar.gz → 0.1.13__tar.gz

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.
Files changed (33) hide show
  1. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/PKG-INFO +1 -1
  2. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/pyproject.toml +1 -1
  3. ai_testing_swarm-0.1.13/src/ai_testing_swarm/__init__.py +1 -0
  4. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/llm_reasoning_agent.py +66 -0
  5. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/release_gate_agent.py +9 -1
  6. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/orchestrator.py +15 -1
  7. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm.egg-info/PKG-INFO +1 -1
  8. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm.egg-info/SOURCES.txt +1 -0
  9. ai_testing_swarm-0.1.13/tests/test_policy_expected_negatives.py +34 -0
  10. ai_testing_swarm-0.1.11/src/ai_testing_swarm/__init__.py +0 -1
  11. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/README.md +0 -0
  12. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/setup.cfg +0 -0
  13. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/__init__.py +0 -0
  14. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/execution_agent.py +0 -0
  15. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/learning_agent.py +0 -0
  16. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/test_planner_agent.py +0 -0
  17. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/test_writer_agent.py +0 -0
  18. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/agents/ui_agent.py +0 -0
  19. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/cli.py +0 -0
  20. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/__init__.py +0 -0
  21. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/api_client.py +0 -0
  22. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/config.py +0 -0
  23. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/curl_parser.py +0 -0
  24. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/openai_client.py +0 -0
  25. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/openapi_loader.py +0 -0
  26. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/core/safety.py +0 -0
  27. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/reporting/__init__.py +0 -0
  28. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm/reporting/report_writer.py +0 -0
  29. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm.egg-info/dependency_links.txt +0 -0
  30. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm.egg-info/entry_points.txt +0 -0
  31. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/src/ai_testing_swarm.egg-info/top_level.txt +0 -0
  32. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/tests/test_openapi_loader.py +0 -0
  33. {ai_testing_swarm-0.1.11 → ai_testing_swarm-0.1.13}/tests/test_swarm_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-testing-swarm
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: AI-powered testing swarm
5
5
  Author-email: Arif Shah <ashah7775@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ai-testing-swarm"
7
- version = "0.1.11"
7
+ version = "0.1.13"
8
8
  description = "AI-powered testing swarm"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1 @@
1
+ __version__ = "0.1.13"
@@ -73,13 +73,33 @@ class LLMReasoningAgent:
73
73
  # =========================================================
74
74
  # 3️⃣ HTTP-LEVEL HARD RULES (NO AI)
75
75
  # =========================================================
76
+ # Method misuse tests: 404/405 are expected (endpoint not available for that method)
77
+ if status_code in (404, 405):
78
+ if (
79
+ isinstance(test_name, str)
80
+ and test_name.startswith("wrong_method_")
81
+ ) or (mutation and mutation.get("strategy") == "method_misuse"):
82
+ return {
83
+ "type": "method_not_allowed" if status_code == 405 else "not_found",
84
+ "confidence": 1.0,
85
+ "explanation": f"HTTP {status_code} for wrong-method negative test (expected)"
86
+ }
87
+
76
88
  if status_code == 405:
89
+ # 405 outside method-misuse tests can still be informative
77
90
  return {
78
91
  "type": "method_not_allowed",
79
92
  "confidence": 1.0,
80
93
  "explanation": "HTTP 405 Method Not Allowed"
81
94
  }
82
95
 
96
+ if status_code == 404:
97
+ return {
98
+ "type": "not_found",
99
+ "confidence": 0.9,
100
+ "explanation": "HTTP 404 Not Found"
101
+ }
102
+
83
103
  if status_code in (401, 403):
84
104
  return {
85
105
  "type": "auth_issue",
@@ -100,6 +120,30 @@ class LLMReasoningAgent:
100
120
  if mutation:
101
121
  strategy = mutation.get("strategy")
102
122
 
123
+ # Header tampering / content negotiation tests
124
+ if status_code == 406 and strategy == "headers":
125
+ return {
126
+ "type": "content_negotiation",
127
+ "confidence": 1.0,
128
+ "explanation": "406 Not Acceptable after Accept/header mutation (expected)"
129
+ }
130
+
131
+ # Header tampering accepted (2xx) can be risky depending on the mutation
132
+ if 200 <= status_code < 300 and strategy == "headers":
133
+ return {
134
+ "type": "headers_accepted",
135
+ "confidence": 0.8,
136
+ "explanation": "Header mutation still returned 2xx (review if expected)"
137
+ }
138
+
139
+ # Wrong-method negative test accepted => risk
140
+ if 200 <= status_code < 300 and strategy == "method_misuse":
141
+ return {
142
+ "type": "method_risk",
143
+ "confidence": 1.0,
144
+ "explanation": "Wrong HTTP method was accepted with 2xx (unexpected)"
145
+ }
146
+
103
147
  if status_code == 400 and strategy == "missing_param":
104
148
  return {
105
149
  "type": "missing_param",
@@ -107,6 +151,14 @@ class LLMReasoningAgent:
107
151
  "explanation": "400 response after required parameter removal"
108
152
  }
109
153
 
154
+ # Missing param accepted (2xx) is often weak validation (risky)
155
+ if 200 <= status_code < 300 and strategy == "missing_param":
156
+ return {
157
+ "type": "missing_param_accepted",
158
+ "confidence": 1.0,
159
+ "explanation": "Parameter removal still returned 2xx (potential missing validation)"
160
+ }
161
+
110
162
  if status_code == 400 and strategy == "null_param":
111
163
  return {
112
164
  "type": "missing_param",
@@ -114,6 +166,13 @@ class LLMReasoningAgent:
114
166
  "explanation": "400 response after nullifying parameter"
115
167
  }
116
168
 
169
+ if 200 <= status_code < 300 and strategy == "null_param":
170
+ return {
171
+ "type": "null_param_accepted",
172
+ "confidence": 1.0,
173
+ "explanation": "Nullified parameter still returned 2xx (potential weak validation)"
174
+ }
175
+
117
176
  if status_code == 400 and strategy == "invalid_param":
118
177
  return {
119
178
  "type": "invalid_param",
@@ -121,6 +180,13 @@ class LLMReasoningAgent:
121
180
  "explanation": "400 response after invalid parameter mutation"
122
181
  }
123
182
 
183
+ if 200 <= status_code < 300 and strategy == "invalid_param":
184
+ return {
185
+ "type": "invalid_param_accepted",
186
+ "confidence": 1.0,
187
+ "explanation": "Invalid parameter still returned 2xx (potential weak validation)"
188
+ }
189
+
124
190
  # ✅ FIX: security payload blocked → SAFE
125
191
  if status_code >= 400 and status_code < 500 and strategy == "security":
126
192
  return {
@@ -90,12 +90,17 @@ class ReleaseGateAgent:
90
90
  "infra", # infra / network / 5xx
91
91
  "security_risk", # malicious payload succeeded (2xx/3xx)
92
92
  "server_error",
93
- "method_not_allowed",
93
+ # NOTE: method_not_allowed is expected for negative tests (wrong method)
94
94
  }
95
95
 
96
96
  # ⚠️ Ambiguous behavior (release with caution)
97
97
  RISKY_FAILURES = {
98
98
  "unknown",
99
+ "missing_param_accepted",
100
+ "null_param_accepted",
101
+ "invalid_param_accepted",
102
+ "headers_accepted",
103
+ "method_risk",
99
104
  }
100
105
 
101
106
  # ✅ EXPECTED & HEALTHY system behavior
@@ -104,6 +109,9 @@ class ReleaseGateAgent:
104
109
  "missing_param",
105
110
  "invalid_param",
106
111
  "security", # 🔥 IMPORTANT: security blocked = SAFE
112
+ "method_not_allowed",
113
+ "not_found",
114
+ "content_negotiation",
107
115
  }
108
116
 
109
117
  def decide(self, results: list) -> str:
@@ -14,6 +14,18 @@ EXPECTED_FAILURES = {
14
14
  "missing_param",
15
15
  "invalid_param",
16
16
  "security",
17
+ "method_not_allowed",
18
+ "not_found",
19
+ "content_negotiation",
20
+ }
21
+
22
+ RISKY_FAILURES = {
23
+ "unknown",
24
+ "missing_param_accepted",
25
+ "null_param_accepted",
26
+ "invalid_param_accepted",
27
+ "headers_accepted",
28
+ "method_risk",
17
29
  }
18
30
 
19
31
  class SwarmOrchestrator:
@@ -61,7 +73,9 @@ class SwarmOrchestrator:
61
73
  "confidence": classification.get("confidence", 1.0),
62
74
  "failure_type": classification.get("type"),
63
75
  "status": (
64
- "PASSED" if classification.get("type") in EXPECTED_FAILURES else "FAILED"
76
+ "PASSED" if classification.get("type") in EXPECTED_FAILURES
77
+ else "RISK" if classification.get("type") in RISKY_FAILURES
78
+ else "FAILED"
65
79
  ),
66
80
  })
67
81
  # Optional learning step
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-testing-swarm
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: AI-powered testing swarm
5
5
  Author-email: Arif Shah <ashah7775@gmail.com>
6
6
  License: MIT
@@ -26,4 +26,5 @@ src/ai_testing_swarm/core/safety.py
26
26
  src/ai_testing_swarm/reporting/__init__.py
27
27
  src/ai_testing_swarm/reporting/report_writer.py
28
28
  tests/test_openapi_loader.py
29
+ tests/test_policy_expected_negatives.py
29
30
  tests/test_swarm_api.py
@@ -0,0 +1,34 @@
1
+ from ai_testing_swarm.agents.llm_reasoning_agent import LLMReasoningAgent
2
+ from ai_testing_swarm.agents.release_gate_agent import ReleaseGateAgent
3
+
4
+
5
+ def test_wrong_method_405_is_expected_not_blocking():
6
+ reasoner = LLMReasoningAgent()
7
+
8
+ exec_result = {
9
+ "name": "wrong_method_POST",
10
+ "mutation": {"strategy": "method_misuse"},
11
+ "response": {"status_code": 405, "elapsed_ms": 10, "body_snippet": ""},
12
+ }
13
+
14
+ cls = reasoner.reason(exec_result)
15
+ assert cls["type"] == "method_not_allowed"
16
+
17
+ # Gate should not reject release if happy path passes and only expected negatives exist
18
+ gate = ReleaseGateAgent()
19
+ decision = gate.decide([
20
+ {"name": "happy_path", "failure_type": "success", "response": {"status_code": 200}},
21
+ {"name": "wrong_method_POST", "failure_type": cls["type"], "response": {"status_code": 405}},
22
+ ])
23
+ assert decision in ("APPROVE_RELEASE", "APPROVE_RELEASE_WITH_RISKS")
24
+
25
+
26
+ def test_accept_header_406_is_expected():
27
+ reasoner = LLMReasoningAgent()
28
+ exec_result = {
29
+ "name": "headers_accept_text",
30
+ "mutation": {"strategy": "headers"},
31
+ "response": {"status_code": 406, "elapsed_ms": 10, "body_snippet": ""},
32
+ }
33
+ cls = reasoner.reason(exec_result)
34
+ assert cls["type"] == "content_negotiation"
@@ -1 +0,0 @@
1
- __version__ = "0.1.11"