Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
735ef6b3f2 |
@@ -27,6 +27,10 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// markdownStatUpload stats the prepared upload file. Tests override it to
|
||||||
|
// deterministically simulate the file disappearing between write and stat.
|
||||||
|
var markdownStatUpload = os.Stat
|
||||||
|
|
||||||
func newMarkdownCommand() *cobra.Command {
|
func newMarkdownCommand() *cobra.Command {
|
||||||
root := &cobra.Command{
|
root := &cobra.Command{
|
||||||
Use: "markdown",
|
Use: "markdown",
|
||||||
@@ -257,7 +261,7 @@ func runMarkdownCreate(cmd *cobra.Command, _ []string) error {
|
|||||||
if !hasMarkdownExtension(nameFlag) {
|
if !hasMarkdownExtension(nameFlag) {
|
||||||
return fmt.Errorf("--name 必须以 .md 结尾,当前: %s", nameFlag)
|
return fmt.Errorf("--name 必须以 .md 结尾,当前: %s", nameFlag)
|
||||||
}
|
}
|
||||||
info, err := os.Stat(uploadPath)
|
info, err := markdownStatUpload(uploadPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("读取上传文件失败: %w", err)
|
return fmt.Errorf("读取上传文件失败: %w", err)
|
||||||
}
|
}
|
||||||
@@ -421,7 +425,7 @@ func runMarkdownOverwrite(cmd *cobra.Command, _ []string) error {
|
|||||||
if !hasMarkdownExtension(nameFlag) {
|
if !hasMarkdownExtension(nameFlag) {
|
||||||
return fmt.Errorf("--name 必须以 .md 结尾,当前: %s", nameFlag)
|
return fmt.Errorf("--name 必须以 .md 结尾,当前: %s", nameFlag)
|
||||||
}
|
}
|
||||||
info, err := os.Stat(uploadPath)
|
info, err := markdownStatUpload(uploadPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("读取上传文件失败: %w", err)
|
return fmt.Errorf("读取上传文件失败: %w", err)
|
||||||
}
|
}
|
||||||
@@ -563,7 +567,7 @@ func runMarkdownPatch(cmd *cobra.Command, _ []string) error {
|
|||||||
if err := os.WriteFile(uploadPath, []byte(newContent), 0o600); err != nil {
|
if err := os.WriteFile(uploadPath, []byte(newContent), 0o600); err != nil {
|
||||||
return fmt.Errorf("写入临时文件失败: %w", err)
|
return fmt.Errorf("写入临时文件失败: %w", err)
|
||||||
}
|
}
|
||||||
info, err := os.Stat(uploadPath)
|
info, err := markdownStatUpload(uploadPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("读取临时文件失败: %w", err)
|
return fmt.Errorf("读取临时文件失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -26,47 +24,20 @@ func (markdownCIFailingReader) Read([]byte) (int, error) {
|
|||||||
return 0, errors.New("forced read failure")
|
return 0, errors.New("forced read failure")
|
||||||
}
|
}
|
||||||
|
|
||||||
func startMarkdownCITempFileRemover(t *testing.T, root, dirPrefix, fileName string) <-chan error {
|
// installMarkdownStatUploadRemover overrides the markdownStatUpload seam so
|
||||||
|
// the prepared upload file is deterministically removed before stat runs,
|
||||||
|
// simulating the file disappearing between write and stat without racing a
|
||||||
|
// background goroutine.
|
||||||
|
func installMarkdownStatUploadRemover(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
result := make(chan error, 1)
|
original := markdownStatUpload
|
||||||
go func() {
|
markdownStatUpload = func(name string) (os.FileInfo, error) {
|
||||||
deadline := time.Now().Add(10 * time.Second)
|
if err := os.Remove(name); err != nil && !os.IsNotExist(err) {
|
||||||
for time.Now().Before(deadline) {
|
t.Errorf("removing upload file %q: %v", name, err)
|
||||||
entries, err := os.ReadDir(root)
|
|
||||||
if err != nil {
|
|
||||||
result <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, entry := range entries {
|
|
||||||
if !entry.IsDir() || !strings.HasPrefix(entry.Name(), dirPrefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
target := filepath.Join(root, entry.Name(), fileName)
|
|
||||||
if err := os.Remove(target); err == nil {
|
|
||||||
result <- nil
|
|
||||||
return
|
|
||||||
} else if !os.IsNotExist(err) {
|
|
||||||
result <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runtime.Gosched()
|
|
||||||
}
|
}
|
||||||
result <- fmt.Errorf("timed out waiting for %s/%s", dirPrefix, fileName)
|
return os.Stat(name)
|
||||||
}()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitMarkdownCIRemover(t *testing.T, result <-chan error) {
|
|
||||||
t.Helper()
|
|
||||||
select {
|
|
||||||
case err := <-result:
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
case <-time.After(12 * time.Second):
|
|
||||||
t.Fatal("timed out waiting for temporary-file remover")
|
|
||||||
}
|
}
|
||||||
|
t.Cleanup(func() { markdownStatUpload = original })
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarkdownCICoverageFetchAndOutputPaths(t *testing.T) {
|
func TestMarkdownCICoverageFetchAndOutputPaths(t *testing.T) {
|
||||||
@@ -189,13 +160,9 @@ func TestMarkdownCICoverageCreateEdges(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("temporary file disappears before stat", func(t *testing.T) {
|
t.Run("temporary file disappears before stat", func(t *testing.T) {
|
||||||
installMarkdownDriveDeps(t, &markdownDriveCaller{format: "json"})
|
installMarkdownDriveDeps(t, &markdownDriveCaller{format: "json"})
|
||||||
tempRoot := t.TempDir()
|
installMarkdownStatUploadRemover(t)
|
||||||
t.Setenv("TMPDIR", tempRoot)
|
|
||||||
removed := startMarkdownCITempFileRemover(t, tempRoot, "dws-markdown-create-", "a.md")
|
|
||||||
content := strings.Repeat("x", 16<<20)
|
|
||||||
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
||||||
"markdown", "create", "--name", "a.md", "--content", content)
|
"markdown", "create", "--name", "a.md", "--content", "body")
|
||||||
waitMarkdownCIRemover(t, removed)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "读取上传文件失败") {
|
if err == nil || !strings.Contains(err.Error(), "读取上传文件失败") {
|
||||||
t.Fatalf("error = %v", err)
|
t.Fatalf("error = %v", err)
|
||||||
}
|
}
|
||||||
@@ -270,14 +237,10 @@ func TestMarkdownCICoverageOverwriteEdges(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("temporary file disappears before stat", func(t *testing.T) {
|
t.Run("temporary file disappears before stat", func(t *testing.T) {
|
||||||
installMarkdownDriveDeps(t, &markdownDriveCaller{format: "json"})
|
installMarkdownDriveDeps(t, &markdownDriveCaller{format: "json"})
|
||||||
tempRoot := t.TempDir()
|
installMarkdownStatUploadRemover(t)
|
||||||
t.Setenv("TMPDIR", tempRoot)
|
|
||||||
removed := startMarkdownCITempFileRemover(t, tempRoot, "dws-markdown-overwrite-", "a.md")
|
|
||||||
content := strings.Repeat("x", 16<<20)
|
|
||||||
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
||||||
"markdown", "overwrite", "--node", "node-1", "--space-id", "space-1",
|
"markdown", "overwrite", "--node", "node-1", "--space-id", "space-1",
|
||||||
"--name", "a.md", "--content", content)
|
"--name", "a.md", "--content", "body")
|
||||||
waitMarkdownCIRemover(t, removed)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "读取上传文件失败") {
|
if err == nil || !strings.Contains(err.Error(), "读取上传文件失败") {
|
||||||
t.Fatalf("error = %v", err)
|
t.Fatalf("error = %v", err)
|
||||||
}
|
}
|
||||||
@@ -451,14 +414,10 @@ func TestMarkdownCICoveragePatchEdges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
installMarkdownDriveDeps(t, caller)
|
installMarkdownDriveDeps(t, caller)
|
||||||
installMarkdownHTTPGet(t, "old")
|
installMarkdownHTTPGet(t, "old")
|
||||||
tempRoot := t.TempDir()
|
installMarkdownStatUploadRemover(t)
|
||||||
t.Setenv("TMPDIR", tempRoot)
|
|
||||||
removed := startMarkdownCITempFileRemover(t, tempRoot, "dws-markdown-patch-", "current.md")
|
|
||||||
replacement := strings.Repeat("x", 16<<20)
|
|
||||||
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
err := executeMarkdownDriveCommand(t, newMarkdownCommand(), nil,
|
||||||
"markdown", "patch", "--node", "node-1", "--space-id", "space-1",
|
"markdown", "patch", "--node", "node-1", "--space-id", "space-1",
|
||||||
"--pattern", "old", "--content", replacement, "--yes")
|
"--pattern", "old", "--content", "new", "--yes")
|
||||||
waitMarkdownCIRemover(t, removed)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "读取临时文件失败") {
|
if err == nil || !strings.Contains(err.Error(), "读取临时文件失败") {
|
||||||
t.Fatalf("error = %v", err)
|
t.Fatalf("error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user