Files
dingtalk-workspace-cli/internal/msgcrypto/logger_test.go

158 lines
4.5 KiB
Go

// Copyright 2026 Alibaba Group
// 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.
package msgcrypto
import (
"errors"
"fmt"
"strings"
"testing"
"time"
)
// captureLogf collects emitted lines for assertions.
func captureLogf(lines *[]string) func(string, ...any) {
return func(format string, args ...any) {
*lines = append(*lines, fmt.Sprintf(format, args...))
}
}
func TestNewRedactingLoggerReturnsNilWhenSinkIsNil(t *testing.T) {
// A nil logger makes the vendor SDK skip logging entirely, which is the
// safe default because it logs the authCode at debug level.
if got := newRedactingLogger(nil); got != nil {
t.Fatalf("newRedactingLogger(nil) = %v, want nil", got)
}
}
func TestRedactingLoggerHidesAuthCode(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
// This mirrors the vendor SDK's own debug line, which prints the code.
const secret = "abc123authcode"
logger.Debug("Code (auth_token, length=%d): %s", len(secret), secret)
if len(lines) != 1 {
t.Fatalf("got %d lines, want 1", len(lines))
}
if strings.Contains(lines[0], secret) {
t.Fatalf("log line leaked the auth code: %q", lines[0])
}
if !strings.Contains(lines[0], "redacted") {
t.Fatalf("log line = %q, want a redaction marker", lines[0])
}
}
func TestRedactingLoggerHidesKeyServerResponseBody(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
body := `{"key":"BASE64KEYMATERIAL==","keyVersion":3}`
logger.Debug("Body (length=%d): %s", len(body), body)
if strings.Contains(lines[0], "BASE64KEYMATERIAL") {
t.Fatalf("log line leaked key material: %q", lines[0])
}
}
func TestRedactingLoggerHidesByteSlices(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
logger.Info("payload=%s", []byte("plaintext-message"))
if strings.Contains(lines[0], "plaintext-message") {
t.Fatalf("log line leaked a byte payload: %q", lines[0])
}
}
func TestRedactingLoggerHidesErrorText(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
// Vendor error strings can embed a response body preview.
logger.Error("request failed: %v", errors.New(`server said {"key":"LEAKED"}`))
if strings.Contains(lines[0], "LEAKED") {
t.Fatalf("log line leaked error contents: %q", lines[0])
}
}
func TestRedactingLoggerKeepsDiagnosticNumbers(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
logger.Debug("Status: %d (took %s), size=%d", 503, 1500*time.Millisecond, 4096)
line := lines[0]
for _, want := range []string{"503", "1.5s", "4096"} {
if !strings.Contains(line, want) {
t.Fatalf("log line = %q, want it to keep %q for diagnostics", line, want)
}
}
}
func TestRedactingLoggerLabelsLevel(t *testing.T) {
var lines []string
logger := newRedactingLogger(captureLogf(&lines))
logger.Debug("d")
logger.Info("i")
logger.Error("e")
if len(lines) != 3 {
t.Fatalf("got %d lines, want 3", len(lines))
}
for i, want := range []string{"debug", "info", "error"} {
if !strings.Contains(lines[i], want) {
t.Fatalf("line %d = %q, want level %q", i, lines[i], want)
}
}
}
func TestRedactingLoggerToleratesNilSinkAtEmit(t *testing.T) {
// Guard against a partially constructed logger being used.
var logger *redactingLogger
logger.Debug("must not panic %s", "value")
}
func TestRedactArgKeepsDurations(t *testing.T) {
if got := redactArg(2 * time.Second); got != "2s" {
t.Fatalf("redactArg(2s) = %v, want 2s", got)
}
}
func TestRedactArgElidesStrings(t *testing.T) {
got, ok := redactArg("secret").(string)
if !ok {
t.Fatalf("redactArg returned %T, want string", got)
}
if strings.Contains(got, "secret") {
t.Fatalf("redactArg = %q, want the value elided", got)
}
if !strings.Contains(got, "len=6") {
t.Fatalf("redactArg = %q, want the length preserved", got)
}
}
func TestRedactArgPassesThroughNumbers(t *testing.T) {
if got := redactArg(42); got != 42 {
t.Fatalf("redactArg(42) = %v, want 42", got)
}
if got := redactArg(true); got != true {
t.Fatalf("redactArg(true) = %v, want true", got)
}
}