mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package contact
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
)
|
|
|
|
func TestContactFanoutErrorSummary_HTTPStatus(t *testing.T) {
|
|
err := errs.NewNetworkError(errs.SubtypeNetworkServer, `HTTP 503: {"reason":"upstream_unavailable"}`).
|
|
WithCode(503).
|
|
WithRetryable()
|
|
|
|
got := contactFanoutErrorSummary(err)
|
|
if !strings.HasPrefix(got, "HTTP 503 Service Unavailable: ") {
|
|
t.Fatalf("summary: got %q", got)
|
|
}
|
|
if !strings.Contains(got, "upstream_unavailable") {
|
|
t.Fatalf("summary should include truncated body details, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestContactInvalidResponseError_TypedInternal(t *testing.T) {
|
|
got := contactInvalidResponseError("decode contact response failed")
|
|
p, ok := errs.ProblemOf(got)
|
|
if !ok {
|
|
t.Fatalf("expected typed problem, got %T", got)
|
|
}
|
|
if p.Category != errs.CategoryInternal || p.Subtype != errs.SubtypeInvalidResponse {
|
|
t.Fatalf("problem type: got %s/%s", p.Category, p.Subtype)
|
|
}
|
|
}
|
|
|
|
func TestContactFanoutAllFailedError_PreservesTypedProblem(t *testing.T) {
|
|
err := errs.NewAPIError(errs.SubtypeRateLimit, "rate limit").
|
|
WithCode(99991663).
|
|
WithLogID("log-contact-1").
|
|
WithRetryable()
|
|
|
|
got := contactFanoutAllFailedError(err, "all 2 queries failed; first: API 99991663: rate limit (query=\"alice\")")
|
|
p, ok := errs.ProblemOf(got)
|
|
if !ok {
|
|
t.Fatalf("expected typed problem, got %T", got)
|
|
}
|
|
if p.Category != errs.CategoryAPI || p.Subtype != errs.SubtypeRateLimit {
|
|
t.Fatalf("problem type: got %s/%s", p.Category, p.Subtype)
|
|
}
|
|
if p.Code != 99991663 || p.LogID != "log-contact-1" || !p.Retryable {
|
|
t.Fatalf("problem metadata not preserved: %+v", p)
|
|
}
|
|
if !strings.Contains(p.Message, "all 2 queries failed") {
|
|
t.Fatalf("problem message not decorated: %q", p.Message)
|
|
}
|
|
// The representative error must not be mutated: it stays a single-query
|
|
// failure, while the aggregate is a distinct value carrying it as cause.
|
|
if err.Message != "rate limit" {
|
|
t.Fatalf("representative error message was mutated: %q", err.Message)
|
|
}
|
|
if !errors.Is(got, err) {
|
|
t.Fatalf("aggregate error should keep the representative failure as its cause")
|
|
}
|
|
}
|
|
|
|
func TestContactFanoutAllFailedError_UntypedGetsActionableHint(t *testing.T) {
|
|
got := contactFanoutAllFailedError(nil, "all 2 queries failed; first: internal error (query=\"alice\")")
|
|
p, ok := errs.ProblemOf(got)
|
|
if !ok {
|
|
t.Fatalf("expected typed problem, got %T", got)
|
|
}
|
|
if p.Category != errs.CategoryInternal || p.Subtype != errs.SubtypeUnknown {
|
|
t.Fatalf("problem type: got %s/%s", p.Category, p.Subtype)
|
|
}
|
|
if !strings.Contains(p.Hint, "narrow --queries") {
|
|
t.Fatalf("hint should guide recovery, got %q", p.Hint)
|
|
}
|
|
}
|