| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- /*
- Copyright © The ESO Authors
- 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
- https://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 ctrlutil
- import (
- "errors"
- "fmt"
- "strings"
- "testing"
- )
- // providerErr stands in for an error returned by provider code, which may embed
- // secret material and must never reach a status condition.
- const providerErr = "json: cannot unmarshal number 8019210420527506405 into Go value of type float64"
- func TestSafeMessage(t *testing.T) {
- sentinel := errors.New("secret is immutable")
- tests := []struct {
- name string
- err error
- want string
- }{
- {
- name: "nil error yields nothing",
- err: nil,
- want: "",
- },
- {
- name: "unmarked error yields nothing",
- err: errors.New(providerErr),
- want: "",
- },
- {
- name: "marked error is published",
- err: Safe(errors.New("could not update secret foo: already exists")),
- want: "could not update secret foo: already exists",
- },
- {
- name: "marking nil stays nil",
- err: Safe(nil),
- want: "",
- },
- {
- name: "wrapping a marked error keeps it publishable",
- err: fmt.Errorf("could not update secret: %w", Safe(sentinel)),
- want: "secret is immutable",
- },
- {
- name: "marked twice is not duplicated",
- err: Safe(Safe(errors.New("target is owned by another ExternalSecret"))),
- want: "target is owned by another ExternalSecret",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := SafeMessage(tt.err); got != tt.want {
- t.Errorf("SafeMessage() = %q, want %q", got, tt.want)
- }
- })
- }
- }
- // Text composed around a marked error must never be published, whichever way it
- // was composed. Each shape here leaked before SafeMessage took the innermost mark.
- func TestSafeMessageDoesNotPublishWrapperText(t *testing.T) {
- marked := Safe(errors.New("connection refused"))
- tests := []struct {
- name string
- err error
- }{
- {
- name: "unmarked provider wrapper",
- err: fmt.Errorf("%s: %w", providerErr, marked),
- },
- {
- name: "marked provider wrapper",
- err: Safe(fmt.Errorf("%s: %w", providerErr, marked)),
- },
- {
- name: "joined with a provider error",
- err: Safe(errors.Join(errors.New(providerErr), marked)),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := SafeMessage(tt.err)
- if got != "connection refused" {
- t.Errorf("SafeMessage() = %q, want %q", got, "connection refused")
- }
- if strings.Contains(got, "8019210420527506405") {
- t.Errorf("SafeMessage() leaked wrapper text: %q", got)
- }
- })
- }
- }
- func TestSafePreservesErrorsIs(t *testing.T) {
- sentinel := errors.New("sentinel")
- if !errors.Is(Safe(sentinel), sentinel) {
- t.Error("Safe() broke errors.Is against the wrapped error")
- }
- if Safe(sentinel).Error() != "sentinel" {
- t.Errorf("Safe().Error() = %q, want %q", Safe(sentinel).Error(), "sentinel")
- }
- }
- func TestSafeMessageTruncates(t *testing.T) {
- long := strings.Repeat("a", MaxConditionMessageLength+50)
- got := SafeMessage(Safe(errors.New(long)))
- want := strings.Repeat("a", MaxConditionMessageLength-len("...")) + "..."
- if got != want {
- t.Errorf("SafeMessage() length = %d, want %d", len(got), len(want))
- }
- }
- // Truncation counts runes, so a multi-byte message is not cut mid-character.
- func TestSafeMessageTruncatesOnRunes(t *testing.T) {
- long := strings.Repeat("é", MaxConditionMessageLength+10)
- got := SafeMessage(Safe(errors.New(long)))
- if runes := []rune(got); len(runes) != MaxConditionMessageLength {
- t.Errorf("truncated to %d runes, want %d", len(runes), MaxConditionMessageLength)
- }
- if !strings.HasPrefix(got, "é") {
- t.Errorf("truncation split a multi-byte rune: %q", got[:8])
- }
- }
- // A limit too small to hold the marker still has to be respected.
- func TestTruncateLimitBelowMarker(t *testing.T) {
- if got := truncate("abcdef", 2); got != "ab" {
- t.Errorf("truncate() = %q, want %q", got, "ab")
- }
- }
|