Files
larksuite__cli/internal/output/errors_test.go
zhaoyukun.yk 4dfe1b7dc0 feat(auth): support managed external credentials
Add Extended-only direct, credential-proxy, and platform-proxy modes while keeping Standard behavior and external-credential product details isolated behind source-neutral runtime capabilities.

Fail closed across helper execution, proxy routing, streams, managed file transfers, and unsupported event flows. Harden system configuration, edition updates, and release publication.

Cover Standard compatibility and Extended integration with contract tests, repository-local three-mode E2E, native trust checks, and CI and release gates.
2026-08-12 14:55:41 +08:00

146 lines
4.6 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package output
import (
"bytes"
"encoding/json"
"io"
"testing"
"github.com/larksuite/cli/errs"
)
func TestWriteTypedErrorEnvelopeUsesEditionCompatibleCLIOrigin(t *testing.T) {
var out bytes.Buffer
err := errs.NewValidationError(errs.SubtypeInvalidArgument, "bad flag")
if ok := WriteTypedErrorEnvelope(&out, err, "user"); !ok {
t.Fatal("typed error was not written")
}
var envelope struct {
Error struct {
Origin string `json:"origin"`
} `json:"error"`
}
if decodeErr := json.Unmarshal(out.Bytes(), &envelope); decodeErr != nil {
t.Fatal(decodeErr)
}
if envelope.Error.Origin != defaultErrorOrigin() {
t.Fatalf("origin = %q, want %q", envelope.Error.Origin, defaultErrorOrigin())
}
var rawEnvelope struct {
Error map[string]json.RawMessage `json:"error"`
}
if decodeErr := json.Unmarshal(out.Bytes(), &rawEnvelope); decodeErr != nil {
t.Fatal(decodeErr)
}
_, originPresent := rawEnvelope.Error["origin"]
if wantPresent := defaultErrorOrigin() != ""; originPresent != wantPresent {
t.Fatalf("origin field present = %v, want %v; envelope = %s", originPresent, wantPresent, out.Bytes())
}
if metadata, ok := errs.DiagnosticMetadataOf(err); ok {
t.Fatalf("rendering mutated the reusable typed error: %#v", metadata)
}
}
func TestWriteTypedErrorEnvelopeDoesNotUseInnerProducerMetadata(t *testing.T) {
inner := errs.WithDiagnosticMetadata(
errs.NewNetworkError(errs.SubtypeUpstreamUnavailable, "proxy unavailable"),
errs.DiagnosticMetadata{
Origin: "proxy",
ProxyRequestID: "proxy_req_inner",
},
)
outer := errs.NewInternalError(errs.SubtypeUnknown, "business reclassified failure").
WithCause(inner)
var out bytes.Buffer
if ok := WriteTypedErrorEnvelope(&out, outer, "bot"); !ok {
t.Fatal("typed outer error was not written")
}
var envelope struct {
Error struct {
Type errs.Category `json:"type"`
Subtype errs.Subtype `json:"subtype"`
Message string `json:"message"`
Origin string `json:"origin"`
ProxyRequestID string `json:"proxy_request_id"`
} `json:"error"`
}
if err := json.Unmarshal(out.Bytes(), &envelope); err != nil {
t.Fatal(err)
}
if envelope.Error.Type != errs.CategoryInternal ||
envelope.Error.Subtype != errs.SubtypeUnknown ||
envelope.Error.Message != "business reclassified failure" {
t.Fatalf("wire error used the wrong typed producer: %#v", envelope.Error)
}
if envelope.Error.Origin != defaultErrorOrigin() {
t.Fatalf("origin = %q, want outer producer default %q", envelope.Error.Origin, defaultErrorOrigin())
}
if envelope.Error.ProxyRequestID != "" {
t.Fatalf("outer producer inherited inner proxy request id %q", envelope.Error.ProxyRequestID)
}
}
// failingWriter writes up to limit bytes then returns io.ErrShortWrite on
// the write that would push past the limit. Used to simulate a stderr that
// dies mid-envelope.
type failingWriter struct {
limit int
n int
}
func (f *failingWriter) Write(p []byte) (int, error) {
if f.n+len(p) > f.limit {
canWrite := f.limit - f.n
if canWrite < 0 {
canWrite = 0
}
f.n += canWrite
return canWrite, io.ErrShortWrite
}
f.n += len(p)
return len(p), nil
}
// TestWriteTypedErrorEnvelope_PartialWritePreservesSuccessStatus pins that
// when serialization succeeds but the underlying write fails mid-envelope,
// WriteTypedErrorEnvelope returns true so the dispatcher honors the typed
// exit code instead of reclassifying the error. Exit code is preserved
// separately by handleRootError computing ExitCodeOf(err) before the write.
func TestWriteTypedErrorEnvelope_PartialWritePreservesSuccessStatus(t *testing.T) {
err := errs.NewAuthenticationError(errs.SubtypeTokenExpired, "token expired")
w := &failingWriter{limit: 20} // dies mid-envelope
if ok := WriteTypedErrorEnvelope(w, err, "user"); !ok {
t.Error("partial write must return true; exit code is preserved separately")
}
}
func TestGetNotice(t *testing.T) {
// Nil PendingNotice → nil
origNotice := PendingNotice
PendingNotice = nil
if got := GetNotice(); got != nil {
t.Errorf("expected nil, got %v", got)
}
// With PendingNotice → returns value
PendingNotice = func() map[string]interface{} {
return map[string]interface{}{"update": "test"}
}
got := GetNotice()
if got == nil || got["update"] != "test" {
t.Errorf("expected {update: test}, got %v", got)
}
// PendingNotice returns nil → nil
PendingNotice = func() map[string]interface{} { return nil }
if got := GetNotice(); got != nil {
t.Errorf("expected nil, got %v", got)
}
PendingNotice = origNotice
}