Skip to content

Commit a347389

Browse files
committed
support test op definition
1 parent 8f82a93 commit a347389

File tree

2 files changed

+165
-17
lines changed

2 files changed

+165
-17
lines changed

patch/op_definition.go

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import (
88

99
// OpDefinition struct is useful for JSON and YAML unmarshaling
1010
type OpDefinition struct {
11-
Type string `json:",omitempty" yaml:",omitempty"`
12-
Path *string `json:",omitempty" yaml:",omitempty"`
13-
Value *interface{} `json:",omitempty" yaml:",omitempty"`
14-
15-
Error *string `json:",omitempty" yaml:",omitempty"`
11+
Type string `json:",omitempty" yaml:",omitempty"`
12+
Path *string `json:",omitempty" yaml:",omitempty"`
13+
Value *interface{} `json:",omitempty" yaml:",omitempty"`
14+
Absent *bool `json:",omitempty" yaml:",omitempty"`
15+
Error *string `json:",omitempty" yaml:",omitempty"`
1616
}
1717

1818
type parser struct{}
@@ -40,6 +40,12 @@ func NewOpsFromDefinitions(opDefs []OpDefinition) (Ops, error) {
4040
return nil, fmt.Errorf("Remove operation [%d]: %s within\n%s", i, err, opFmt)
4141
}
4242

43+
case "test":
44+
op, err = p.newTestOp(opDef)
45+
if err != nil {
46+
return nil, fmt.Errorf("Test operation [%d]: %s within\n%s", i, err, opFmt)
47+
}
48+
4349
default:
4450
return nil, fmt.Errorf("Unknown operation [%d] with type '%s' within\n%s", i, opDef.Type, opFmt)
4551
}
@@ -88,6 +94,33 @@ func (parser) newRemoveOp(opDef OpDefinition) (RemoveOp, error) {
8894
return RemoveOp{Path: ptr}, nil
8995
}
9096

97+
func (parser) newTestOp(opDef OpDefinition) (TestOp, error) {
98+
if opDef.Path == nil {
99+
return TestOp{}, fmt.Errorf("Missing path")
100+
}
101+
102+
if opDef.Value == nil && opDef.Absent == nil {
103+
return TestOp{}, fmt.Errorf("Missing value or absent")
104+
}
105+
106+
ptr, err := NewPointerFromString(*opDef.Path)
107+
if err != nil {
108+
return TestOp{}, fmt.Errorf("Invalid path: %s", err)
109+
}
110+
111+
op := TestOp{Path: ptr}
112+
113+
if opDef.Value != nil {
114+
op.Value = *opDef.Value
115+
}
116+
117+
if opDef.Absent != nil {
118+
op.Absent = *opDef.Absent
119+
}
120+
121+
return op, nil
122+
}
123+
91124
func (parser) fmtOpDef(opDef OpDefinition) string {
92125
var (
93126
redactedVal interface{} = "<redacted>"
@@ -134,11 +167,18 @@ func NewOpDefinitionsFromOps(ops Ops) ([]OpDefinition, error) {
134167
path := typedOp.Path.String()
135168
val := typedOp.Value
136169

137-
opDefs = append(opDefs, OpDefinition{
138-
Type: "test",
139-
Path: &path,
140-
Value: &val,
141-
})
170+
opDef := OpDefinition{
171+
Type: "test",
172+
Path: &path,
173+
}
174+
175+
if typedOp.Absent {
176+
opDef.Absent = &typedOp.Absent
177+
} else {
178+
opDef.Value = &val
179+
}
180+
181+
opDefs = append(opDefs, opDef)
142182

143183
default:
144184
return nil, fmt.Errorf("Unknown operation [%d] with type '%t'", i, op)

patch/op_definition_test.go

Lines changed: 115 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package patch_test
22

33
import (
4+
"encoding/json"
5+
46
. "github.com/onsi/ginkgo"
57
. "github.com/onsi/gomega"
8+
"gopkg.in/yaml.v2"
69

710
. "github.com/cppforlife/go-patch/patch"
811
)
@@ -14,12 +17,15 @@ var _ = Describe("NewOpsFromDefinitions", func() {
1417
errorMsg = "error"
1518
val interface{} = 123
1619
complexVal interface{} = map[interface{}]interface{}{123: 123}
20+
trueBool = true
1721
)
1822

19-
It("supports 'replace' and 'remove' operations", func() {
23+
It("supports 'replace', 'remove', 'test' operations", func() {
2024
opDefs := []OpDefinition{
2125
{Type: "replace", Path: &path, Value: &val},
2226
{Type: "remove", Path: &path},
27+
{Type: "test", Path: &path, Value: &val},
28+
{Type: "test", Path: &path, Absent: &trueBool},
2329
}
2430

2531
ops, err := NewOpsFromDefinitions(opDefs)
@@ -28,15 +34,17 @@ var _ = Describe("NewOpsFromDefinitions", func() {
2834
Expect(ops).To(Equal(Ops([]Op{
2935
ReplaceOp{Path: MustNewPointerFromString("/abc"), Value: 123},
3036
RemoveOp{Path: MustNewPointerFromString("/abc")},
37+
TestOp{Path: MustNewPointerFromString("/abc"), Value: 123},
38+
TestOp{Path: MustNewPointerFromString("/abc"), Absent: true},
3139
})))
3240
})
3341

3442
It("returns error if operation type is unknown", func() {
35-
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test"}})
43+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "op"}})
3644
Expect(err).To(HaveOccurred())
37-
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'test' within
45+
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'op' within
3846
{
39-
"Type": "test"
47+
"Type": "op"
4048
}`))
4149
})
4250

@@ -47,11 +55,11 @@ var _ = Describe("NewOpsFromDefinitions", func() {
4755
})
4856

4957
It("allows values to be complex in error messages", func() {
50-
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test", Path: &invalidPath, Value: &complexVal}})
58+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "op", Path: &invalidPath, Value: &complexVal}})
5159
Expect(err).To(HaveOccurred())
52-
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'test' within
60+
Expect(err.Error()).To(Equal(`Unknown operation [0] with type 'op' within
5361
{
54-
"Type": "test",
62+
"Type": "op",
5563
"Path": "abc",
5664
"Value": "<redacted>"
5765
}`))
@@ -148,4 +156,104 @@ var _ = Describe("NewOpsFromDefinitions", func() {
148156
}`))
149157
})
150158
})
159+
160+
Describe("test", func() {
161+
It("allows error description", func() {
162+
opDefs := []OpDefinition{{Type: "test", Path: &path, Value: &val, Error: &errorMsg}}
163+
164+
ops, err := NewOpsFromDefinitions(opDefs)
165+
Expect(err).ToNot(HaveOccurred())
166+
167+
Expect(ops).To(Equal(Ops([]Op{
168+
DescriptiveOp{
169+
Op: TestOp{Path: MustNewPointerFromString("/abc"), Value: 123},
170+
ErrorMsg: errorMsg,
171+
},
172+
})))
173+
})
174+
175+
It("requires path", func() {
176+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test"}})
177+
Expect(err).To(HaveOccurred())
178+
Expect(err.Error()).To(Equal(`Test operation [0]: Missing path within
179+
{
180+
"Type": "test"
181+
}`))
182+
})
183+
184+
It("requires value or absent flag", func() {
185+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test", Path: &path}})
186+
Expect(err).To(HaveOccurred())
187+
Expect(err.Error()).To(Equal(`Test operation [0]: Missing value or absent within
188+
{
189+
"Type": "test",
190+
"Path": "/abc"
191+
}`))
192+
})
193+
194+
It("requires valid path", func() {
195+
_, err := NewOpsFromDefinitions([]OpDefinition{{Type: "test", Path: &invalidPath, Value: &val}})
196+
Expect(err).To(HaveOccurred())
197+
Expect(err.Error()).To(Equal(`Test operation [0]: Invalid path: Expected to start with '/' within
198+
{
199+
"Type": "test",
200+
"Path": "abc",
201+
"Value": "<redacted>"
202+
}`))
203+
})
204+
})
205+
})
206+
207+
var _ = Describe("NewOpDefinitionsFromOps", func() {
208+
It("supports 'replace', 'remove', 'test' operations serialized", func() {
209+
ops := Ops([]Op{
210+
ReplaceOp{Path: MustNewPointerFromString("/abc"), Value: 123},
211+
RemoveOp{Path: MustNewPointerFromString("/abc")},
212+
TestOp{Path: MustNewPointerFromString("/abc"), Value: 123},
213+
TestOp{Path: MustNewPointerFromString("/abc"), Absent: true},
214+
})
215+
216+
opDefs, err := NewOpDefinitionsFromOps(ops)
217+
Expect(err).ToNot(HaveOccurred())
218+
219+
bs, err := yaml.Marshal(opDefs)
220+
Expect(err).ToNot(HaveOccurred())
221+
222+
Expect("\n" + string(bs)).To(Equal(`
223+
- type: replace
224+
path: /abc
225+
value: 123
226+
- type: remove
227+
path: /abc
228+
- type: test
229+
path: /abc
230+
value: 123
231+
- type: test
232+
path: /abc
233+
absent: true
234+
`))
235+
236+
bs, err = json.MarshalIndent(opDefs, "", " ")
237+
Expect(string(bs)).To(Equal(`[
238+
{
239+
"Type": "replace",
240+
"Path": "/abc",
241+
"Value": 123
242+
},
243+
{
244+
"Type": "remove",
245+
"Path": "/abc"
246+
},
247+
{
248+
"Type": "test",
249+
"Path": "/abc",
250+
"Value": 123
251+
},
252+
{
253+
"Type": "test",
254+
"Path": "/abc",
255+
"Absent": true
256+
}
257+
]`))
258+
})
151259
})

0 commit comments

Comments
 (0)