Skip to content

Commit

Permalink
chore(refactor): rearrange some code and test files
Browse files Browse the repository at this point in the history
  • Loading branch information
Tieske committed Jan 5, 2024
1 parent 652aea1 commit 4157f7b
Show file tree
Hide file tree
Showing 7 changed files with 525 additions and 450 deletions.
136 changes: 136 additions & 0 deletions deckformat/compatibility.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package deckformat

import (
"errors"
"fmt"
"strconv"
"strings"

"github.com/kong/go-apiops/jsonbasics"
)

//
//
// section on compatibility and versioning between deckfiles
//
//

// CompatibleTransform checks if 2 files are compatible, by '_transform' keys.
// Returns nil if compatible, and error otherwise.
func CompatibleTransform(data1 map[string]interface{}, data2 map[string]interface{}) error {
if data1 == nil {
panic("expected 'data1' to be non-nil")
}
if data2 == nil {
panic("expected 'data2' to be non-nil")
}

transform1 := true // this is the default value
if data1[TransformKey] != nil {
var err error
if transform1, err = jsonbasics.GetBoolField(data1, TransformKey); err != nil {
return err
}
}
transform2 := true // this is the default value
if data2[TransformKey] != nil {
var err error
if transform2, err = jsonbasics.GetBoolField(data2, TransformKey); err != nil {
return err
}
}

if transform1 != transform2 {
return errors.New("files with '" + TransformKey + ": true' (default) and '" +
TransformKey + ": false' are not compatible")
}

return nil
}

// CompatibleVersion checks if 2 files are compatible, by '_format_version'. Version is compatible
// if they are the same major. Missing versions are assumed to be compatible.
// Returns nil if compatible, and error otherwise.
func CompatibleVersion(data1 map[string]interface{}, data2 map[string]interface{}) error {
if data1 == nil {
panic("expected 'data1' to be non-nil")
}
if data2 == nil {
panic("expected 'data2' to be non-nil")
}

if data1[VersionKey] == nil {
if data2[VersionKey] == nil {
return nil // neither given , so assume compatible
}
// data1 omitted, just validate data2 has a proper version, any version will do
_, _, err := ParseFormatVersion(data2)
return err
}

// data1 has a version
if data2[VersionKey] == nil {
// data2 omitted, just validate data1 has a proper version, any version will do
_, _, err := ParseFormatVersion(data1)
return err
}

// both versions given, go parse them
major1, minor1, err1 := ParseFormatVersion(data1)
if err1 != nil {
return err1
}
major2, minor2, err2 := ParseFormatVersion(data2)
if err2 != nil {
return err2
}

if major1 != major2 {
return fmt.Errorf("major versions are incompatible; %d.%d and %d.%d", major1, minor1, major2, minor2)
}

return nil
}

// CompatibleFile returns nil if the files are compatible. An error otherwise.
// see CompatibleVersion and CompatibleTransform for what compatibility means.
func CompatibleFile(data1 map[string]interface{}, data2 map[string]interface{}) error {
err := CompatibleTransform(data1, data2)
if err != nil {
return fmt.Errorf("files are incompatible; %w", err)
}
err = CompatibleVersion(data1, data2)
if err != nil {
return fmt.Errorf("files are incompatible; %w", err)
}
return nil
}

// parseFormatVersion parses field `_format_version` and returns major+minor.
// Field must be present, a string, and have an 'x.y' format. Returns an error otherwise.
func ParseFormatVersion(data map[string]interface{}) (int, int, error) {
// get the file version and check it
v, err := jsonbasics.GetStringField(data, VersionKey)
if err != nil {
return 0, 0, errors.New("expected field '." + VersionKey + "' to be a string in 'x.y' format")
}
elem := strings.Split(v, ".")
if len(elem) > 2 {
return 0, 0, errors.New("expected field '." + VersionKey + "' to be a string in 'x.y' format")
}

majorVersion, err := strconv.Atoi(elem[0])
if err != nil {
return 0, 0, errors.New("expected field '." + VersionKey + "' to be a string in 'x.y' format")
}

minorVersion := 0
if len(elem) > 1 {
minorVersion, err = strconv.Atoi(elem[1])
if err != nil {
return 0, 0, errors.New("expected field '." + VersionKey + "' to be a string in 'x.y' format")
}
}

return majorVersion, minorVersion, nil
}
164 changes: 164 additions & 0 deletions deckformat/compatibility_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package deckformat_test

import (
. "github.com/kong/go-apiops/deckformat"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("deckformat", func() {
Describe("ParseFormatVersion", func() {
It("parses a version", func() {
data := map[string]interface{}{
VersionKey: "123.456",
}

major, minor, err := ParseFormatVersion(data)
Expect(major).To(Equal(123))
Expect(minor).To(Equal(456))
Expect(err).To(BeNil())
})

It("returns minor = 0 if omitted", func() {
data := map[string]interface{}{
VersionKey: "123",
}

major, minor, err := ParseFormatVersion(data)
Expect(major).To(Equal(123))
Expect(minor).To(Equal(0))
Expect(err).To(BeNil())
})

Describe("returns an error if the version", func() {
It("has more than 2 segments", func() {
data := map[string]interface{}{
VersionKey: "123.456.789",
}

major, minor, err := ParseFormatVersion(data)
Expect(err).To(MatchError("expected field '._format_version' to be a string in 'x.y' format"))
Expect(major).To(Equal(0))
Expect(minor).To(Equal(0))
})

It("has a non-numeric major", func() {
data := map[string]interface{}{
VersionKey: "abc.456",
}

major, minor, err := ParseFormatVersion(data)
Expect(err).To(MatchError("expected field '._format_version' to be a string in 'x.y' format"))
Expect(major).To(Equal(0))
Expect(minor).To(Equal(0))
})

It("has a non-numeric minor", func() {
data := map[string]interface{}{
VersionKey: "123.def",
}

major, minor, err := ParseFormatVersion(data)
Expect(err).To(MatchError("expected field '._format_version' to be a string in 'x.y' format"))
Expect(major).To(Equal(0))
Expect(minor).To(Equal(0))
})

It("doesn't exist", func() {
data := map[string]interface{}{}

major, minor, err := ParseFormatVersion(data)
Expect(err).To(MatchError("expected field '._format_version' to be a string in 'x.y' format"))
Expect(major).To(Equal(0))
Expect(minor).To(Equal(0))
})

It("doesn't exist, because data is nil", func() {
major, minor, err := ParseFormatVersion(nil)
Expect(err).To(MatchError("expected field '._format_version' to be a string in 'x.y' format"))
Expect(major).To(Equal(0))
Expect(minor).To(Equal(0))
})
})
})

Describe("compatibility", func() {
DescribeTable("CompatibleTransform",
func(transform1 interface{}, transform2 interface{}, expected bool) {
res := CompatibleTransform(
map[string]interface{}{TransformKey: transform1},
map[string]interface{}{TransformKey: transform2},
)
if expected {
// compatible, then result is nil
Expect(res).To(BeNil())
} else {
// not-compatible, then result is an error
Expect(res).Should(HaveOccurred())
}
},
// transform1, transform2, expected
Entry("1", true, false, false),
Entry("2", true, true, true),
Entry("3", true, nil, true),
Entry("4", false, false, true),
Entry("5", false, true, false),
Entry("6", false, nil, false),
Entry("7", nil, false, false),
Entry("8", nil, true, true),
Entry("9", nil, nil, true),
)

DescribeTable("CompatibleVersion",
func(version1 interface{}, version2 interface{}, expected bool) {
res := CompatibleVersion(
map[string]interface{}{VersionKey: version1},
map[string]interface{}{VersionKey: version2},
)
if expected {
// compatible, then result is nil
Expect(res).To(BeNil())
} else {
// not-compatible, then result is an error
Expect(res).Should(HaveOccurred())
}
},
// version1, version2, expected
Entry("same major is compatible", "1.1", "1.2", true),
Entry("different major is incompatible", "1.1", "2.1", false),
Entry("omitted version is compatible 1", "1.1", nil, true),
Entry("omitted version is compatible 2", nil, "1.1", true),
Entry("omitted version is compatible 3", nil, nil, true),
Entry("bad version is incompatible 1", "bad", "1.1", false),
Entry("bad version is incompatible 2", "bad", nil, false),
Entry("bad version is incompatible 3", "1.1", "bad", false),
Entry("bad version is incompatible 4", nil, "bad", false),
)

DescribeTable("CompatibleFile",
func(version1 interface{}, transform1 interface{}, version2 interface{}, transform2 interface{}, expected bool) {
res := CompatibleFile(
map[string]interface{}{
VersionKey: version1,
TransformKey: transform1,
},
map[string]interface{}{
VersionKey: version2,
TransformKey: transform2,
},
)
if expected {
// compatible, then result is nil
Expect(res).To(BeNil())
} else {
// not-compatible, then result is an error
Expect(res).Should(HaveOccurred())
}
},
// version1, version2, expected
Entry("1", "1.1", true, "1.2", true, true),
Entry("2", "1.1", true, "1.2", false, false),
Entry("3", "1.1", true, "2.1", true, false),
)
})
})
Loading

0 comments on commit 4157f7b

Please sign in to comment.