| package schema |
| |
| import ( |
| "reflect" |
| "testing" |
| ) |
| |
| func TestMapFieldWriter_impl(t *testing.T) { |
| var _ FieldWriter = new(MapFieldWriter) |
| } |
| |
| func TestMapFieldWriter(t *testing.T) { |
| schema := map[string]*Schema{ |
| "bool": &Schema{Type: TypeBool}, |
| "int": &Schema{Type: TypeInt}, |
| "string": &Schema{Type: TypeString}, |
| "list": &Schema{ |
| Type: TypeList, |
| Elem: &Schema{Type: TypeString}, |
| }, |
| "listInt": &Schema{ |
| Type: TypeList, |
| Elem: &Schema{Type: TypeInt}, |
| }, |
| "listResource": &Schema{ |
| Type: TypeList, |
| Optional: true, |
| Computed: true, |
| Elem: &Resource{ |
| Schema: map[string]*Schema{ |
| "value": &Schema{ |
| Type: TypeInt, |
| Optional: true, |
| }, |
| }, |
| }, |
| }, |
| "map": &Schema{Type: TypeMap}, |
| "set": &Schema{ |
| Type: TypeSet, |
| Elem: &Schema{Type: TypeInt}, |
| Set: func(a interface{}) int { |
| return a.(int) |
| }, |
| }, |
| "setDeep": &Schema{ |
| Type: TypeSet, |
| Elem: &Resource{ |
| Schema: map[string]*Schema{ |
| "index": &Schema{Type: TypeInt}, |
| "value": &Schema{Type: TypeString}, |
| }, |
| }, |
| Set: func(a interface{}) int { |
| return a.(map[string]interface{})["index"].(int) |
| }, |
| }, |
| } |
| |
| cases := map[string]struct { |
| Addr []string |
| Value interface{} |
| Err bool |
| Out map[string]string |
| }{ |
| "noexist": { |
| []string{"noexist"}, |
| 42, |
| true, |
| map[string]string{}, |
| }, |
| |
| "bool": { |
| []string{"bool"}, |
| false, |
| false, |
| map[string]string{ |
| "bool": "false", |
| }, |
| }, |
| |
| "int": { |
| []string{"int"}, |
| 42, |
| false, |
| map[string]string{ |
| "int": "42", |
| }, |
| }, |
| |
| "string": { |
| []string{"string"}, |
| "42", |
| false, |
| map[string]string{ |
| "string": "42", |
| }, |
| }, |
| |
| "string nil": { |
| []string{"string"}, |
| nil, |
| false, |
| map[string]string{ |
| "string": "", |
| }, |
| }, |
| |
| "list of resources": { |
| []string{"listResource"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "value": 80, |
| }, |
| }, |
| false, |
| map[string]string{ |
| "listResource.#": "1", |
| "listResource.0.value": "80", |
| }, |
| }, |
| |
| "list of resources empty": { |
| []string{"listResource"}, |
| []interface{}{}, |
| false, |
| map[string]string{ |
| "listResource.#": "0", |
| }, |
| }, |
| |
| "list of resources nil": { |
| []string{"listResource"}, |
| nil, |
| false, |
| map[string]string{ |
| "listResource.#": "0", |
| }, |
| }, |
| |
| "list of strings": { |
| []string{"list"}, |
| []interface{}{"foo", "bar"}, |
| false, |
| map[string]string{ |
| "list.#": "2", |
| "list.0": "foo", |
| "list.1": "bar", |
| }, |
| }, |
| |
| "list element": { |
| []string{"list", "0"}, |
| "string", |
| true, |
| map[string]string{}, |
| }, |
| |
| "map": { |
| []string{"map"}, |
| map[string]interface{}{"foo": "bar"}, |
| false, |
| map[string]string{ |
| "map.%": "1", |
| "map.foo": "bar", |
| }, |
| }, |
| |
| "map delete": { |
| []string{"map"}, |
| nil, |
| false, |
| map[string]string{ |
| "map": "", |
| }, |
| }, |
| |
| "map element": { |
| []string{"map", "foo"}, |
| "bar", |
| true, |
| map[string]string{}, |
| }, |
| |
| "set": { |
| []string{"set"}, |
| []interface{}{1, 2, 5}, |
| false, |
| map[string]string{ |
| "set.#": "3", |
| "set.1": "1", |
| "set.2": "2", |
| "set.5": "5", |
| }, |
| }, |
| |
| "set nil": { |
| []string{"set"}, |
| nil, |
| false, |
| map[string]string{ |
| "set.#": "0", |
| }, |
| }, |
| |
| "set typed nil": { |
| []string{"set"}, |
| func() *Set { return nil }(), |
| false, |
| map[string]string{ |
| "set.#": "0", |
| }, |
| }, |
| |
| "set resource": { |
| []string{"setDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "index": 10, |
| "value": "foo", |
| }, |
| map[string]interface{}{ |
| "index": 50, |
| "value": "bar", |
| }, |
| }, |
| false, |
| map[string]string{ |
| "setDeep.#": "2", |
| "setDeep.10.index": "10", |
| "setDeep.10.value": "foo", |
| "setDeep.50.index": "50", |
| "setDeep.50.value": "bar", |
| }, |
| }, |
| |
| "set element": { |
| []string{"set", "5"}, |
| 5, |
| true, |
| map[string]string{}, |
| }, |
| |
| "full object": { |
| nil, |
| map[string]interface{}{ |
| "string": "foo", |
| "list": []interface{}{"foo", "bar"}, |
| }, |
| false, |
| map[string]string{ |
| "string": "foo", |
| "list.#": "2", |
| "list.0": "foo", |
| "list.1": "bar", |
| }, |
| }, |
| } |
| |
| for name, tc := range cases { |
| w := &MapFieldWriter{Schema: schema} |
| err := w.WriteField(tc.Addr, tc.Value) |
| if err != nil != tc.Err { |
| t.Fatalf("%s: err: %s", name, err) |
| } |
| |
| actual := w.Map() |
| if !reflect.DeepEqual(actual, tc.Out) { |
| t.Fatalf("%s: bad: %#v", name, actual) |
| } |
| } |
| } |
| |
| func TestMapFieldWriterCleanSet(t *testing.T) { |
| schema := map[string]*Schema{ |
| "setDeep": &Schema{ |
| Type: TypeSet, |
| Elem: &Resource{ |
| Schema: map[string]*Schema{ |
| "index": &Schema{Type: TypeInt}, |
| "value": &Schema{Type: TypeString}, |
| }, |
| }, |
| Set: func(a interface{}) int { |
| return a.(map[string]interface{})["index"].(int) |
| }, |
| }, |
| } |
| |
| values := []struct { |
| Addr []string |
| Value interface{} |
| Out map[string]string |
| }{ |
| { |
| []string{"setDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "index": 10, |
| "value": "foo", |
| }, |
| map[string]interface{}{ |
| "index": 50, |
| "value": "bar", |
| }, |
| }, |
| map[string]string{ |
| "setDeep.#": "2", |
| "setDeep.10.index": "10", |
| "setDeep.10.value": "foo", |
| "setDeep.50.index": "50", |
| "setDeep.50.value": "bar", |
| }, |
| }, |
| { |
| []string{"setDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "index": 20, |
| "value": "baz", |
| }, |
| map[string]interface{}{ |
| "index": 60, |
| "value": "qux", |
| }, |
| }, |
| map[string]string{ |
| "setDeep.#": "2", |
| "setDeep.20.index": "20", |
| "setDeep.20.value": "baz", |
| "setDeep.60.index": "60", |
| "setDeep.60.value": "qux", |
| }, |
| }, |
| { |
| []string{"setDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "index": 30, |
| "value": "one", |
| }, |
| map[string]interface{}{ |
| "index": 70, |
| "value": "two", |
| }, |
| }, |
| map[string]string{ |
| "setDeep.#": "2", |
| "setDeep.30.index": "30", |
| "setDeep.30.value": "one", |
| "setDeep.70.index": "70", |
| "setDeep.70.value": "two", |
| }, |
| }, |
| } |
| |
| w := &MapFieldWriter{Schema: schema} |
| |
| for n, tc := range values { |
| err := w.WriteField(tc.Addr, tc.Value) |
| if err != nil { |
| t.Fatalf("%d: err: %s", n, err) |
| } |
| |
| actual := w.Map() |
| if !reflect.DeepEqual(actual, tc.Out) { |
| t.Fatalf("%d: bad: %#v", n, actual) |
| } |
| } |
| } |
| |
| func TestMapFieldWriterCleanList(t *testing.T) { |
| schema := map[string]*Schema{ |
| "listDeep": &Schema{ |
| Type: TypeList, |
| Elem: &Resource{ |
| Schema: map[string]*Schema{ |
| "thing1": &Schema{Type: TypeString}, |
| "thing2": &Schema{Type: TypeString}, |
| }, |
| }, |
| }, |
| } |
| |
| values := []struct { |
| Addr []string |
| Value interface{} |
| Out map[string]string |
| }{ |
| { |
| // Base list |
| []string{"listDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "thing1": "a", |
| "thing2": "b", |
| }, |
| map[string]interface{}{ |
| "thing1": "c", |
| "thing2": "d", |
| }, |
| map[string]interface{}{ |
| "thing1": "e", |
| "thing2": "f", |
| }, |
| map[string]interface{}{ |
| "thing1": "g", |
| "thing2": "h", |
| }, |
| }, |
| map[string]string{ |
| "listDeep.#": "4", |
| "listDeep.0.thing1": "a", |
| "listDeep.0.thing2": "b", |
| "listDeep.1.thing1": "c", |
| "listDeep.1.thing2": "d", |
| "listDeep.2.thing1": "e", |
| "listDeep.2.thing2": "f", |
| "listDeep.3.thing1": "g", |
| "listDeep.3.thing2": "h", |
| }, |
| }, |
| { |
| // Remove an element |
| []string{"listDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "thing1": "a", |
| "thing2": "b", |
| }, |
| map[string]interface{}{ |
| "thing1": "c", |
| "thing2": "d", |
| }, |
| map[string]interface{}{ |
| "thing1": "e", |
| "thing2": "f", |
| }, |
| }, |
| map[string]string{ |
| "listDeep.#": "3", |
| "listDeep.0.thing1": "a", |
| "listDeep.0.thing2": "b", |
| "listDeep.1.thing1": "c", |
| "listDeep.1.thing2": "d", |
| "listDeep.2.thing1": "e", |
| "listDeep.2.thing2": "f", |
| }, |
| }, |
| { |
| // Rewrite with missing keys. This should normally not be necessary, as |
| // hopefully the writers are writing zero values as necessary, but for |
| // brevity we want to make sure that what exists in the writer is exactly |
| // what the last write looked like coming from the provider. |
| []string{"listDeep"}, |
| []interface{}{ |
| map[string]interface{}{ |
| "thing1": "a", |
| }, |
| map[string]interface{}{ |
| "thing1": "c", |
| }, |
| map[string]interface{}{ |
| "thing1": "e", |
| }, |
| }, |
| map[string]string{ |
| "listDeep.#": "3", |
| "listDeep.0.thing1": "a", |
| "listDeep.1.thing1": "c", |
| "listDeep.2.thing1": "e", |
| }, |
| }, |
| } |
| |
| w := &MapFieldWriter{Schema: schema} |
| |
| for n, tc := range values { |
| err := w.WriteField(tc.Addr, tc.Value) |
| if err != nil { |
| t.Fatalf("%d: err: %s", n, err) |
| } |
| |
| actual := w.Map() |
| if !reflect.DeepEqual(actual, tc.Out) { |
| t.Fatalf("%d: bad: %#v", n, actual) |
| } |
| } |
| } |
| |
| func TestMapFieldWriterCleanMap(t *testing.T) { |
| schema := map[string]*Schema{ |
| "map": &Schema{ |
| Type: TypeMap, |
| }, |
| } |
| |
| values := []struct { |
| Value interface{} |
| Out map[string]string |
| }{ |
| { |
| // Base map |
| map[string]interface{}{ |
| "thing1": "a", |
| "thing2": "b", |
| "thing3": "c", |
| "thing4": "d", |
| }, |
| map[string]string{ |
| "map.%": "4", |
| "map.thing1": "a", |
| "map.thing2": "b", |
| "map.thing3": "c", |
| "map.thing4": "d", |
| }, |
| }, |
| { |
| // Base map |
| map[string]interface{}{ |
| "thing1": "a", |
| "thing2": "b", |
| "thing4": "d", |
| }, |
| map[string]string{ |
| "map.%": "3", |
| "map.thing1": "a", |
| "map.thing2": "b", |
| "map.thing4": "d", |
| }, |
| }, |
| } |
| |
| w := &MapFieldWriter{Schema: schema} |
| |
| for n, tc := range values { |
| err := w.WriteField([]string{"map"}, tc.Value) |
| if err != nil { |
| t.Fatalf("%d: err: %s", n, err) |
| } |
| |
| actual := w.Map() |
| if !reflect.DeepEqual(actual, tc.Out) { |
| t.Fatalf("%d: bad: %#v", n, actual) |
| } |
| } |
| } |