Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/agent-gateway/internal/handler/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ func waitForEnvelope(
}
}

func gatewayErrorStatus(errResp *gatewayv1.ErrorResponse) int {
func GatewayErrorStatus(errResp *gatewayv1.ErrorResponse) int {
if errResp == nil {
return http.StatusBadGateway
}
switch errResp.GetCode() {
case http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusConflict:
switch int(errResp.GetCode()) {
case http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusConflict:
return int(errResp.GetCode())
default:
return http.StatusBadGateway
Expand Down
29 changes: 29 additions & 0 deletions crates/agent-gateway/internal/handler/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package handler

import (
"net/http"
"testing"

gatewayv1 "github.com/liveagent/agent-gateway/internal/proto/v1"
)

func TestGatewayErrorStatusPassesExpectedClientErrors(t *testing.T) {
t.Parallel()

cases := map[int32]int{
http.StatusBadRequest: http.StatusBadRequest,
http.StatusUnauthorized: http.StatusUnauthorized,
http.StatusForbidden: http.StatusForbidden,
http.StatusNotFound: http.StatusNotFound,
http.StatusConflict: http.StatusConflict,
http.StatusTeapot: http.StatusBadGateway,
0: http.StatusBadGateway,
}

for code, want := range cases {
got := GatewayErrorStatus(&gatewayv1.ErrorResponse{Code: code})
if got != want {
t.Fatalf("GatewayErrorStatus(%d) = %d, want %d", code, got, want)
}
}
}
2 changes: 1 addition & 1 deletion crates/agent-gateway/internal/handler/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func ImportReadableFiles(
return
}
if errResp := env.GetError(); errResp != nil {
writeError(w, gatewayErrorStatus(errResp), errResp.GetMessage())
writeError(w, GatewayErrorStatus(errResp), errResp.GetMessage())
return
}

Expand Down
13 changes: 1 addition & 12 deletions crates/agent-gateway/internal/server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ func publicHistoryShare(cfg *config.Config, sm *session.Manager) http.HandlerFun
return
}
if errResp := response.GetError(); errResp != nil {
status := http.StatusInternalServerError
if isPublicHistoryShareNotFound(errResp.GetMessage()) {
status = http.StatusNotFound
}
writePublicHistoryShareError(w, status, errResp.GetMessage())
writePublicHistoryShareError(w, handler.GatewayErrorStatus(errResp), errResp.GetMessage())
return
}

Expand All @@ -130,13 +126,6 @@ func publicHistoryShare(cfg *config.Config, sm *session.Manager) http.HandlerFun
}
}

func isPublicHistoryShareNotFound(message string) bool {
normalized := strings.TrimSpace(message)
return strings.Contains(normalized, "分享链接不存在或已关闭") ||
strings.Contains(normalized, "分享 token 不能为空") ||
strings.Contains(normalized, "未找到对应的历史对话")
}

func writePublicHistoryShareError(w http.ResponseWriter, status int, message string) {
writeJSON(w, status, map[string]any{
"error": strings.TrimSpace(message),
Expand Down
31 changes: 26 additions & 5 deletions crates/agent-gateway/internal/server/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,29 @@ func TestPublicHistoryShareResolvesWithoutAuthorization(t *testing.T) {
}

func TestPublicHistoryShareReturnsNotFoundForDisabledToken(t *testing.T) {
status := publicHistoryShareErrorStatusForTest(t, http.StatusNotFound, "分享链接不存在或已关闭")
if status != http.StatusNotFound {
t.Fatalf("expected status %d, got %d", http.StatusNotFound, status)
}
}

func TestPublicHistoryShareReturnsBadRequestFromAgentCode(t *testing.T) {
status := publicHistoryShareErrorStatusForTest(t, http.StatusBadRequest, "分享 token 不能为空")
if status != http.StatusBadRequest {
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, status)
}
}

func TestPublicHistoryShareDoesNotInferStatusFromLegacyMessage(t *testing.T) {
status := publicHistoryShareErrorStatusForTest(t, http.StatusInternalServerError, "分享链接不存在或已关闭")
if status != http.StatusBadGateway {
t.Fatalf("expected status %d, got %d", http.StatusBadGateway, status)
}
}

func publicHistoryShareErrorStatusForTest(t *testing.T, code int, message string) int {
t.Helper()

sm := session.NewManager()
sm.RecordAuthentication("desktop-agent", "0.9.0", "session-1")
agentSession := session.NewAgentSession(sm.LatestAuthSnapshot())
Expand Down Expand Up @@ -151,8 +174,8 @@ func TestPublicHistoryShareReturnsNotFoundForDisabledToken(t *testing.T) {
Timestamp: time.Now().Unix(),
Payload: &gatewayv1.AgentEnvelope_Error{
Error: &gatewayv1.ErrorResponse{
Code: http.StatusNotFound,
Message: "分享链接不存在或已关闭",
Code: int32(code),
Message: message,
},
},
})
Expand All @@ -162,9 +185,7 @@ func TestPublicHistoryShareReturnsNotFoundForDisabledToken(t *testing.T) {
case <-time.After(time.Second):
t.Fatal("timed out waiting for public share response")
}
if rec.Code != http.StatusNotFound {
t.Fatalf("expected status %d, got %d body %s", http.StatusNotFound, rec.Code, rec.Body.String())
}
return rec.Code
}

func TestPublicHistoryShareReturnsUnavailableWhenAgentOffline(t *testing.T) {
Expand Down
Loading
Loading