Files
replypier/backend/tests/agents/test_ticket_investigator_agent.py
T
WangShen007 54b949b392 chore(brand): rename the product to ReplyPier
The upstream project name is gone from code, assets, docs and build
output: brand strings, domains (replypier.site and its app/api/docs/help
subdomains), env prefixes, image and network names, the widget's public
JS API (window.ReplyPier, replypierId/BaseUrl/Config/Token), CSS tokens
(--rp-*), file and directory names, and the WordPress plugin package.

- widget loader is now webclient/replypier.min.js; both bundles rebuilt
  from source (backend/assets/widget.{js,css}, frontend/public/webclient)
- NOTICE, llms.txt, CONTRIBUTING, HELP_CENTER_INFRA and config.dev.js
  rewritten rather than string-swapped; the demo config no longer ships
  the previous vendor's Firebase/Razorpay/Google keys
- new migration replypier_ai_model_001 adds REPLYPIER to the managed
  AIModelType and rewrites pre-rename rows
- local compose project, database, role and volume renamed; LOCAL_DEPLOY
  documents carrying the old local data over
2026-10-26 00:51:03 +08:00

121 lines
3.8 KiB
Python

"""
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
"""
ReplyPier - Ticket investigator agent tests
"""
import pytest
from agno.exceptions import ModelProviderError, ModelRateLimitError
from app.agents.ticket_investigator import TicketInvestigatorAgent
def _agent():
return TicketInvestigatorAgent(api_key="k", model_name="gpt-4o-mini", model_type="OPENAI")
def test_a_refused_request_is_classified_as_a_provider_error():
"""Every failed run returns None, so the reason has to be recorded
separately or a hard 400 is indistinguishable from a quiet model (#303)."""
agent = _agent()
agent._note_provider_error(
ModelProviderError(
message=(
"Invalid schema for function 'search': In context="
"('properties', 'queryBody', 'additionalProperties'), schema "
"must have a 'type' key."
),
status_code=400,
)
)
assert "rejected the request (400)" in agent.last_provider_error
# The provider's own words reach the operator, whatever the vendor.
assert "Invalid schema for function 'search'" in agent.last_provider_error
assert agent.provider_errors == [agent.last_provider_error]
@pytest.mark.parametrize("status", [400, 403, 404, 422])
def test_any_client_error_counts_no_matter_the_wording(status):
"""Classification is on the status agno attaches, not on vendor phrasing —
the same failure on Anthropic or Gemini has to land the same way."""
agent = _agent()
agent._note_provider_error(ModelProviderError(message="refused", status_code=status))
assert f"({status})" in agent.last_provider_error
def test_rate_limiting_is_left_unclassified():
"""429 is transient — retrying can succeed, so it is not a configuration
problem to report."""
agent = _agent()
agent._note_provider_error(ModelRateLimitError(message="slow down"))
assert agent.last_provider_error is None
assert agent.provider_errors == []
def test_a_request_timeout_is_left_unclassified():
"""408 is the other 4xx a retry can clear."""
agent = _agent()
agent._note_provider_error(ModelProviderError(message="timeout", status_code=408))
assert agent.last_provider_error is None
def test_a_server_side_failure_is_left_unclassified():
"""agno defaults to 502 when there was no response at all (a connection
error); "no verdict" already describes that."""
agent = _agent()
agent._note_provider_error(ModelProviderError(message="connection reset"))
assert agent.last_provider_error is None
def test_a_non_provider_exception_is_ignored():
agent = _agent()
agent._note_provider_error(ValueError("something else entirely"))
assert agent.last_provider_error is None
def test_a_long_provider_message_is_capped():
agent = _agent()
agent._note_provider_error(ModelProviderError(message="x" * 5000, status_code=400))
assert len(agent.last_provider_error) < 400
assert agent.last_provider_error.endswith("…")
def test_the_same_rejection_is_recorded_once_for_the_banner():
"""Every hypothesis in the run hits the identical 400; the banner says it
once."""
agent = _agent()
error = ModelProviderError(message="refused", status_code=400)
agent._note_provider_error(error)
agent._note_provider_error(error)
assert len(agent.provider_errors) == 1