Repository navigation
Expand file tree
/
Copy pathsave_test.go
More file actions
152 lines (137 loc) · 5.01 KB
/
Copy pathsave_test.go
File metadata and controls
152 lines (137 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main
import (
"bytes"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
const argvSep = "\x1f"
// runFxArgs runs fx with args as given and stdin from /dev/null.
func runFxArgs(t *testing.T, args ...string) (stdout, stderr string, exitCode int) {
t.Helper()
cmd := exec.Command(os.Args[0], "-test.run=^$")
cmd.Env = append(os.Environ(), "FX_TEST_RUN_MAIN=1", "FX_TEST_ARGV="+strings.Join(args, argvSep))
devNull, err := os.Open(os.DevNull)
require.NoError(t, err)
t.Cleanup(func() { devNull.Close() })
cmd.Stdin = devNull
var out, errOut bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &errOut
err = cmd.Run()
if exitErr, ok := err.(*exec.ExitError); ok {
exitCode = exitErr.ExitCode()
} else {
require.NoError(t, err)
}
return out.String(), errOut.String(), exitCode
}
// save rewrites the file the parser is still reading. The rest of the input
// must be the original file, not the new content from the old offset.
func TestSaveFileMode(t *testing.T) {
tests := []struct {
name, input, query, want string
}{
{"longer", `{"name":"a"}` + "\n", `x.name = x.name.toUpperCase(), x`, "{\n \"name\": \"A\"\n}\n"},
{"longer number", `{"b":1}` + "\n", `x.b = 7, x`, "{\n \"b\": 7\n}\n"},
{"spread", `{"b":1}` + "\n", `{...x, b: 8}`, "{\n \"b\": 8\n}\n"},
{"shorter", `{"a":1,"b":2,"c":3,"d":4}`, `({a: x.a})`, "{\n \"a\": 1\n}\n"},
{"shorter pretty", "{\n \"a\": 1,\n \"long\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n}\n", `({a: x.a})`, "{\n \"a\": 1\n}\n"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
file := filepath.Join(t.TempDir(), "f.json")
require.NoError(t, os.WriteFile(file, []byte(tt.input), 0o644))
stdout, stderr, code := runFxArgs(t, file, tt.query, "save")
require.Equal(t, 0, code, stderr)
require.Empty(t, stderr)
require.Equal(t, tt.want, stdout)
data, err := os.ReadFile(file)
require.NoError(t, err)
require.Equal(t, tt.want, string(data))
})
}
}
func TestSaveKeepsFileMode(t *testing.T) {
dir := t.TempDir()
file := filepath.Join(dir, "f.json")
require.NoError(t, os.WriteFile(file, []byte(`{"a":1}`), 0o600))
require.NoError(t, os.Chmod(file, 0o600))
_, stderr, code := runFxArgs(t, file, `x.a = 2, x`, "save")
require.Equal(t, 0, code, stderr)
info, err := os.Stat(file)
require.NoError(t, err)
require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
entries, err := os.ReadDir(dir)
require.NoError(t, err)
require.Len(t, entries, 1, "temp file left behind")
}
func TestSaveRefusesSeveralValues(t *testing.T) {
tests := []struct {
name, file, input string
args []string
}{
{"json lines", "f.jsonl", "{\"a\":1}\n{\"a\":2}\n", nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
file := filepath.Join(t.TempDir(), tt.file)
require.NoError(t, os.WriteFile(file, []byte(tt.input), 0o644))
args := append(tt.args, file, "save")
stdout, stderr, code := runFxArgs(t, args...)
require.Equal(t, 1, code)
require.Empty(t, stdout)
require.Contains(t, stderr, "save supports a single JSON value")
data, err := os.ReadFile(file)
require.NoError(t, err)
require.Equal(t, tt.input, string(data), "file must be untouched")
})
}
}
// Retrying save() after the lookahead failed must not overwrite the input.
func TestSaveRefusesMalformedRest(t *testing.T) {
const input = `{"a":1} /* unfinished`
file := filepath.Join(t.TempDir(), "f.json")
require.NoError(t, os.WriteFile(file, []byte(input), 0o644))
stdout, stderr, code := runFxArgs(t, file, `x => { try { save(x) } catch (e) {} return save(x) }`)
require.Equal(t, 1, code)
require.Empty(t, stdout)
require.Contains(t, stderr, "Unexpected end of input in comment")
data, err := os.ReadFile(file)
require.NoError(t, err)
require.Equal(t, input, string(data), "file must be untouched")
}
// save() writes JSON: it must not replace a file read as another format,
// as :w doesn't.
func TestSaveRefusesNonJSONFile(t *testing.T) {
tests := []struct {
name, file, input, format string
args []string
}{
{"toml", "f.toml", "a = 1\n", "TOML", nil},
{"yaml", "f.yaml", "a: 1\n", "YAML", nil},
{"yaml stream", "f.yaml", "a: 1\n---\na: 2\n", "YAML", nil},
{"edn", "f.edn", "{:a 1}\n", "EDN", nil},
{"xml", "f.xml", "<r><a>1</a></r>\n", "XML", nil},
{"yaml flag", "f.json", "a: 1\n", "YAML", []string{"--yaml"}},
{"raw line", "f.txt", "hello\n", "raw text", []string{"-r"}},
{"raw lines", "f.txt", "a\nb\n", "raw text", []string{"-r"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
file := filepath.Join(t.TempDir(), tt.file)
require.NoError(t, os.WriteFile(file, []byte(tt.input), 0o644))
args := append(tt.args, file, "save")
stdout, stderr, code := runFxArgs(t, args...)
require.Equal(t, 1, code)
require.Empty(t, stdout)
require.Contains(t, stderr, "save writes JSON, but "+file+" is "+tt.format)
data, err := os.ReadFile(file)
require.NoError(t, err)
require.Equal(t, tt.input, string(data), "file must be untouched")
})
}
}