Merge lp:~niemeyer/pyjuju/go-iface-schema into lp:pyjuju/go
- go-iface-schema
- Merge into go
Proposed by
Gustavo Niemeyer
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Kapil Thangavelu | ||||
Approved revision: | 6 | ||||
Merged at revision: | 3 | ||||
Proposed branch: | lp:~niemeyer/pyjuju/go-iface-schema | ||||
Merge into: | lp:pyjuju/go | ||||
Diff against target: |
562 lines (+304/-15) 6 files modified
formula/Makefile (+23/-0) formula/export_test.go (+11/-0) formula/formula.go (+101/-0) formula/formula_test.go (+86/-0) schema/schema.go (+29/-9) schema/schema_test.go (+54/-6) |
||||
To merge this branch: | bzr merge lp:~niemeyer/pyjuju/go-iface-schema | ||||
Related bugs: |
|
||||
Related blueprints: |
The Formula Store
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Kapil Thangavelu (community) | Approve | ||
William Reade (community) | Approve | ||
Review via email: mp+73099@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote : | # |
LGTM, thanks for addressing the naming as well in the subsequent branch. as well as the help in getting the tests running.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'formula' |
2 | === added file 'formula/Makefile' |
3 | --- formula/Makefile 1970-01-01 00:00:00 +0000 |
4 | +++ formula/Makefile 2011-08-26 20:38:37 +0000 |
5 | @@ -0,0 +1,23 @@ |
6 | +include $(GOROOT)/src/Make.inc |
7 | + |
8 | +all: package |
9 | + |
10 | +TARG=launchpad.net/ensemble/go/formula |
11 | + |
12 | +GOFILES=\ |
13 | + formula.go\ |
14 | + |
15 | +GOFMT=gofmt |
16 | +BADFMT:=$(shell $(GOFMT) -l $(GOFILES) $(CGOFILES) $(wildcard *_test.go)) |
17 | + |
18 | +gofmt: $(BADFMT) |
19 | + @for F in $(BADFMT); do $(GOFMT) -w $$F && echo $$F; done |
20 | + |
21 | +ifneq ($(BADFMT),) |
22 | +ifneq ($(MAKECMDGOALS),gofmt) |
23 | +$(warning WARNING: make gofmt: $(BADFMT)) |
24 | +endif |
25 | +endif |
26 | + |
27 | +include $(GOROOT)/src/Make.pkg |
28 | + |
29 | |
30 | === added file 'formula/export_test.go' |
31 | --- formula/export_test.go 1970-01-01 00:00:00 +0000 |
32 | +++ formula/export_test.go 2011-08-26 20:38:37 +0000 |
33 | @@ -0,0 +1,11 @@ |
34 | +package formula |
35 | + |
36 | +import ( |
37 | + "launchpad.net/ensemble/go/schema" |
38 | +) |
39 | + |
40 | +// Export meaningful bits for tests only. |
41 | + |
42 | +func IfaceExpander(limit interface{}) schema.Checker { |
43 | + return ifaceExpander(limit) |
44 | +} |
45 | |
46 | === added file 'formula/formula.go' |
47 | --- formula/formula.go 1970-01-01 00:00:00 +0000 |
48 | +++ formula/formula.go 2011-08-26 20:38:37 +0000 |
49 | @@ -0,0 +1,101 @@ |
50 | +package formula |
51 | + |
52 | +import ( |
53 | + "fmt" |
54 | + "launchpad.net/ensemble/go/schema" |
55 | + "os" |
56 | + "strconv" |
57 | + "strings" |
58 | +) |
59 | + |
60 | +func errorf(format string, args ...interface{}) os.Error { |
61 | + return os.NewError(fmt.Sprintf(format, args...)) |
62 | +} |
63 | + |
64 | +// ParseId splits a formula identifier into its constituting parts. |
65 | +func ParseId(id string) (namespace string, name string, rev int, err os.Error) { |
66 | + colon := strings.Index(id, ":") |
67 | + if colon == -1 { |
68 | + err = errorf("Missing formula namespace: %q", id) |
69 | + return |
70 | + } |
71 | + dash := strings.LastIndex(id, "-") |
72 | + if dash != -1 { |
73 | + rev, err = strconv.Atoi(id[dash+1:]) |
74 | + } |
75 | + if dash == -1 || err != nil { |
76 | + err = errorf("Missing formula revision: %q", id) |
77 | + return |
78 | + } |
79 | + namespace = id[:colon] |
80 | + name = id[colon+1 : dash] |
81 | + return |
82 | +} |
83 | + |
84 | +var ifaceSchema = schema.FieldMap(schema.Fields{ |
85 | + "interface": schema.String(), |
86 | + "limit": schema.OneOf(schema.Const(nil), schema.Int()), |
87 | + "optional": schema.Bool(), |
88 | +}, nil) |
89 | + |
90 | +// Schema coercer that expands the interface shorthand notation. |
91 | +// A consistent format is easier to work with than considering the |
92 | +// potential difference everywhere. |
93 | +// |
94 | +// Supports the following variants:: |
95 | +// |
96 | +// provides: |
97 | +// server: riak |
98 | +// admin: http |
99 | +// foobar: |
100 | +// interface: blah |
101 | +// |
102 | +// provides: |
103 | +// server: |
104 | +// interface: mysql |
105 | +// limit: |
106 | +// optional: false |
107 | +// |
108 | +// In all input cases, the output is the fully specified interface |
109 | +// representation as seen in the mysql interface description above. |
110 | +func ifaceExpander(limit interface{}) schema.Checker { |
111 | + return ifaceExpC{limit} |
112 | +} |
113 | + |
114 | +type ifaceExpC struct { |
115 | + limit interface{} |
116 | +} |
117 | + |
118 | +var ( |
119 | + stringC = schema.String() |
120 | + mapC = schema.Map(schema.String(), schema.Any()) |
121 | +) |
122 | + |
123 | +func (c ifaceExpC) Coerce(v interface{}, path []string) (newv interface{}, err os.Error) { |
124 | + s, err := stringC.Coerce(v, path) |
125 | + if err == nil { |
126 | + newv = schema.M{ |
127 | + "interface": s, |
128 | + "limit": c.limit, |
129 | + "optional": false, |
130 | + } |
131 | + return |
132 | + } |
133 | + |
134 | + // Optional values are context-sensitive and/or have |
135 | + // defaults, which is different than what KeyDict can |
136 | + // readily support. So just do it here first, then |
137 | + // coerce to the real schema. |
138 | + v, err = mapC.Coerce(v, path) |
139 | + if err != nil { |
140 | + return |
141 | + } |
142 | + m := v.(schema.M) |
143 | + if _, ok := m["limit"]; !ok { |
144 | + m["limit"] = c.limit |
145 | + } |
146 | + if _, ok := m["optional"]; !ok { |
147 | + m["optional"] = false |
148 | + } |
149 | + return ifaceSchema.Coerce(m, path) |
150 | +} |
151 | |
152 | === added file 'formula/formula_test.go' |
153 | --- formula/formula_test.go 1970-01-01 00:00:00 +0000 |
154 | +++ formula/formula_test.go 2011-08-26 20:38:37 +0000 |
155 | @@ -0,0 +1,86 @@ |
156 | +package formula_test |
157 | + |
158 | +import ( |
159 | + "testing" |
160 | + . "launchpad.net/gocheck" |
161 | + "launchpad.net/ensemble/go/formula" |
162 | + "launchpad.net/ensemble/go/schema" |
163 | +) |
164 | + |
165 | +func Test(t *testing.T) { |
166 | + TestingT(t) |
167 | +} |
168 | + |
169 | +type S struct{} |
170 | + |
171 | +var _ = Suite(&S{}) |
172 | + |
173 | +func (s *S) TestParseId(c *C) { |
174 | + namespace, name, rev, err := formula.ParseId("local:mysql-21") |
175 | + c.Assert(err, IsNil) |
176 | + c.Assert(namespace, Equals, "local") |
177 | + c.Assert(name, Equals, "mysql") |
178 | + c.Assert(rev, Equals, 21) |
179 | + |
180 | + namespace, name, rev, err = formula.ParseId("local:mysql-cluster-21") |
181 | + c.Assert(err, IsNil) |
182 | + c.Assert(namespace, Equals, "local") |
183 | + c.Assert(name, Equals, "mysql-cluster") |
184 | + c.Assert(rev, Equals, 21) |
185 | + |
186 | + _, _, _, err = formula.ParseId("foo") |
187 | + c.Assert(err, Matches, `Missing formula namespace: "foo"`) |
188 | + |
189 | + _, _, _, err = formula.ParseId("local:foo-x") |
190 | + c.Assert(err, Matches, `Missing formula revision: "local:foo-x"`) |
191 | +} |
192 | + |
193 | +// Test rewriting of a given interface specification into long form. |
194 | +// |
195 | +// InterfaceExpander uses `coerce` to do one of two things: |
196 | +// |
197 | +// - Rewrite shorthand to the long form used for actual storage |
198 | +// - Fills in defaults, including a configurable `limit` |
199 | +// |
200 | +// This test ensures test coverage on each of these branches, along |
201 | +// with ensuring the conversion object properly raises SchemaError |
202 | +// exceptions on invalid data. |
203 | +func (s *S) TestIfaceExpander(c *C) { |
204 | + e := formula.IfaceExpander(nil) |
205 | + |
206 | + path := []string{"<pa", "th>"} |
207 | + |
208 | + // Shorthand is properly rewritten |
209 | + v, err := e.Coerce("http", path) |
210 | + c.Assert(err, IsNil) |
211 | + c.Assert(v, Equals, schema.M{"interface": "http", "limit": nil, "optional": false}) |
212 | + |
213 | + // Defaults are properly applied |
214 | + v, err = e.Coerce(schema.M{"interface": "http"}, path) |
215 | + c.Assert(err, IsNil) |
216 | + c.Assert(v, Equals, schema.M{"interface": "http", "limit": nil, "optional": false}) |
217 | + |
218 | + v, err = e.Coerce(schema.M{"interface": "http", "limit": 2}, path) |
219 | + c.Assert(err, IsNil) |
220 | + c.Assert(v, Equals, schema.M{"interface": "http", "limit": int64(2), "optional": false}) |
221 | + |
222 | + v, err = e.Coerce(schema.M{"interface": "http", "optional": true}, path) |
223 | + c.Assert(err, IsNil) |
224 | + c.Assert(v, Equals, schema.M{"interface": "http", "limit": nil, "optional": true}) |
225 | + |
226 | + // Invalid data raises an error. |
227 | + v, err = e.Coerce(42, path) |
228 | + c.Assert(err, Matches, "<path>: expected map, got 42") |
229 | + |
230 | + v, err = e.Coerce(schema.M{"interface": "http", "optional": nil}, path) |
231 | + c.Assert(err, Matches, "<path>.optional: expected bool, got nothing") |
232 | + |
233 | + v, err = e.Coerce(schema.M{"interface": "http", "limit": "none, really"}, path) |
234 | + c.Assert(err, Matches, "<path>.limit: unsupported value") |
235 | + |
236 | + // Can change default limit |
237 | + e = formula.IfaceExpander(1) |
238 | + v, err = e.Coerce(schema.M{"interface": "http"}, path) |
239 | + c.Assert(err, IsNil) |
240 | + c.Assert(v, Equals, schema.M{"interface": "http", "limit": int64(1), "optional": false}) |
241 | +} |
242 | |
243 | === modified file 'schema/schema.go' |
244 | --- schema/schema.go 2011-08-24 23:55:56 +0000 |
245 | +++ schema/schema.go 2011-08-26 20:38:37 +0000 |
246 | @@ -9,6 +9,12 @@ |
247 | "strings" |
248 | ) |
249 | |
250 | +// All map types used in the schema package are of type M. |
251 | +type M map[interface{}]interface{} |
252 | + |
253 | +// All the slice types generated in the schema package are of type L. |
254 | +type L []interface{} |
255 | + |
256 | // The Coerce method of the Checker interface is called recursively when |
257 | // v is being validated. If err is nil, newv is used as the new value |
258 | // at the recursion point. If err is non-nil, v is taken as invalid and |
259 | @@ -101,14 +107,14 @@ |
260 | type boolC struct{} |
261 | |
262 | func (c boolC) Coerce(v interface{}, path []string) (interface{}, os.Error) { |
263 | - if reflect.TypeOf(v).Kind() == reflect.Bool { |
264 | + if v != nil && reflect.TypeOf(v).Kind() == reflect.Bool { |
265 | return v, nil |
266 | } |
267 | return nil, error{"bool", v, path} |
268 | } |
269 | |
270 | // Int returns a Checker that accepts any integer value, and returns |
271 | -// the same value typed as an int64. |
272 | +// the same value consistently typed as an int64. |
273 | func Int() Checker { |
274 | return intC{} |
275 | } |
276 | @@ -116,6 +122,9 @@ |
277 | type intC struct{} |
278 | |
279 | func (c intC) Coerce(v interface{}, path []string) (interface{}, os.Error) { |
280 | + if v == nil { |
281 | + return nil, error{"int", v, path} |
282 | + } |
283 | switch reflect.TypeOf(v).Kind() { |
284 | case reflect.Int: |
285 | case reflect.Int8: |
286 | @@ -129,7 +138,7 @@ |
287 | } |
288 | |
289 | // Int returns a Checker that accepts any float value, and returns |
290 | -// the same value typed as a float64. |
291 | +// the same value consistently typed as a float64. |
292 | func Float() Checker { |
293 | return floatC{} |
294 | } |
295 | @@ -137,6 +146,9 @@ |
296 | type floatC struct{} |
297 | |
298 | func (c floatC) Coerce(v interface{}, path []string) (interface{}, os.Error) { |
299 | + if v == nil { |
300 | + return nil, error{"float", v, path} |
301 | + } |
302 | switch reflect.TypeOf(v).Kind() { |
303 | case reflect.Float32: |
304 | case reflect.Float64: |
305 | @@ -156,7 +168,7 @@ |
306 | type stringC struct{} |
307 | |
308 | func (c stringC) Coerce(v interface{}, path []string) (interface{}, os.Error) { |
309 | - if reflect.TypeOf(v).Kind() == reflect.String { |
310 | + if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
311 | return reflect.ValueOf(v).String(), nil |
312 | } |
313 | return nil, error{"string", v, path} |
314 | @@ -172,7 +184,7 @@ |
315 | // XXX The regexp package happens to be extremely simple right now. |
316 | // Once exp/regexp goes mainstream, we'll have to update this |
317 | // logic to use a more widely accepted regexp subset. |
318 | - if reflect.TypeOf(v).Kind() == reflect.String { |
319 | + if v != nil && reflect.TypeOf(v).Kind() == reflect.String { |
320 | s := reflect.ValueOf(v).String() |
321 | _, err := regexp.Compile(s) |
322 | if err != nil { |
323 | @@ -183,10 +195,12 @@ |
324 | return nil, error{"regexp string", v, path} |
325 | } |
326 | |
327 | -// String returns a Checker that accepts a slice value with values |
328 | +// List returns a Checker that accepts a slice value with values |
329 | // that are processed with the elem checker. If any element of the |
330 | // provided slice value fails to be processed, processing will stop |
331 | // and return with the obtained error. |
332 | +// |
333 | +// The coerced output value has type schema.L. |
334 | func List(elem Checker) Checker { |
335 | return listC{elem} |
336 | } |
337 | @@ -204,7 +218,7 @@ |
338 | path = append(path, "[", "?", "]") |
339 | |
340 | l := rv.Len() |
341 | - out := make([]interface{}, 0, l) |
342 | + out := make(L, 0, l) |
343 | for i := 0; i != l; i++ { |
344 | path[len(path)-2] = strconv.Itoa(i) |
345 | elem, err := c.elem.Coerce(rv.Index(i).Interface(), path) |
346 | @@ -220,6 +234,8 @@ |
347 | // in the map are processed with the respective checker, and if any |
348 | // value fails to be coerced, processing stops and returns with the |
349 | // underlying error. |
350 | +// |
351 | +// The coerced output value has type schema.M. |
352 | func Map(key Checker, value Checker) Checker { |
353 | return mapC{key, value} |
354 | } |
355 | @@ -238,7 +254,7 @@ |
356 | vpath := append(path, ".", "?") |
357 | |
358 | l := rv.Len() |
359 | - out := make(map[interface{}]interface{}, l) |
360 | + out := make(M, l) |
361 | keys := rv.MapKeys() |
362 | for i := 0; i != l; i++ { |
363 | k := keys[i] |
364 | @@ -264,6 +280,8 @@ |
365 | // and processing will only succeed if all the values succeed |
366 | // individually. If a field fails to be processed, processing stops |
367 | // and returns with the underlying error. |
368 | +// |
369 | +// The coerced output value has type schema.M. |
370 | func FieldMap(fields Fields, optional Optional) Checker { |
371 | return fieldMapC{fields, optional} |
372 | } |
373 | @@ -291,7 +309,7 @@ |
374 | vpath := append(path, ".", "?") |
375 | |
376 | l := rv.Len() |
377 | - out := make(map[string]interface{}, l) |
378 | + out := make(M, l) |
379 | for k, checker := range c.fields { |
380 | vpath[len(vpath)-1] = k |
381 | var value interface{} |
382 | @@ -315,6 +333,8 @@ |
383 | // used is the first one whose checker associated with the selector |
384 | // field processes the map correctly. If no checker processes |
385 | // the selector value correctly, an error is returned. |
386 | +// |
387 | +// The coerced output value has type schema.M. |
388 | func FieldMapSet(selector string, maps []Checker) Checker { |
389 | fmaps := make([]fieldMapC, len(maps)) |
390 | for i, m := range maps { |
391 | |
392 | === modified file 'schema/schema_test.go' |
393 | --- schema/schema_test.go 2011-08-24 23:55:56 +0000 |
394 | +++ schema/schema_test.go 2011-08-26 20:38:37 +0000 |
395 | @@ -33,6 +33,10 @@ |
396 | out, err = sch.Coerce(42, aPath) |
397 | c.Assert(out, IsNil) |
398 | c.Assert(err, Matches, `<path>: expected "foo", got 42`) |
399 | + |
400 | + out, err = sch.Coerce(nil, aPath) |
401 | + c.Assert(out, IsNil) |
402 | + c.Assert(err, Matches, `<path>: expected "foo", got nothing`) |
403 | } |
404 | |
405 | func (s *S) TestAny(c *C) { |
406 | @@ -41,6 +45,10 @@ |
407 | out, err := sch.Coerce("foo", aPath) |
408 | c.Assert(err, IsNil) |
409 | c.Assert(out, Equals, "foo") |
410 | + |
411 | + out, err = sch.Coerce(nil, aPath) |
412 | + c.Assert(err, IsNil) |
413 | + c.Assert(out, Equals, nil) |
414 | } |
415 | |
416 | func (s *S) TestOneOf(c *C) { |
417 | @@ -73,6 +81,10 @@ |
418 | out, err = sch.Coerce(1, aPath) |
419 | c.Assert(out, IsNil) |
420 | c.Assert(err, Matches, "<path>: expected bool, got 1") |
421 | + |
422 | + out, err = sch.Coerce(nil, aPath) |
423 | + c.Assert(out, IsNil) |
424 | + c.Assert(err, Matches, "<path>: expected bool, got nothing") |
425 | } |
426 | |
427 | func (s *S) TestInt(c *C) { |
428 | @@ -89,6 +101,10 @@ |
429 | out, err = sch.Coerce(true, aPath) |
430 | c.Assert(out, IsNil) |
431 | c.Assert(err, Matches, "<path>: expected int, got true") |
432 | + |
433 | + out, err = sch.Coerce(nil, aPath) |
434 | + c.Assert(out, IsNil) |
435 | + c.Assert(err, Matches, "<path>: expected int, got nothing") |
436 | } |
437 | |
438 | func (s *S) TestFloat(c *C) { |
439 | @@ -105,6 +121,10 @@ |
440 | out, err = sch.Coerce(true, aPath) |
441 | c.Assert(out, IsNil) |
442 | c.Assert(err, Matches, "<path>: expected float, got true") |
443 | + |
444 | + out, err = sch.Coerce(nil, aPath) |
445 | + c.Assert(out, IsNil) |
446 | + c.Assert(err, Matches, "<path>: expected float, got nothing") |
447 | } |
448 | |
449 | func (s *S) TestString(c *C) { |
450 | @@ -117,6 +137,10 @@ |
451 | out, err = sch.Coerce(true, aPath) |
452 | c.Assert(out, IsNil) |
453 | c.Assert(err, Matches, "<path>: expected string, got true") |
454 | + |
455 | + out, err = sch.Coerce(nil, aPath) |
456 | + c.Assert(out, IsNil) |
457 | + c.Assert(err, Matches, "<path>: expected string, got nothing") |
458 | } |
459 | |
460 | func (s *S) TestSimpleRegexp(c *C) { |
461 | @@ -132,18 +156,26 @@ |
462 | out, err = sch.Coerce("[", aPath) |
463 | c.Assert(out, IsNil) |
464 | c.Assert(err, Matches, `<path>: expected valid regexp, got "\["`) |
465 | + |
466 | + out, err = sch.Coerce(nil, aPath) |
467 | + c.Assert(out, IsNil) |
468 | + c.Assert(err, Matches, `<path>: expected regexp string, got nothing`) |
469 | } |
470 | |
471 | func (s *S) TestList(c *C) { |
472 | sch := schema.List(schema.Int()) |
473 | out, err := sch.Coerce([]int8{1, 2}, aPath) |
474 | c.Assert(err, IsNil) |
475 | - c.Assert(out, Equals, []interface{}{int64(1), int64(2)}) |
476 | + c.Assert(out, Equals, schema.L{int64(1), int64(2)}) |
477 | |
478 | out, err = sch.Coerce(42, aPath) |
479 | c.Assert(out, IsNil) |
480 | c.Assert(err, Matches, "<path>: expected list, got 42") |
481 | |
482 | + out, err = sch.Coerce(nil, aPath) |
483 | + c.Assert(out, IsNil) |
484 | + c.Assert(err, Matches, "<path>: expected list, got nothing") |
485 | + |
486 | out, err = sch.Coerce([]interface{}{1, true}, aPath) |
487 | c.Assert(out, IsNil) |
488 | c.Assert(err, Matches, `<path>\[1\]: expected int, got true`) |
489 | @@ -153,12 +185,16 @@ |
490 | sch := schema.Map(schema.String(), schema.Int()) |
491 | out, err := sch.Coerce(map[string]interface{}{"a": 1, "b": int8(2)}, aPath) |
492 | c.Assert(err, IsNil) |
493 | - c.Assert(out, Equals, map[interface{}]interface{}{"a": int64(1), "b": int64(2)}) |
494 | + c.Assert(out, Equals, schema.M{"a": int64(1), "b": int64(2)}) |
495 | |
496 | out, err = sch.Coerce(42, aPath) |
497 | c.Assert(out, IsNil) |
498 | c.Assert(err, Matches, "<path>: expected map, got 42") |
499 | |
500 | + out, err = sch.Coerce(nil, aPath) |
501 | + c.Assert(out, IsNil) |
502 | + c.Assert(err, Matches, "<path>: expected map, got nothing") |
503 | + |
504 | out, err = sch.Coerce(map[int]int{1: 1}, aPath) |
505 | c.Assert(out, IsNil) |
506 | c.Assert(err, Matches, "<path>: expected string, got 1") |
507 | @@ -182,12 +218,16 @@ |
508 | |
509 | out, err := sch.Coerce(map[string]interface{}{"a": "A", "b": "B"}, aPath) |
510 | c.Assert(err, IsNil) |
511 | - c.Assert(out, Equals, map[string]interface{}{"a": "A", "b": "B"}) |
512 | + c.Assert(out, Equals, schema.M{"a": "A", "b": "B"}) |
513 | |
514 | out, err = sch.Coerce(42, aPath) |
515 | c.Assert(out, IsNil) |
516 | c.Assert(err, Matches, "<path>: expected map, got 42") |
517 | |
518 | + out, err = sch.Coerce(nil, aPath) |
519 | + c.Assert(out, IsNil) |
520 | + c.Assert(err, Matches, "<path>: expected map, got nothing") |
521 | + |
522 | out, err = sch.Coerce(map[string]interface{}{"a": "A", "b": "C"}, aPath) |
523 | c.Assert(out, IsNil) |
524 | c.Assert(err, Matches, `<path>\.b: expected "B", got "C"`) |
525 | @@ -199,7 +239,7 @@ |
526 | // b is optional |
527 | out, err = sch.Coerce(map[string]interface{}{"a": "A"}, aPath) |
528 | c.Assert(err, IsNil) |
529 | - c.Assert(out, Equals, map[string]interface{}{"a": "A"}) |
530 | + c.Assert(out, Equals, schema.M{"a": "A"}) |
531 | |
532 | // First path entry shouldn't have dots in an error message. |
533 | out, err = sch.Coerce(map[string]bool{"a": true}, nil) |
534 | @@ -220,11 +260,11 @@ |
535 | |
536 | out, err := sch.Coerce(map[string]int{"type": 1, "a": 2}, aPath) |
537 | c.Assert(err, IsNil) |
538 | - c.Assert(out, Equals, map[string]interface{}{"type": 1, "a": 2}) |
539 | + c.Assert(out, Equals, schema.M{"type": 1, "a": 2}) |
540 | |
541 | out, err = sch.Coerce(map[string]int{"type": 3, "b": 4}, aPath) |
542 | c.Assert(err, IsNil) |
543 | - c.Assert(out, Equals, map[string]interface{}{"type": 3, "b": 4}) |
544 | + c.Assert(out, Equals, schema.M{"type": 3, "b": 4}) |
545 | |
546 | out, err = sch.Coerce(map[string]int{}, aPath) |
547 | c.Assert(out, IsNil) |
548 | @@ -238,6 +278,14 @@ |
549 | c.Assert(out, IsNil) |
550 | c.Assert(err, Matches, `<path>\.b: expected 4, got 5`) |
551 | |
552 | + out, err = sch.Coerce(42, aPath) |
553 | + c.Assert(out, IsNil) |
554 | + c.Assert(err, Matches, `<path>: expected map, got 42`) |
555 | + |
556 | + out, err = sch.Coerce(nil, aPath) |
557 | + c.Assert(out, IsNil) |
558 | + c.Assert(err, Matches, `<path>: expected map, got nothing`) |
559 | + |
560 | // First path entry shouldn't have dots in an error message. |
561 | out, err = sch.Coerce(map[string]int{"a": 1}, nil) |
562 | c.Assert(out, IsNil) |
Also LGTM