150 lines
4.5 KiB
Go
150 lines
4.5 KiB
Go
// Copyright 2026 Alibaba Group
|
|
// Licensed under the Apache License, Version 2.0
|
|
|
|
package localio
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/testseam"
|
|
)
|
|
|
|
func TestCrossPlatformCoverageReadTextInputLiteralStdinAndFileE2E(t *testing.T) {
|
|
if got, err := ReadTextInput("literal", nil, 10); err != nil || got != "literal" {
|
|
t.Fatalf("literal = %q, %v", got, err)
|
|
}
|
|
if got, err := ReadTextInput("-", strings.NewReader("stdin"), 10); err != nil || got != "stdin" {
|
|
t.Fatalf("stdin = %q, %v", got, err)
|
|
}
|
|
dir := t.TempDir()
|
|
old, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = os.Chdir(old) })
|
|
if err := os.WriteFile(filepath.Join(dir, "input.json"), []byte("file"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, err := ReadTextInput("@input.json", nil, 10); err != nil || got != "file" {
|
|
t.Fatalf("file = %q, %v", got, err)
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageReadTextInputRejectsEscapeAndOversizeE2E(t *testing.T) {
|
|
for _, spec := range []string{"@../secret", "@/tmp/secret", "@"} {
|
|
if _, err := ReadTextInput(spec, nil, 10); err == nil {
|
|
t.Fatalf("unsafe input accepted: %q", spec)
|
|
}
|
|
}
|
|
if _, err := ReadTextInput("too-large", nil, 2); err == nil {
|
|
t.Fatal("oversize literal accepted")
|
|
}
|
|
if _, err := ReadTextInput("-", strings.NewReader("too-large"), 2); err == nil {
|
|
t.Fatal("oversize stdin accepted")
|
|
}
|
|
}
|
|
|
|
func TestCrossPlatformCoverageReadTextInputFailureBranchesE2E(t *testing.T) {
|
|
if got, err := ReadTextInput("literal", nil, 0); err != nil || got != "literal" {
|
|
t.Fatalf("default limit literal = %q, %v", got, err)
|
|
}
|
|
if _, err := ReadTextInput("-", nil, 1); err == nil {
|
|
t.Fatal("nil stdin accepted")
|
|
}
|
|
t.Run("stdin read", func(t *testing.T) {
|
|
testseam.Swap(t, &readTextInputAll, func(io.Reader) ([]byte, error) { return nil, errors.New("read") })
|
|
if _, err := ReadTextInput("-", strings.NewReader("x"), 1); err == nil {
|
|
t.Fatal("stdin read failure ignored")
|
|
}
|
|
})
|
|
|
|
dir := t.TempDir()
|
|
file := filepath.Join(dir, "input.txt")
|
|
if err := os.WriteFile(file, []byte("long"), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chdir(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = os.Chdir(old) })
|
|
if _, err := ReadTextInput("@input.txt", nil, 1); err == nil {
|
|
t.Fatal("oversize file accepted")
|
|
}
|
|
if _, err := ReadTextInput("@.", nil, 10); err == nil {
|
|
t.Fatal("directory accepted")
|
|
}
|
|
|
|
for name, run := range map[string]func(*testing.T){
|
|
"getwd": func(t *testing.T) {
|
|
testseam.Swap(t, &localGetwd, func() (string, error) { return "", errors.New("getwd") })
|
|
},
|
|
"eval base": func(t *testing.T) {
|
|
testseam.Swap(t, &localEvalSymlinks, func(string) (string, error) { return "", errors.New("eval base") })
|
|
},
|
|
"eval path": func(t *testing.T) {
|
|
calls := 0
|
|
testseam.Swap(t, &localEvalSymlinks, func(path string) (string, error) {
|
|
calls++
|
|
if calls == 2 {
|
|
return "", errors.New("eval path")
|
|
}
|
|
return path, nil
|
|
})
|
|
},
|
|
"relative": func(t *testing.T) {
|
|
testseam.Swap(t, &readTextInputRel, func(string, string) (string, error) { return "", errors.New("rel") })
|
|
},
|
|
"path stat": func(t *testing.T) {
|
|
testseam.Swap(t, &statTextInputPath, func(string) (os.FileInfo, error) { return nil, errors.New("stat") })
|
|
},
|
|
"open": func(t *testing.T) {
|
|
testseam.Swap(t, &openTextInputFile, func(string) (*os.File, error) { return nil, errors.New("open") })
|
|
},
|
|
"opened file stat": func(t *testing.T) {
|
|
testseam.Swap(t, &openTextInputFile, func(candidate string) (*os.File, error) {
|
|
file, openErr := os.Open(candidate)
|
|
if openErr == nil {
|
|
_ = file.Close()
|
|
}
|
|
return file, openErr
|
|
})
|
|
},
|
|
"read file": func(t *testing.T) {
|
|
testseam.Swap(t, &readTextInputAll, func(io.Reader) ([]byte, error) { return nil, errors.New("read file") })
|
|
},
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
run(t)
|
|
if _, err := ReadTextInput("@input.txt", nil, 10); err == nil {
|
|
t.Fatalf("%s failure ignored", name)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("resolved escape", func(t *testing.T) {
|
|
calls := 0
|
|
testseam.Swap(t, &localEvalSymlinks, func(path string) (string, error) {
|
|
calls++
|
|
if calls == 2 {
|
|
return filepath.Join(filepath.Dir(path), "..", "outside"), nil
|
|
}
|
|
return path, nil
|
|
})
|
|
if _, err := ReadTextInput("@input.txt", nil, 10); err == nil {
|
|
t.Fatal("resolved escape accepted")
|
|
}
|
|
})
|
|
}
|