mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-11 03:23:50 +08:00
Merge branch 'master' into rc/5.2
解决合并冲突: - .github/workflows/release.yml: 统一使用codeagent-wrapper目录和输出名称 - codeagent-wrapper/main_test.go: 合并HEAD的新测试(Claude/Gemini事件解析)和master的测试改进 - 接受master的新文件: .gitignore, process_check_* 到codeagent-wrapper目录 - 确认删除: codex-wrapper/main.go (已重命名) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,17 +2,31 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestLoggerCreatesFileWithPID(t *testing.T) {
|
||||
func compareCleanupStats(got, want CleanupStats) bool {
|
||||
if got.Scanned != want.Scanned || got.Deleted != want.Deleted || got.Kept != want.Kept || got.Errors != want.Errors {
|
||||
return false
|
||||
}
|
||||
// File lists may be in different order, just check lengths
|
||||
if len(got.DeletedFiles) != want.Deleted || len(got.KeptFiles) != want.Kept {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestRunLoggerCreatesFileWithPID(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("TMPDIR", tempDir)
|
||||
|
||||
@@ -32,7 +46,7 @@ func TestLoggerCreatesFileWithPID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerWritesLevels(t *testing.T) {
|
||||
func TestRunLoggerWritesLevels(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("TMPDIR", tempDir)
|
||||
|
||||
@@ -63,7 +77,7 @@ func TestLoggerWritesLevels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerCloseRemovesFileAndStopsWorker(t *testing.T) {
|
||||
func TestRunLoggerCloseRemovesFileAndStopsWorker(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("TMPDIR", tempDir)
|
||||
|
||||
@@ -102,7 +116,7 @@ func TestLoggerCloseRemovesFileAndStopsWorker(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerConcurrentWritesSafe(t *testing.T) {
|
||||
func TestRunLoggerConcurrentWritesSafe(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("TMPDIR", tempDir)
|
||||
|
||||
@@ -151,7 +165,7 @@ func TestLoggerConcurrentWritesSafe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoggerTerminateProcessActive(t *testing.T) {
|
||||
func TestRunLoggerTerminateProcessActive(t *testing.T) {
|
||||
cmd := exec.Command("sleep", "5")
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Skipf("cannot start sleep command: %v", err)
|
||||
@@ -179,8 +193,578 @@ func TestLoggerTerminateProcessActive(t *testing.T) {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestRunTerminateProcessNil(t *testing.T) {
|
||||
if timer := terminateProcess(nil); timer != nil {
|
||||
t.Fatalf("terminateProcess(nil) should return nil timer")
|
||||
}
|
||||
if timer := terminateProcess(&exec.Cmd{}); timer != nil {
|
||||
t.Fatalf("terminateProcess with nil process should return nil timer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsRemovesOrphans(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
orphan1 := createTempLog(t, tempDir, "codex-wrapper-111.log")
|
||||
orphan2 := createTempLog(t, tempDir, "codex-wrapper-222-suffix.log")
|
||||
running1 := createTempLog(t, tempDir, "codex-wrapper-333.log")
|
||||
running2 := createTempLog(t, tempDir, "codex-wrapper-444-extra-info.log")
|
||||
untouched := createTempLog(t, tempDir, "unrelated.log")
|
||||
|
||||
runningPIDs := map[int]bool{333: true, 444: true}
|
||||
stubProcessRunning(t, func(pid int) bool {
|
||||
return runningPIDs[pid]
|
||||
})
|
||||
|
||||
// Stub process start time to be in the past so files won't be considered as PID reused
|
||||
stubProcessStartTime(t, func(pid int) time.Time {
|
||||
if runningPIDs[pid] {
|
||||
// Return a time before file creation
|
||||
return time.Now().Add(-1 * time.Hour)
|
||||
}
|
||||
return time.Time{}
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err != nil {
|
||||
t.Fatalf("cleanupOldLogs() unexpected error: %v", err)
|
||||
}
|
||||
|
||||
want := CleanupStats{Scanned: 4, Deleted: 2, Kept: 2}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(orphan1); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected orphan %s to be removed, err=%v", orphan1, err)
|
||||
}
|
||||
if _, err := os.Stat(orphan2); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected orphan %s to be removed, err=%v", orphan2, err)
|
||||
}
|
||||
if _, err := os.Stat(running1); err != nil {
|
||||
t.Fatalf("expected running log %s to remain, err=%v", running1, err)
|
||||
}
|
||||
if _, err := os.Stat(running2); err != nil {
|
||||
t.Fatalf("expected running log %s to remain, err=%v", running2, err)
|
||||
}
|
||||
if _, err := os.Stat(untouched); err != nil {
|
||||
t.Fatalf("expected unrelated file %s to remain, err=%v", untouched, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsHandlesInvalidNamesAndErrors(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
invalid := []string{
|
||||
"codex-wrapper-.log",
|
||||
"codex-wrapper.log",
|
||||
"codex-wrapper-foo-bar.txt",
|
||||
"not-a-codex.log",
|
||||
}
|
||||
for _, name := range invalid {
|
||||
createTempLog(t, tempDir, name)
|
||||
}
|
||||
target := createTempLog(t, tempDir, "codex-wrapper-555-extra.log")
|
||||
|
||||
var checked []int
|
||||
stubProcessRunning(t, func(pid int) bool {
|
||||
checked = append(checked, pid)
|
||||
return false
|
||||
})
|
||||
|
||||
stubProcessStartTime(t, func(pid int) time.Time {
|
||||
return time.Time{} // Return zero time for processes not running
|
||||
})
|
||||
|
||||
removeErr := errors.New("remove failure")
|
||||
callCount := 0
|
||||
stubRemoveLogFile(t, func(path string) error {
|
||||
callCount++
|
||||
if path == target {
|
||||
return removeErr
|
||||
}
|
||||
return os.Remove(path)
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err == nil {
|
||||
t.Fatalf("cleanupOldLogs() expected error")
|
||||
}
|
||||
if !errors.Is(err, removeErr) {
|
||||
t.Fatalf("cleanupOldLogs error = %v, want %v", err, removeErr)
|
||||
}
|
||||
|
||||
want := CleanupStats{Scanned: 2, Kept: 1, Errors: 1}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
|
||||
if len(checked) != 1 || checked[0] != 555 {
|
||||
t.Fatalf("expected only valid PID to be checked, got %v", checked)
|
||||
}
|
||||
if callCount != 1 {
|
||||
t.Fatalf("expected remove to be called once, got %d", callCount)
|
||||
}
|
||||
if _, err := os.Stat(target); err != nil {
|
||||
t.Fatalf("expected errored file %s to remain for manual cleanup, err=%v", target, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsHandlesGlobFailures(t *testing.T) {
|
||||
stubProcessRunning(t, func(pid int) bool {
|
||||
t.Fatalf("process check should not run when glob fails")
|
||||
return false
|
||||
})
|
||||
stubProcessStartTime(t, func(int) time.Time {
|
||||
return time.Time{}
|
||||
})
|
||||
|
||||
globErr := errors.New("glob failure")
|
||||
stubGlobLogFiles(t, func(pattern string) ([]string, error) {
|
||||
return nil, globErr
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err == nil {
|
||||
t.Fatalf("cleanupOldLogs() expected error")
|
||||
}
|
||||
if !errors.Is(err, globErr) {
|
||||
t.Fatalf("cleanupOldLogs error = %v, want %v", err, globErr)
|
||||
}
|
||||
if stats.Scanned != 0 || stats.Deleted != 0 || stats.Kept != 0 || stats.Errors != 0 || len(stats.DeletedFiles) != 0 || len(stats.KeptFiles) != 0 {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want zero", stats)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsEmptyDirectoryStats(t *testing.T) {
|
||||
setTempDirEnv(t, t.TempDir())
|
||||
|
||||
stubProcessRunning(t, func(int) bool {
|
||||
t.Fatalf("process check should not run for empty directory")
|
||||
return false
|
||||
})
|
||||
stubProcessStartTime(t, func(int) time.Time {
|
||||
return time.Time{}
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err != nil {
|
||||
t.Fatalf("cleanupOldLogs() unexpected error: %v", err)
|
||||
}
|
||||
if stats.Scanned != 0 || stats.Deleted != 0 || stats.Kept != 0 || stats.Errors != 0 || len(stats.DeletedFiles) != 0 || len(stats.KeptFiles) != 0 {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want zero", stats)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsHandlesTempDirPermissionErrors(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
paths := []string{
|
||||
createTempLog(t, tempDir, "codex-wrapper-6100.log"),
|
||||
createTempLog(t, tempDir, "codex-wrapper-6101.log"),
|
||||
}
|
||||
|
||||
stubProcessRunning(t, func(int) bool { return false })
|
||||
stubProcessStartTime(t, func(int) time.Time { return time.Time{} })
|
||||
|
||||
var attempts int
|
||||
stubRemoveLogFile(t, func(path string) error {
|
||||
attempts++
|
||||
return &os.PathError{Op: "remove", Path: path, Err: os.ErrPermission}
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err == nil {
|
||||
t.Fatalf("cleanupOldLogs() expected error")
|
||||
}
|
||||
if !errors.Is(err, os.ErrPermission) {
|
||||
t.Fatalf("cleanupOldLogs error = %v, want permission", err)
|
||||
}
|
||||
|
||||
want := CleanupStats{Scanned: len(paths), Errors: len(paths)}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
|
||||
if attempts != len(paths) {
|
||||
t.Fatalf("expected %d attempts, got %d", len(paths), attempts)
|
||||
}
|
||||
for _, path := range paths {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatalf("expected protected file %s to remain, err=%v", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsHandlesPermissionDeniedFile(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
protected := createTempLog(t, tempDir, "codex-wrapper-6200.log")
|
||||
deletable := createTempLog(t, tempDir, "codex-wrapper-6201.log")
|
||||
|
||||
stubProcessRunning(t, func(int) bool { return false })
|
||||
stubProcessStartTime(t, func(int) time.Time { return time.Time{} })
|
||||
|
||||
stubRemoveLogFile(t, func(path string) error {
|
||||
if path == protected {
|
||||
return &os.PathError{Op: "remove", Path: path, Err: os.ErrPermission}
|
||||
}
|
||||
return os.Remove(path)
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err == nil {
|
||||
t.Fatalf("cleanupOldLogs() expected error")
|
||||
}
|
||||
if !errors.Is(err, os.ErrPermission) {
|
||||
t.Fatalf("cleanupOldLogs error = %v, want permission", err)
|
||||
}
|
||||
|
||||
want := CleanupStats{Scanned: 2, Deleted: 1, Errors: 1}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(protected); err != nil {
|
||||
t.Fatalf("expected protected file to remain, err=%v", err)
|
||||
}
|
||||
if _, err := os.Stat(deletable); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected deletable file to be removed, err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsPerformanceBound(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
const fileCount = 400
|
||||
fakePaths := make([]string, fileCount)
|
||||
for i := 0; i < fileCount; i++ {
|
||||
name := fmt.Sprintf("codex-wrapper-%d.log", 10000+i)
|
||||
fakePaths[i] = createTempLog(t, tempDir, name)
|
||||
}
|
||||
|
||||
stubGlobLogFiles(t, func(pattern string) ([]string, error) {
|
||||
return fakePaths, nil
|
||||
})
|
||||
stubProcessRunning(t, func(int) bool { return false })
|
||||
stubProcessStartTime(t, func(int) time.Time { return time.Time{} })
|
||||
|
||||
var removed int
|
||||
stubRemoveLogFile(t, func(path string) error {
|
||||
removed++
|
||||
return nil
|
||||
})
|
||||
|
||||
start := time.Now()
|
||||
stats, err := cleanupOldLogs()
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("cleanupOldLogs() unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if removed != fileCount {
|
||||
t.Fatalf("expected %d removals, got %d", fileCount, removed)
|
||||
}
|
||||
if elapsed > 100*time.Millisecond {
|
||||
t.Fatalf("cleanup took too long: %v for %d files", elapsed, fileCount)
|
||||
}
|
||||
|
||||
want := CleanupStats{Scanned: fileCount, Deleted: fileCount}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsCoverageSuite(t *testing.T) {
|
||||
TestRunParseJSONStream_CoverageSuite(t)
|
||||
}
|
||||
|
||||
// Reuse the existing coverage suite so the focused TestLogger run still exercises
|
||||
// the rest of the codebase and keeps coverage high.
|
||||
func TestLoggerCoverageSuite(t *testing.T) {
|
||||
TestParseJSONStream_CoverageSuite(t)
|
||||
func TestRunLoggerCoverageSuite(t *testing.T) {
|
||||
TestRunParseJSONStream_CoverageSuite(t)
|
||||
}
|
||||
|
||||
func TestRunCleanupOldLogsKeepsCurrentProcessLog(t *testing.T) {
|
||||
tempDir := setTempDirEnv(t, t.TempDir())
|
||||
|
||||
currentPID := os.Getpid()
|
||||
currentLog := createTempLog(t, tempDir, fmt.Sprintf("codex-wrapper-%d.log", currentPID))
|
||||
|
||||
stubProcessRunning(t, func(pid int) bool {
|
||||
if pid != currentPID {
|
||||
t.Fatalf("unexpected pid check: %d", pid)
|
||||
}
|
||||
return true
|
||||
})
|
||||
stubProcessStartTime(t, func(pid int) time.Time {
|
||||
if pid == currentPID {
|
||||
return time.Now().Add(-1 * time.Hour)
|
||||
}
|
||||
return time.Time{}
|
||||
})
|
||||
|
||||
stats, err := cleanupOldLogs()
|
||||
if err != nil {
|
||||
t.Fatalf("cleanupOldLogs() unexpected error: %v", err)
|
||||
}
|
||||
want := CleanupStats{Scanned: 1, Kept: 1}
|
||||
if !compareCleanupStats(stats, want) {
|
||||
t.Fatalf("cleanup stats mismatch: got %+v, want %+v", stats, want)
|
||||
}
|
||||
if _, err := os.Stat(currentLog); err != nil {
|
||||
t.Fatalf("expected current process log to remain, err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPIDReusedScenarios(t *testing.T) {
|
||||
now := time.Now()
|
||||
tests := []struct {
|
||||
name string
|
||||
statErr error
|
||||
modTime time.Time
|
||||
startTime time.Time
|
||||
want bool
|
||||
}{
|
||||
{"stat error", errors.New("stat failed"), time.Time{}, time.Time{}, false},
|
||||
{"old file unknown start", nil, now.Add(-8 * 24 * time.Hour), time.Time{}, true},
|
||||
{"recent file unknown start", nil, now.Add(-2 * time.Hour), time.Time{}, false},
|
||||
{"pid reused", nil, now.Add(-2 * time.Hour), now.Add(-30 * time.Minute), true},
|
||||
{"pid active", nil, now.Add(-30 * time.Minute), now.Add(-2 * time.Hour), false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||
if tt.statErr != nil {
|
||||
return nil, tt.statErr
|
||||
}
|
||||
return fakeFileInfo{modTime: tt.modTime}, nil
|
||||
})
|
||||
stubProcessStartTime(t, func(int) time.Time {
|
||||
return tt.startTime
|
||||
})
|
||||
if got := isPIDReused("log", 1234); got != tt.want {
|
||||
t.Fatalf("isPIDReused() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsUnsafeFileSecurityChecks(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
absTempDir, err := filepath.Abs(tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("filepath.Abs() error = %v", err)
|
||||
}
|
||||
|
||||
t.Run("symlink", func(t *testing.T) {
|
||||
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||
return fakeFileInfo{mode: os.ModeSymlink}, nil
|
||||
})
|
||||
stubEvalSymlinks(t, func(path string) (string, error) {
|
||||
return filepath.Join(absTempDir, filepath.Base(path)), nil
|
||||
})
|
||||
unsafe, reason := isUnsafeFile(filepath.Join(absTempDir, "codex-wrapper-1.log"), tempDir)
|
||||
if !unsafe || reason != "refusing to delete symlink" {
|
||||
t.Fatalf("expected symlink to be rejected, got unsafe=%v reason=%q", unsafe, reason)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("path traversal", func(t *testing.T) {
|
||||
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||
return fakeFileInfo{}, nil
|
||||
})
|
||||
outside := filepath.Join(filepath.Dir(absTempDir), "etc", "passwd")
|
||||
stubEvalSymlinks(t, func(string) (string, error) {
|
||||
return outside, nil
|
||||
})
|
||||
unsafe, reason := isUnsafeFile(filepath.Join("..", "..", "etc", "passwd"), tempDir)
|
||||
if !unsafe || reason != "file is outside tempDir" {
|
||||
t.Fatalf("expected traversal path to be rejected, got unsafe=%v reason=%q", unsafe, reason)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("outside temp dir", func(t *testing.T) {
|
||||
stubFileStat(t, func(string) (os.FileInfo, error) {
|
||||
return fakeFileInfo{}, nil
|
||||
})
|
||||
otherDir := t.TempDir()
|
||||
stubEvalSymlinks(t, func(string) (string, error) {
|
||||
return filepath.Join(otherDir, "codex-wrapper-9.log"), nil
|
||||
})
|
||||
unsafe, reason := isUnsafeFile(filepath.Join(otherDir, "codex-wrapper-9.log"), tempDir)
|
||||
if !unsafe || reason != "file is outside tempDir" {
|
||||
t.Fatalf("expected outside file to be rejected, got unsafe=%v reason=%q", unsafe, reason)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunLoggerPathAndRemove(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
path := filepath.Join(tempDir, "sample.log")
|
||||
if err := os.WriteFile(path, []byte("test"), 0o644); err != nil {
|
||||
t.Fatalf("failed to create temp file: %v", err)
|
||||
}
|
||||
|
||||
logger := &Logger{path: path}
|
||||
if got := logger.Path(); got != path {
|
||||
t.Fatalf("Path() = %q, want %q", got, path)
|
||||
}
|
||||
if err := logger.RemoveLogFile(); err != nil {
|
||||
t.Fatalf("RemoveLogFile() error = %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
t.Fatalf("expected log file to be removed, err=%v", err)
|
||||
}
|
||||
|
||||
var nilLogger *Logger
|
||||
if nilLogger.Path() != "" {
|
||||
t.Fatalf("nil logger Path() should be empty")
|
||||
}
|
||||
if err := nilLogger.RemoveLogFile(); err != nil {
|
||||
t.Fatalf("nil logger RemoveLogFile() should return nil, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunLoggerInternalLog(t *testing.T) {
|
||||
logger := &Logger{
|
||||
ch: make(chan logEntry, 1),
|
||||
done: make(chan struct{}),
|
||||
pendingWG: sync.WaitGroup{},
|
||||
}
|
||||
|
||||
done := make(chan logEntry, 1)
|
||||
go func() {
|
||||
entry := <-logger.ch
|
||||
logger.pendingWG.Done()
|
||||
done <- entry
|
||||
}()
|
||||
|
||||
logger.log("INFO", "hello")
|
||||
entry := <-done
|
||||
if entry.level != "INFO" || entry.msg != "hello" {
|
||||
t.Fatalf("unexpected entry %+v", entry)
|
||||
}
|
||||
|
||||
logger.closed.Store(true)
|
||||
logger.log("INFO", "ignored")
|
||||
close(logger.done)
|
||||
}
|
||||
|
||||
func TestRunParsePIDFromLog(t *testing.T) {
|
||||
hugePID := strconv.FormatInt(math.MaxInt64, 10) + "0"
|
||||
tests := []struct {
|
||||
name string
|
||||
pid int
|
||||
ok bool
|
||||
}{
|
||||
{"codex-wrapper-123.log", 123, true},
|
||||
{"codex-wrapper-999-extra.log", 999, true},
|
||||
{"codex-wrapper-.log", 0, false},
|
||||
{"invalid-name.log", 0, false},
|
||||
{"codex-wrapper--5.log", 0, false},
|
||||
{"codex-wrapper-0.log", 0, false},
|
||||
{fmt.Sprintf("codex-wrapper-%s.log", hugePID), 0, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, ok := parsePIDFromLog(filepath.Join("/tmp", tt.name))
|
||||
if ok != tt.ok {
|
||||
t.Fatalf("parsePIDFromLog ok = %v, want %v", ok, tt.ok)
|
||||
}
|
||||
if ok && got != tt.pid {
|
||||
t.Fatalf("pid = %d, want %d", got, tt.pid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createTempLog(t *testing.T, dir, name string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(dir, name)
|
||||
if err := os.WriteFile(path, []byte("test"), 0o644); err != nil {
|
||||
t.Fatalf("failed to create temp log %s: %v", path, err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func setTempDirEnv(t *testing.T, dir string) string {
|
||||
t.Helper()
|
||||
resolved := dir
|
||||
if eval, err := filepath.EvalSymlinks(dir); err == nil {
|
||||
resolved = eval
|
||||
}
|
||||
t.Setenv("TMPDIR", resolved)
|
||||
t.Setenv("TEMP", resolved)
|
||||
t.Setenv("TMP", resolved)
|
||||
return resolved
|
||||
}
|
||||
|
||||
func stubProcessRunning(t *testing.T, fn func(int) bool) {
|
||||
t.Helper()
|
||||
original := processRunningCheck
|
||||
processRunningCheck = fn
|
||||
t.Cleanup(func() {
|
||||
processRunningCheck = original
|
||||
})
|
||||
}
|
||||
|
||||
func stubProcessStartTime(t *testing.T, fn func(int) time.Time) {
|
||||
t.Helper()
|
||||
original := processStartTimeFn
|
||||
processStartTimeFn = fn
|
||||
t.Cleanup(func() {
|
||||
processStartTimeFn = original
|
||||
})
|
||||
}
|
||||
|
||||
func stubRemoveLogFile(t *testing.T, fn func(string) error) {
|
||||
t.Helper()
|
||||
original := removeLogFileFn
|
||||
removeLogFileFn = fn
|
||||
t.Cleanup(func() {
|
||||
removeLogFileFn = original
|
||||
})
|
||||
}
|
||||
|
||||
func stubGlobLogFiles(t *testing.T, fn func(string) ([]string, error)) {
|
||||
t.Helper()
|
||||
original := globLogFiles
|
||||
globLogFiles = fn
|
||||
t.Cleanup(func() {
|
||||
globLogFiles = original
|
||||
})
|
||||
}
|
||||
|
||||
func stubFileStat(t *testing.T, fn func(string) (os.FileInfo, error)) {
|
||||
t.Helper()
|
||||
original := fileStatFn
|
||||
fileStatFn = fn
|
||||
t.Cleanup(func() {
|
||||
fileStatFn = original
|
||||
})
|
||||
}
|
||||
|
||||
func stubEvalSymlinks(t *testing.T, fn func(string) (string, error)) {
|
||||
t.Helper()
|
||||
original := evalSymlinksFn
|
||||
evalSymlinksFn = fn
|
||||
t.Cleanup(func() {
|
||||
evalSymlinksFn = original
|
||||
})
|
||||
}
|
||||
|
||||
type fakeFileInfo struct {
|
||||
modTime time.Time
|
||||
mode os.FileMode
|
||||
}
|
||||
|
||||
func (f fakeFileInfo) Name() string { return "fake" }
|
||||
func (f fakeFileInfo) Size() int64 { return 0 }
|
||||
func (f fakeFileInfo) Mode() os.FileMode { return f.mode }
|
||||
func (f fakeFileInfo) ModTime() time.Time { return f.modTime }
|
||||
func (f fakeFileInfo) IsDir() bool { return false }
|
||||
func (f fakeFileInfo) Sys() interface{} { return nil }
|
||||
|
||||
Reference in New Issue
Block a user