Ctrl K
ComunidadGuías…

api/handler/service_exec_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Copyright (C) 2014-2018 Goodrain Co., Ltd.
// RAINBOND, Application Management Platform

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

package handler

import (
	"strings"
	"testing"
	"time"

	"github.com/pkg/errors"
	k8sexec "k8s.io/client-go/util/exec"
)

func TestClampExecTimeout(t *testing.T) {
	tests := []struct {
		name    string
		seconds int
		want    time.Duration
	}{
		{name: "zero falls back to default", seconds: 0, want: PodExecDefaultTimeoutSeconds * time.Second},
		{name: "negative falls back to default", seconds: -5, want: PodExecDefaultTimeoutSeconds * time.Second},
		{name: "within range kept", seconds: 45, want: 45 * time.Second},
		{name: "over max clamped", seconds: 9999, want: PodExecMaxTimeoutSeconds * time.Second},
		{name: "exactly max kept", seconds: PodExecMaxTimeoutSeconds, want: PodExecMaxTimeoutSeconds * time.Second},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := clampExecTimeout(tt.seconds); got != tt.want {
				t.Fatalf("clampExecTimeout(%d) = %v, want %v", tt.seconds, got, tt.want)
			}
		})
	}
}

func TestCapBufferUnderLimit(t *testing.T) {
	// Arrange
	cb := newCapBuffer(16)

	// Act
	n, err := cb.Write([]byte("hello"))

	// Assert
	if err != nil {
		t.Fatalf("write err: %v", err)
	}
	if n != 5 {
		t.Fatalf("n = %d, want 5", n)
	}
	if cb.truncated {
		t.Fatalf("truncated = true, want false")
	}
	if cb.String() != "hello" {
		t.Fatalf("String() = %q, want hello", cb.String())
	}
}

func TestCapBufferExactlyAtLimit(t *testing.T) {
	cb := newCapBuffer(5)
	cb.Write([]byte("hello"))
	if cb.truncated {
		t.Fatalf("truncated = true at exact limit, want false")
	}
	if cb.String() != "hello" {
		t.Fatalf("String() = %q, want hello", cb.String())
	}
}

func TestCapBufferTruncatesSingleWrite(t *testing.T) {
	cb := newCapBuffer(4)
	n, _ := cb.Write([]byte("abcdefgh"))
	if n != 8 {
		t.Fatalf("reported written = %d, want 8 (full input length)", n)
	}
	if !cb.truncated {
		t.Fatalf("truncated = false, want true")
	}
	if cb.String() != "abcd" {
		t.Fatalf("String() = %q, want abcd", cb.String())
	}
}

func TestCapBufferTruncatesAcrossWrites(t *testing.T) {
	cb := newCapBuffer(5)
	cb.Write([]byte("abc"))
	cb.Write([]byte("def")) // only "de" fits, rest dropped
	if !cb.truncated {
		t.Fatalf("truncated = false, want true")
	}
	if cb.String() != "abcde" {
		t.Fatalf("String() = %q, want abcde", cb.String())
	}
	// A further write once full should keep truncated true and not grow.
	cb.Write([]byte("xyz"))
	if cb.String() != "abcde" {
		t.Fatalf("String() grew past limit: %q", cb.String())
	}
}

func TestCapBufferRespectsOneMiBConstant(t *testing.T) {
	cb := newCapBuffer(PodExecMaxOutputBytes)
	big := strings.Repeat("x", PodExecMaxOutputBytes+100)
	cb.Write([]byte(big))
	if !cb.truncated {
		t.Fatalf("truncated = false for oversize write, want true")
	}
	if len(cb.String()) != PodExecMaxOutputBytes {
		t.Fatalf("buffered len = %d, want %d", len(cb.String()), PodExecMaxOutputBytes)
	}
}

// TestCodeExitErrorExtraction verifies the exit-code extraction contract used
// by ExecCommand: a CodeExitError surfaces its numeric exit status via
// errors.As, and a generic error does not.
func TestCodeExitErrorExtraction(t *testing.T) {
	t.Run("CodeExitError yields exit status", func(t *testing.T) {
		var wrapped error = errors.Wrap(k8sexec.CodeExitError{Err: errors.New("command failed"), Code: 137}, "stream")
		var codeErr k8sexec.CodeExitError
		if !errors.As(wrapped, &codeErr) {
			t.Fatalf("errors.As did not match CodeExitError")
		}
		if codeErr.ExitStatus() != 137 {
			t.Fatalf("ExitStatus() = %d, want 137", codeErr.ExitStatus())
		}
	})
	t.Run("generic error does not match", func(t *testing.T) {
		var codeErr k8sexec.CodeExitError
		if errors.As(errors.New("network blip"), &codeErr) {
			t.Fatalf("errors.As matched a non-CodeExitError")
		}
	})
}

func TestErrContainerNotRunningIsDistinguishable(t *testing.T) {
	wrapped := errors.Wrapf(ErrContainerNotRunning, "container %s", "main")
	if !errors.Is(wrapped, ErrContainerNotRunning) {
		t.Fatalf("errors.Is failed to identify ErrContainerNotRunning")
	}
}