Files
dingtalk-workspace-cli/internal/localio/input_test.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")
}
})
}