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

161 lines
4.1 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 logging
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
)
func TestSetupCreatesLogFile(t *testing.T) {
t.Parallel()
dir := t.TempDir()
fl := Setup(dir)
defer fl.Close()
fl.Logger.Info("test message", "key", "value")
fl.Close()
logPath := filepath.Join(dir, logSubDir, logFileName)
data, err := os.ReadFile(logPath)
if err != nil {
t.Fatalf("failed to read log file: %v", err)
}
if !strings.Contains(string(data), "test message") {
t.Errorf("log file does not contain 'test message': %s", data)
}
if !strings.Contains(string(data), `"key":"value"`) {
t.Errorf("log file does not contain structured key: %s", data)
}
}
func TestSetupInvalidDirReturnsNopLogger(t *testing.T) {
t.Parallel()
fl := Setup("/nonexistent/path/that/cannot/be/created")
defer fl.Close()
// Should not panic
fl.Logger.Info("should be discarded")
}
func TestRotatingWriterRotatesOnSize(t *testing.T) {
t.Parallel()
dir := t.TempDir()
logPath := filepath.Join(dir, "test.log")
w := newRotatingWriter(logPath, 100, 2) // 100 bytes max
if err := w.open(); err != nil {
t.Fatalf("open: %v", err)
}
// Write enough data to trigger rotation
payload := strings.Repeat("x", 60) + "\n"
w.Write([]byte(payload)) // 61 bytes
w.Write([]byte(payload)) // triggers rotate at 122 bytes
// Should have created a backup
if _, err := os.Stat(logPath + ".1"); err != nil {
t.Errorf("backup .1 not created: %v", err)
}
// Write more to trigger second rotation
w.Write([]byte(payload))
w.Write([]byte(payload))
w.close()
if _, err := os.Stat(logPath + ".2"); err != nil {
t.Errorf("backup .2 not created: %v", err)
}
// Active log should be small (last write only)
info, _ := os.Stat(logPath)
if info.Size() > 100 {
t.Errorf("active log too large after rotation: %d bytes", info.Size())
}
}
func TestRotatingWriterMaxBackups(t *testing.T) {
t.Parallel()
dir := t.TempDir()
logPath := filepath.Join(dir, "test.log")
w := newRotatingWriter(logPath, 50, 2)
if err := w.open(); err != nil {
t.Fatalf("open: %v", err)
}
payload := strings.Repeat("a", 51) + "\n"
// Force multiple rotations
for i := 0; i < 5; i++ {
w.Write([]byte(payload))
}
w.close()
// .3 should NOT exist (maxBackups=2)
if _, err := os.Stat(logPath + ".3"); err == nil {
t.Error("backup .3 should not exist with maxBackups=2")
}
}
func TestCloseOnNilLogger(t *testing.T) {
t.Parallel()
var fl *FileLogger
if err := fl.Close(); err != nil {
t.Errorf("Close on nil should not error: %v", err)
}
}
func TestCrossPlatformCoverageFileLoggerCloseIsTerminal(t *testing.T) {
t.Parallel()
dir := t.TempDir()
fl := Setup(dir)
logPath := filepath.Join(dir, logSubDir, logFileName)
fl.Logger.Info("before close")
if err := fl.Close(); err != nil {
t.Fatalf("close logger: %v", err)
}
if err := os.Remove(logPath); err != nil {
t.Fatalf("remove closed log file: %v", err)
}
if err := fl.writer.open(); !errors.Is(err, os.ErrClosed) {
t.Fatalf("open after close = %v, want os.ErrClosed", err)
}
fl.writer.mu.Lock()
err := fl.writer.reopenLocked()
fl.writer.mu.Unlock()
if !errors.Is(err, os.ErrClosed) {
t.Fatalf("reopen after close = %v, want os.ErrClosed", err)
}
n, err := fl.Writer().Write([]byte("late write"))
if n != 0 || !errors.Is(err, os.ErrClosed) {
t.Fatalf("write after close = (%d, %v), want (0, os.ErrClosed)", n, err)
}
fl.Logger.Info("late structured write")
if _, err := os.Stat(logPath); !os.IsNotExist(err) {
t.Fatalf("closed logger recreated log file: %v", err)
}
if err := fl.Close(); err != nil {
t.Fatalf("second close: %v", err)
}
}