Skip to content

Commit f1586da

Browse files
authored
Merge pull request #146 from apelisse/compatibility-tests
Allow tests to specify objects in different versions
2 parents 8e6097e + cafcf9a commit f1586da

File tree

11 files changed

+184
-88
lines changed

11 files changed

+184
-88
lines changed

internal/fixture/state.go

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,39 @@ import (
2626
"sigs.k8s.io/structured-merge-diff/v3/value"
2727
)
2828

29+
// For the sake of tests, a parser is something that can retrieve a
30+
// ParseableType.
31+
type Parser interface {
32+
Type(string) typed.ParseableType
33+
}
34+
35+
// SameVersionParser can be used if all the versions are actually using the same type.
36+
type SameVersionParser struct {
37+
T typed.ParseableType
38+
}
39+
40+
func (p SameVersionParser) Type(_ string) typed.ParseableType {
41+
return p.T
42+
}
43+
44+
// DeducedParser is a parser that is deduced no matter what the version
45+
// specified.
46+
var DeducedParser = SameVersionParser{
47+
T: typed.DeducedParseableType,
48+
}
49+
2950
// State of the current test in terms of live object. One can check at
3051
// any time that Live and Managers match the expectations.
52+
//
53+
// The parser will look for the type by using the APIVersion of the
54+
// object it's trying to parse. If trying to parse a "v1" object, a
55+
// corresponding "v1" type should exist in the schema. If all the
56+
// versions should map to the same type, or to a DeducedParseableType,
57+
// one can use the SameVersionParser or the DeducedParser types defined
58+
// in this package.
3159
type State struct {
3260
Live *typed.TypedValue
33-
Parser typed.ParseableType
61+
Parser Parser
3462
Managers fieldpath.ManagedFields
3563
Updater *merge.Updater
3664
}
@@ -70,9 +98,9 @@ func FixTabsOrDie(in typed.YAMLObject) typed.YAMLObject {
7098
return typed.YAMLObject(bytes.Join(lines, []byte{'\n'}))
7199
}
72100

73-
func (s *State) checkInit() error {
101+
func (s *State) checkInit(version fieldpath.APIVersion) error {
74102
if s.Live == nil {
75-
obj, err := s.Parser.FromUnstructured(nil)
103+
obj, err := s.Parser.Type(string(version)).FromUnstructured(nil)
76104
if err != nil {
77105
return fmt.Errorf("failed to create new empty object: %v", err)
78106
}
@@ -82,7 +110,7 @@ func (s *State) checkInit() error {
82110
}
83111

84112
func (s *State) UpdateObject(tv *typed.TypedValue, version fieldpath.APIVersion, manager string) error {
85-
err := s.checkInit()
113+
err := s.checkInit(version)
86114
if err != nil {
87115
return err
88116
}
@@ -102,15 +130,15 @@ func (s *State) UpdateObject(tv *typed.TypedValue, version fieldpath.APIVersion,
102130

103131
// Update the current state with the passed in object
104132
func (s *State) Update(obj typed.YAMLObject, version fieldpath.APIVersion, manager string) error {
105-
tv, err := s.Parser.FromYAML(FixTabsOrDie(obj))
133+
tv, err := s.Parser.Type(string(version)).FromYAML(FixTabsOrDie(obj))
106134
if err != nil {
107135
return err
108136
}
109137
return s.UpdateObject(tv, version, manager)
110138
}
111139

112140
func (s *State) ApplyObject(tv *typed.TypedValue, version fieldpath.APIVersion, manager string, force bool) error {
113-
err := s.checkInit()
141+
err := s.checkInit(version)
114142
if err != nil {
115143
return err
116144
}
@@ -130,7 +158,7 @@ func (s *State) ApplyObject(tv *typed.TypedValue, version fieldpath.APIVersion,
130158

131159
// Apply the passed in object to the current state
132160
func (s *State) Apply(obj typed.YAMLObject, version fieldpath.APIVersion, manager string, force bool) error {
133-
tv, err := s.Parser.FromYAML(FixTabsOrDie(obj))
161+
tv, err := s.Parser.Type(string(version)).FromYAML(FixTabsOrDie(obj))
134162
if err != nil {
135163
return err
136164
}
@@ -139,16 +167,20 @@ func (s *State) Apply(obj typed.YAMLObject, version fieldpath.APIVersion, manage
139167

140168
// CompareLive takes a YAML string and returns the comparison with the
141169
// current live object or an error.
142-
func (s *State) CompareLive(obj typed.YAMLObject) (*typed.Comparison, error) {
170+
func (s *State) CompareLive(obj typed.YAMLObject, version fieldpath.APIVersion) (*typed.Comparison, error) {
143171
obj = FixTabsOrDie(obj)
144-
if err := s.checkInit(); err != nil {
172+
if err := s.checkInit(version); err != nil {
173+
return nil, err
174+
}
175+
tv, err := s.Parser.Type(string(version)).FromYAML(obj)
176+
if err != nil {
145177
return nil, err
146178
}
147-
tv, err := s.Parser.FromYAML(obj)
179+
live, err := s.Updater.Converter.Convert(s.Live, version)
148180
if err != nil {
149181
return nil, err
150182
}
151-
return s.Live.Compare(tv)
183+
return live.Compare(tv)
152184
}
153185

154186
// dummyConverter doesn't convert, it just returns the same exact object, as long as a version is provided.
@@ -171,7 +203,7 @@ func (dummyConverter) IsMissingVersionError(err error) bool {
171203
// Operation is a step that will run when building a table-driven test.
172204
type Operation interface {
173205
run(*State) error
174-
preprocess(typed.ParseableType) (Operation, error)
206+
preprocess(Parser) (Operation, error)
175207
}
176208

177209
func hasConflict(conflicts merge.Conflicts, conflict merge.Conflict) bool {
@@ -214,8 +246,8 @@ func (a Apply) run(state *State) error {
214246
return p.run(state)
215247
}
216248

217-
func (a Apply) preprocess(parser typed.ParseableType) (Operation, error) {
218-
tv, err := parser.FromYAML(FixTabsOrDie(a.Object))
249+
func (a Apply) preprocess(parser Parser) (Operation, error) {
250+
tv, err := parser.Type(string(a.APIVersion)).FromYAML(FixTabsOrDie(a.Object))
219251
if err != nil {
220252
return nil, err
221253
}
@@ -260,7 +292,7 @@ func (a ApplyObject) run(state *State) error {
260292
return nil
261293
}
262294

263-
func (a ApplyObject) preprocess(parser typed.ParseableType) (Operation, error) {
295+
func (a ApplyObject) preprocess(parser Parser) (Operation, error) {
264296
return a, nil
265297
}
266298

@@ -278,8 +310,8 @@ func (f ForceApply) run(state *State) error {
278310
return state.Apply(f.Object, f.APIVersion, f.Manager, true)
279311
}
280312

281-
func (f ForceApply) preprocess(parser typed.ParseableType) (Operation, error) {
282-
tv, err := parser.FromYAML(FixTabsOrDie(f.Object))
313+
func (f ForceApply) preprocess(parser Parser) (Operation, error) {
314+
tv, err := parser.Type(string(f.APIVersion)).FromYAML(FixTabsOrDie(f.Object))
283315
if err != nil {
284316
return nil, err
285317
}
@@ -304,7 +336,7 @@ func (f ForceApplyObject) run(state *State) error {
304336
return state.ApplyObject(f.Object, f.APIVersion, f.Manager, true)
305337
}
306338

307-
func (f ForceApplyObject) preprocess(parser typed.ParseableType) (Operation, error) {
339+
func (f ForceApplyObject) preprocess(parser Parser) (Operation, error) {
308340
return f, nil
309341
}
310342

@@ -322,8 +354,8 @@ func (u Update) run(state *State) error {
322354
return state.Update(u.Object, u.APIVersion, u.Manager)
323355
}
324356

325-
func (u Update) preprocess(parser typed.ParseableType) (Operation, error) {
326-
tv, err := parser.FromYAML(FixTabsOrDie(u.Object))
357+
func (u Update) preprocess(parser Parser) (Operation, error) {
358+
tv, err := parser.Type(string(u.APIVersion)).FromYAML(FixTabsOrDie(u.Object))
327359
if err != nil {
328360
return nil, err
329361
}
@@ -348,7 +380,7 @@ func (u UpdateObject) run(state *State) error {
348380
return state.UpdateObject(u.Object, u.APIVersion, u.Manager)
349381
}
350382

351-
func (f UpdateObject) preprocess(parser typed.ParseableType) (Operation, error) {
383+
func (f UpdateObject) preprocess(parser Parser) (Operation, error) {
352384
return f, nil
353385
}
354386

@@ -364,6 +396,9 @@ type TestCase struct {
364396
// Object, if not empty, is the object as it's expected to
365397
// be after all the operations are run.
366398
Object typed.YAMLObject
399+
// APIVersion should be set if the object is non-empty and
400+
// describes the version of the object to compare to.
401+
APIVersion fieldpath.APIVersion
367402
// Managed, if not nil, is the ManagedFields as expected
368403
// after all operations are run.
369404
Managed fieldpath.ManagedFields
@@ -372,18 +407,18 @@ type TestCase struct {
372407
}
373408

374409
// Test runs the test-case using the given parser and a dummy converter.
375-
func (tc TestCase) Test(parser typed.ParseableType) error {
410+
func (tc TestCase) Test(parser Parser) error {
376411
return tc.TestWithConverter(parser, &dummyConverter{})
377412
}
378413

379414
// Bench runs the test-case using the given parser and a dummy converter, but
380415
// doesn't check exit conditions--see the comment for BenchWithConverter.
381-
func (tc TestCase) Bench(parser typed.ParseableType) error {
416+
func (tc TestCase) Bench(parser Parser) error {
382417
return tc.BenchWithConverter(parser, &dummyConverter{})
383418
}
384419

385420
// Preprocess all the operations by parsing the yaml before-hand.
386-
func (tc TestCase) PreprocessOperations(parser typed.ParseableType) error {
421+
func (tc TestCase) PreprocessOperations(parser Parser) error {
387422
for i := range tc.Ops {
388423
op, err := tc.Ops[i].preprocess(parser)
389424
if err != nil {
@@ -398,7 +433,7 @@ func (tc TestCase) PreprocessOperations(parser typed.ParseableType) error {
398433
// but doesn't do any comparison operations aftewards; you should probably run
399434
// TestWithConverter once and reset the benchmark, to make sure the test case
400435
// actually passes..
401-
func (tc TestCase) BenchWithConverter(parser typed.ParseableType, converter merge.Converter) error {
436+
func (tc TestCase) BenchWithConverter(parser Parser, converter merge.Converter) error {
402437
state := State{
403438
Updater: &merge.Updater{Converter: converter},
404439
Parser: parser,
@@ -418,16 +453,14 @@ func (tc TestCase) BenchWithConverter(parser typed.ParseableType, converter merg
418453
}
419454

420455
// TestWithConverter runs the test-case using the given parser and converter.
421-
func (tc TestCase) TestWithConverter(parser typed.ParseableType, converter merge.Converter) error {
456+
func (tc TestCase) TestWithConverter(parser Parser, converter merge.Converter) error {
422457
state := State{
423458
Updater: &merge.Updater{Converter: converter},
424459
Parser: parser,
425460
}
426461
if tc.RequiresUnions {
427462
state.Updater.EnableUnionFeature()
428463
}
429-
// We currently don't have any test that converts, we can take
430-
// care of that later.
431464
for i, ops := range tc.Ops {
432465
err := ops.run(&state)
433466
if err != nil {
@@ -437,7 +470,7 @@ func (tc TestCase) TestWithConverter(parser typed.ParseableType, converter merge
437470

438471
// If LastObject was specified, compare it with LiveState
439472
if tc.Object != typed.YAMLObject("") {
440-
comparison, err := state.CompareLive(tc.Object)
473+
comparison, err := state.CompareLive(tc.Object, tc.APIVersion)
441474
if err != nil {
442475
return fmt.Errorf("failed to compare live with config: %v", err)
443476
}

0 commit comments

Comments
 (0)