Browse code

Add some tests for bundlefile and improve the error messages for LoadFile

Signed-off-by: Daniel Nephin <dnephin@docker.com>

Daniel Nephin authored on 2016/06/17 06:55:50
Showing 6 changed files
... ...
@@ -4,8 +4,8 @@ package bundlefile
4 4
 
5 5
 import (
6 6
 	"encoding/json"
7
+	"fmt"
7 8
 	"io"
8
-	"os"
9 9
 )
10 10
 
11 11
 // Bundlefile stores the contents of a bundlefile
... ...
@@ -34,19 +34,28 @@ type Port struct {
34 34
 }
35 35
 
36 36
 // LoadFile loads a bundlefile from a path to the file
37
-func LoadFile(path string) (*Bundlefile, error) {
38
-	reader, err := os.Open(path)
39
-	if err != nil {
40
-		return nil, err
41
-	}
42
-
37
+func LoadFile(reader io.Reader) (*Bundlefile, error) {
43 38
 	bundlefile := &Bundlefile{}
44 39
 
45
-	if err := json.NewDecoder(reader).Decode(bundlefile); err != nil {
40
+	decoder := json.NewDecoder(reader)
41
+	if err := decoder.Decode(bundlefile); err != nil {
42
+		switch jsonErr := err.(type) {
43
+		case *json.SyntaxError:
44
+			return nil, fmt.Errorf(
45
+				"JSON syntax error at byte %v: %s",
46
+				jsonErr.Offset,
47
+				jsonErr.Error())
48
+		case *json.UnmarshalTypeError:
49
+			return nil, fmt.Errorf(
50
+				"Unexpected type at byte %v. Expected %s but received %s.",
51
+				jsonErr.Offset,
52
+				jsonErr.Type,
53
+				jsonErr.Value)
54
+		}
46 55
 		return nil, err
47 56
 	}
48 57
 
49
-	return bundlefile, err
58
+	return bundlefile, nil
50 59
 }
51 60
 
52 61
 // Print writes the contents of the bundlefile to the output writer
53 62
new file mode 100644
... ...
@@ -0,0 +1,79 @@
0
+// +build experimental
1
+
2
+package bundlefile
3
+
4
+import (
5
+	"bytes"
6
+	"strings"
7
+	"testing"
8
+
9
+	"github.com/docker/docker/pkg/testutil/assert"
10
+)
11
+
12
+func TestLoadFileV01Success(t *testing.T) {
13
+	reader := strings.NewReader(`{
14
+		"Version": "0.1",
15
+		"Services": {
16
+			"redis": {
17
+				"Image": "redis@sha256:4b24131101fa0117bcaa18ac37055fffd9176aa1a240392bb8ea85e0be50f2ce",
18
+				"Networks": ["default"]
19
+			},
20
+			"web": {
21
+				"Image": "dockercloud/hello-world@sha256:fe79a2cfbd17eefc344fb8419420808df95a1e22d93b7f621a7399fd1e9dca1d",
22
+				"Networks": ["default"],
23
+				"User": "web"
24
+			}
25
+		}
26
+	}`)
27
+
28
+	bundle, err := LoadFile(reader)
29
+	assert.NilError(t, err)
30
+	assert.Equal(t, bundle.Version, "0.1")
31
+	assert.Equal(t, len(bundle.Services), 2)
32
+}
33
+
34
+func TestLoadFileSyntaxError(t *testing.T) {
35
+	reader := strings.NewReader(`{
36
+		"Version": "0.1",
37
+		"Services": unquoted string
38
+	}`)
39
+
40
+	_, err := LoadFile(reader)
41
+	assert.Error(t, err, "syntax error at byte 37: invalid character 'u'")
42
+}
43
+
44
+func TestLoadFileTypeError(t *testing.T) {
45
+	reader := strings.NewReader(`{
46
+		"Version": "0.1",
47
+		"Services": {
48
+			"web": {
49
+				"Image": "redis",
50
+				"Networks": "none"
51
+			}
52
+		}
53
+	}`)
54
+
55
+	_, err := LoadFile(reader)
56
+	assert.Error(t, err, "Unexpected type at byte 94. Expected []string but received string")
57
+}
58
+
59
+func TestPrint(t *testing.T) {
60
+	var buffer bytes.Buffer
61
+	bundle := &Bundlefile{
62
+		Version: "0.1",
63
+		Services: map[string]Service{
64
+			"web": {
65
+				Image:   "image",
66
+				Command: []string{"echo", "something"},
67
+			},
68
+		},
69
+	}
70
+	assert.NilError(t, Print(&buffer, bundle))
71
+	output := buffer.String()
72
+	assert.Contains(t, output, "\"Image\": \"image\"")
73
+	assert.Contains(t, output,
74
+		`"Command": [
75
+                "echo",
76
+                "something"
77
+            ]`)
78
+}
... ...
@@ -1,82 +1,60 @@
1 1
 package service
2 2
 
3 3
 import (
4
-	"strings"
5 4
 	"testing"
6 5
 	"time"
7 6
 
7
+	"github.com/docker/docker/pkg/testutil/assert"
8 8
 	"github.com/docker/engine-api/types/swarm"
9 9
 )
10 10
 
11
-func assertEqual(t *testing.T, actual, expected interface{}) {
12
-	if expected != actual {
13
-		t.Fatalf("Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual)
14
-	}
15
-}
16
-
17
-func assertNilError(t *testing.T, err error) {
18
-	if err != nil {
19
-		t.Fatalf("Expected no error, got: %s", err.Error())
20
-	}
21
-}
22
-
23
-func assertError(t *testing.T, err error, contains string) {
24
-	if err == nil {
25
-		t.Fatalf("Expected an error, but error was nil")
26
-	}
27
-
28
-	if !strings.Contains(err.Error(), contains) {
29
-		t.Fatalf("Expected error to contain '%s', got '%s'", contains, err.Error())
30
-	}
31
-}
32
-
33 11
 func TestMemBytesString(t *testing.T) {
34 12
 	var mem memBytes = 1048576
35
-	assertEqual(t, mem.String(), "1 MiB")
13
+	assert.Equal(t, mem.String(), "1 MiB")
36 14
 }
37 15
 
38 16
 func TestMemBytesSetAndValue(t *testing.T) {
39 17
 	var mem memBytes
40
-	assertNilError(t, mem.Set("5kb"))
41
-	assertEqual(t, mem.Value(), int64(5120))
18
+	assert.NilError(t, mem.Set("5kb"))
19
+	assert.Equal(t, mem.Value(), int64(5120))
42 20
 }
43 21
 
44 22
 func TestNanoCPUsString(t *testing.T) {
45 23
 	var cpus nanoCPUs = 6100000000
46
-	assertEqual(t, cpus.String(), "6.100")
24
+	assert.Equal(t, cpus.String(), "6.100")
47 25
 }
48 26
 
49 27
 func TestNanoCPUsSetAndValue(t *testing.T) {
50 28
 	var cpus nanoCPUs
51
-	assertNilError(t, cpus.Set("0.35"))
52
-	assertEqual(t, cpus.Value(), int64(350000000))
29
+	assert.NilError(t, cpus.Set("0.35"))
30
+	assert.Equal(t, cpus.Value(), int64(350000000))
53 31
 }
54 32
 
55 33
 func TestDurationOptString(t *testing.T) {
56 34
 	dur := time.Duration(300 * 10e8)
57 35
 	duration := DurationOpt{value: &dur}
58
-	assertEqual(t, duration.String(), "5m0s")
36
+	assert.Equal(t, duration.String(), "5m0s")
59 37
 }
60 38
 
61 39
 func TestDurationOptSetAndValue(t *testing.T) {
62 40
 	var duration DurationOpt
63
-	assertNilError(t, duration.Set("300s"))
64
-	assertEqual(t, *duration.Value(), time.Duration(300*10e8))
41
+	assert.NilError(t, duration.Set("300s"))
42
+	assert.Equal(t, *duration.Value(), time.Duration(300*10e8))
65 43
 }
66 44
 
67 45
 func TestUint64OptString(t *testing.T) {
68 46
 	value := uint64(2345678)
69 47
 	opt := Uint64Opt{value: &value}
70
-	assertEqual(t, opt.String(), "2345678")
48
+	assert.Equal(t, opt.String(), "2345678")
71 49
 
72 50
 	opt = Uint64Opt{}
73
-	assertEqual(t, opt.String(), "none")
51
+	assert.Equal(t, opt.String(), "none")
74 52
 }
75 53
 
76 54
 func TestUint64OptSetAndValue(t *testing.T) {
77 55
 	var opt Uint64Opt
78
-	assertNilError(t, opt.Set("14445"))
79
-	assertEqual(t, *opt.Value(), uint64(14445))
56
+	assert.NilError(t, opt.Set("14445"))
57
+	assert.Equal(t, *opt.Value(), uint64(14445))
80 58
 }
81 59
 
82 60
 func TestMountOptString(t *testing.T) {
... ...
@@ -95,16 +73,16 @@ func TestMountOptString(t *testing.T) {
95 95
 		},
96 96
 	}
97 97
 	expected := "BIND /home/path /target, VOLUME foo /target/foo"
98
-	assertEqual(t, mount.String(), expected)
98
+	assert.Equal(t, mount.String(), expected)
99 99
 }
100 100
 
101 101
 func TestMountOptSetNoError(t *testing.T) {
102 102
 	var mount MountOpt
103
-	assertNilError(t, mount.Set("type=bind,target=/target,source=/foo"))
103
+	assert.NilError(t, mount.Set("type=bind,target=/target,source=/foo"))
104 104
 
105 105
 	mounts := mount.Value()
106
-	assertEqual(t, len(mounts), 1)
107
-	assertEqual(t, mounts[0], swarm.Mount{
106
+	assert.Equal(t, len(mounts), 1)
107
+	assert.Equal(t, mounts[0], swarm.Mount{
108 108
 		Type:   swarm.MountType("BIND"),
109 109
 		Source: "/foo",
110 110
 		Target: "/target",
... ...
@@ -113,25 +91,25 @@ func TestMountOptSetNoError(t *testing.T) {
113 113
 
114 114
 func TestMountOptSetErrorNoType(t *testing.T) {
115 115
 	var mount MountOpt
116
-	assertError(t, mount.Set("target=/target,source=/foo"), "type is required")
116
+	assert.Error(t, mount.Set("target=/target,source=/foo"), "type is required")
117 117
 }
118 118
 
119 119
 func TestMountOptSetErrorNoTarget(t *testing.T) {
120 120
 	var mount MountOpt
121
-	assertError(t, mount.Set("type=VOLUME,source=/foo"), "target is required")
121
+	assert.Error(t, mount.Set("type=VOLUME,source=/foo"), "target is required")
122 122
 }
123 123
 
124 124
 func TestMountOptSetErrorInvalidKey(t *testing.T) {
125 125
 	var mount MountOpt
126
-	assertError(t, mount.Set("type=VOLUME,bogus=foo"), "unexpected key 'bogus'")
126
+	assert.Error(t, mount.Set("type=VOLUME,bogus=foo"), "unexpected key 'bogus'")
127 127
 }
128 128
 
129 129
 func TestMountOptSetErrorInvalidField(t *testing.T) {
130 130
 	var mount MountOpt
131
-	assertError(t, mount.Set("type=VOLUME,bogus"), "invalid field 'bogus'")
131
+	assert.Error(t, mount.Set("type=VOLUME,bogus"), "invalid field 'bogus'")
132 132
 }
133 133
 
134 134
 func TestMountOptSetErrorInvalidWritable(t *testing.T) {
135 135
 	var mount MountOpt
136
-	assertError(t, mount.Set("type=VOLUME,writable=yes"), "invalid value for writable: yes")
136
+	assert.Error(t, mount.Set("type=VOLUME,writable=yes"), "invalid value for writable: yes")
137 137
 }
... ...
@@ -31,7 +31,12 @@ func loadBundlefile(stderr io.Writer, namespace string, path string) (*bundlefil
31 31
 	}
32 32
 
33 33
 	fmt.Fprintf(stderr, "Loading bundle from %s\n", path)
34
-	bundle, err := bundlefile.LoadFile(path)
34
+	reader, err := os.Open(path)
35
+	if err != nil {
36
+		return nil, err
37
+	}
38
+
39
+	bundle, err := bundlefile.LoadFile(reader)
35 40
 	if err != nil {
36 41
 		return nil, fmt.Errorf("Error reading %s: %v\n", path, err)
37 42
 	}
38 43
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+// Package assert contains functions for making assertions in unit tests
1
+package assert
2
+
3
+import (
4
+	"strings"
5
+)
6
+
7
+// TestingT is an interface which defines the methods of testing.T that are
8
+// required by this package
9
+type TestingT interface {
10
+	Fatalf(string, ...interface{})
11
+}
12
+
13
+// Equal compare the actual value to the expected value and fails the test if
14
+// they are not equal.
15
+func Equal(t TestingT, actual, expected interface{}) {
16
+	if expected != actual {
17
+		t.Fatalf("Expected '%v' (%T) got '%v' (%T)", expected, expected, actual, actual)
18
+	}
19
+}
20
+
21
+// NilError asserts that the error is nil, otherwise it fails the test.
22
+func NilError(t TestingT, err error) {
23
+	if err != nil {
24
+		t.Fatalf("Expected no error, got: %s", err.Error())
25
+	}
26
+}
27
+
28
+// Error asserts that error is not nil, and contains the expected text,
29
+// otherwise it fails the test.
30
+func Error(t TestingT, err error, contains string) {
31
+	if err == nil {
32
+		t.Fatalf("Expected an error, but error was nil")
33
+	}
34
+
35
+	if !strings.Contains(err.Error(), contains) {
36
+		t.Fatalf("Expected error to contain '%s', got '%s'", contains, err.Error())
37
+	}
38
+}
39
+
40
+// Contains asserts that the string contains a substring, otherwise it fails the
41
+// test.
42
+func Contains(t TestingT, actual, contains string) {
43
+	if !strings.Contains(actual, contains) {
44
+		t.Fatalf("Expected '%s' to contain '%s'", actual, contains)
45
+	}
46
+}
0 47
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+package testutil