Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // adapted from github.com/golang/appengine/datastore | 5 // adapted from github.com/golang/appengine/datastore |
| 6 | 6 |
| 7 package datastore | 7 package datastore |
| 8 | 8 |
| 9 import ( | 9 import ( |
| 10 "fmt" | 10 "fmt" |
| (...skipping 908 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 919 So(ds.Run(q, func(k *Key, _ CursorCB) bool { | 919 So(ds.Run(q, func(k *Key, _ CursorCB) bool { |
| 920 So(k.IntID(), ShouldEqual, i+1) | 920 So(k.IntID(), ShouldEqual, i+1) |
| 921 i++ | 921 i++ |
| 922 return true | 922 return true |
| 923 }), ShouldBeNil) | 923 }), ShouldBeNil) |
| 924 }) | 924 }) |
| 925 | 925 |
| 926 }) | 926 }) |
| 927 }) | 927 }) |
| 928 } | 928 } |
| 929 | |
| 930 type fixedDataDatastore struct { | |
| 931 RawInterface | |
| 932 | |
| 933 data map[string]PropertyMap | |
| 934 } | |
| 935 | |
| 936 func (d *fixedDataDatastore) GetMulti(keys []*Key, _ MultiMetaGetter, cb GetMult iCB) error { | |
| 937 for _, k := range keys { | |
| 938 data, ok := d.data[k.String()] | |
| 939 if ok { | |
| 940 cb(data, nil) | |
| 941 } else { | |
| 942 cb(nil, ErrNoSuchEntity) | |
| 943 } | |
| 944 } | |
| 945 return nil | |
| 946 } | |
| 947 | |
| 948 func (d *fixedDataDatastore) PutMulti(keys []*Key, vals []PropertyMap, cb PutMul tiCB) error { | |
| 949 if d.data == nil { | |
| 950 d.data = make(map[string]PropertyMap, len(keys)) | |
| 951 } | |
| 952 for i, k := range keys { | |
| 953 if k.Incomplete() { | |
| 954 panic("key is incomplete, don't do that.") | |
| 955 } | |
| 956 d.data[k.String()], _ = vals[i].Save(false) | |
| 957 cb(k, nil) | |
| 958 } | |
| 959 return nil | |
| 960 } | |
| 961 | |
| 962 func TestSchemaChange(t *testing.T) { | |
| 963 t.Parallel() | |
| 964 | |
| 965 Convey("Test changing schemas", t, func() { | |
| 966 fds := fixedDataDatastore{} | |
| 967 ds := &datastoreImpl{&fds, "", ""} | |
| 968 | |
| 969 Convey("Can add fields", func() { | |
| 970 initial := PropertyMap{ | |
| 971 "$key": {mpNI(ds.MakeKey("Val", 10))}, | |
| 972 "Val": {mp(100)}, | |
| 973 } | |
| 974 So(ds.Put(initial), ShouldBeNil) | |
| 975 | |
| 976 type Val struct { | |
| 977 ID int64 `gae:"$id"` | |
| 978 | |
| 979 Val int64 | |
| 980 TwoVal int64 // whoa, TWO vals! amazing | |
| 981 } | |
| 982 tv := &Val{ID: 10, TwoVal: 2} | |
| 983 So(ds.Get(tv), ShouldBeNil) | |
| 984 So(tv, ShouldResemble, &Val{ID: 10, Val: 100, TwoVal: 2} ) | |
| 985 }) | |
| 986 | |
| 987 Convey("Removing fields", func() { | |
| 988 initial := PropertyMap{ | |
| 989 "$key": {mpNI(ds.MakeKey("Val", 10))}, | |
| 990 "Val": {mp(100)}, | |
| 991 "TwoVal": {mp(200)}, | |
| 992 } | |
| 993 So(ds.Put(initial), ShouldBeNil) | |
| 994 | |
| 995 Convey("is normally an error", func() { | |
| 996 type Val struct { | |
| 997 ID int64 `gae:"$id"` | |
| 998 | |
| 999 Val int64 | |
| 1000 } | |
| 1001 tv := &Val{ID: 10} | |
| 1002 So(ds.Get(tv), ShouldErrLike, | |
| 1003 `gae: cannot load field "TwoVal" into a "datastore.Val`) | |
| 1004 So(tv, ShouldResemble, &Val{ID: 10, Val: 100}) | |
| 1005 }) | |
| 1006 | |
| 1007 Convey("Unless you have an ,extra field!", func() { | |
| 1008 type Val struct { | |
| 1009 ID int64 `gae:"$id"` | |
| 1010 | |
| 1011 Val int64 | |
| 1012 Extra PropertyMap `gae:",extra"` | |
| 1013 } | |
| 1014 tv := &Val{ID: 10} | |
| 1015 So(ds.Get(tv), ShouldBeNil) | |
| 1016 So(tv, ShouldResembleV, &Val{ | |
| 1017 ID: 10, | |
| 1018 Val: 100, | |
| 1019 Extra: PropertyMap{ | |
| 1020 "TwoVal": {mp(200)}, | |
| 1021 }, | |
| 1022 }) | |
| 1023 }) | |
| 1024 }) | |
| 1025 | |
| 1026 Convey("Can round-trip extra fields", func() { | |
| 1027 type Expando struct { | |
| 1028 ID int64 `gae:"$id"` | |
| 1029 | |
| 1030 Something int | |
| 1031 Extra PropertyMap `gae:",extra"` | |
| 1032 } | |
| 1033 ex := &Expando{10, 17, PropertyMap{ | |
| 1034 "Hello": {mp("Hello")}, | |
| 1035 "World": {mp(true)}, | |
| 1036 }} | |
| 1037 So(ds.Put(ex), ShouldBeNil) | |
| 1038 | |
| 1039 ex = &Expando{ID: 10} | |
| 1040 So(ds.Get(ex), ShouldBeNil) | |
| 1041 So(ex, ShouldResembleV, &Expando{ | |
| 1042 ID: 10, | |
| 1043 Something: 17, | |
| 1044 Extra: PropertyMap{ | |
| 1045 "Hello": {mp("Hello")}, | |
| 1046 "World": {mp(true)}, | |
| 1047 }, | |
| 1048 }) | |
| 1049 }) | |
| 1050 | |
| 1051 Convey("Can read-but-not-write", func() { | |
| 1052 initial := PropertyMap{ | |
| 1053 "$key": {mpNI(ds.MakeKey("Convert", 10))}, | |
| 1054 "Val": {mp(100)}, | |
| 1055 "TwoVal": {mp(200)}, | |
| 1056 } | |
| 1057 So(ds.Put(initial), ShouldBeNil) | |
| 1058 type Convert struct { | |
| 1059 ID int64 `gae:"$id"` | |
| 1060 | |
| 1061 Val int64 | |
| 1062 NewVal int64 | |
| 1063 Extra PropertyMap `gae:"-,extra"` | |
| 1064 } | |
| 1065 c := &Convert{ID: 10} | |
| 1066 So(ds.Get(c), ShouldBeNil) | |
| 1067 So(c, ShouldResembleV, &Convert{ | |
| 1068 ID: 10, Val: 100, NewVal: 0, Extra: PropertyMap{ "TwoVal": {mp(200)}}, | |
| 1069 }) | |
| 1070 c.NewVal = c.Extra["TwoVal"][0].Value().(int64) | |
| 1071 So(ds.Put(c), ShouldBeNil) | |
| 1072 | |
| 1073 c = &Convert{ID: 10} | |
| 1074 So(ds.Get(c), ShouldBeNil) | |
| 1075 So(c, ShouldResembleV, &Convert{ | |
| 1076 ID: 10, Val: 100, NewVal: 200, Extra: nil, | |
| 1077 }) | |
| 1078 }) | |
| 1079 | |
| 1080 Convey("Can black hole", func() { | |
| 1081 initial := PropertyMap{ | |
| 1082 "$key": {mpNI(ds.MakeKey("BlackHole", 10))}, | |
| 1083 "Val": {mp(100)}, | |
| 1084 "TwoVal": {mp(200)}, | |
| 1085 } | |
| 1086 So(ds.Put(initial), ShouldBeNil) | |
| 1087 type BlackHole struct { | |
| 1088 ID int64 `gae:"$id"` | |
| 1089 | |
| 1090 NewStuff string | |
| 1091 blackHole PropertyMap `gae:"-,extra"` | |
| 1092 } | |
| 1093 b := &BlackHole{ID: 10, NewStuff: "amazeballs"} | |
|
dnj
2015/12/12 20:18:34
(╯°□°)╯︵ ┻━┻
iannucci
2015/12/12 21:14:42
good point. done.
dnj (Google)
2015/12/13 02:09:23
+1
| |
| 1094 So(ds.Get(b), ShouldBeNil) | |
| 1095 So(b, ShouldResemble, &BlackHole{ID: 10, NewStuff: "amaz eballs"}) | |
| 1096 }) | |
| 1097 | |
| 1098 Convey("Can change field types", func() { | |
| 1099 initial := PropertyMap{ | |
| 1100 "$key": {mpNI(ds.MakeKey("IntChange", 10))}, | |
| 1101 "Val": {mp(100)}, | |
| 1102 } | |
| 1103 So(ds.Put(initial), ShouldBeNil) | |
| 1104 | |
| 1105 type IntChange struct { | |
| 1106 ID int64 `gae:"$id"` | |
| 1107 Val string | |
| 1108 Extra PropertyMap `gae:"-,extra"` | |
| 1109 } | |
| 1110 i := &IntChange{ID: 10} | |
| 1111 So(ds.Get(i), ShouldBeNil) | |
| 1112 So(i, ShouldResembleV, &IntChange{ID: 10, Extra: Propert yMap{"Val": {mp(100)}}}) | |
| 1113 i.Val = fmt.Sprint(i.Extra["Val"][0].Value()) | |
| 1114 So(ds.Put(i), ShouldBeNil) | |
| 1115 | |
| 1116 i = &IntChange{ID: 10} | |
| 1117 So(ds.Get(i), ShouldBeNil) | |
| 1118 So(i, ShouldResembleV, &IntChange{ID: 10, Val: "100"}) | |
| 1119 }) | |
| 1120 | |
| 1121 Convey("Native fields have priority over Extra fields", func() { | |
| 1122 type Dup struct { | |
| 1123 ID int64 `gae:"$id"` | |
| 1124 Val int64 | |
| 1125 Extra PropertyMap `gae:",extra"` | |
| 1126 } | |
| 1127 d := &Dup{ID: 10, Val: 100, Extra: PropertyMap{ | |
| 1128 "Val": {mp(200)}, | |
| 1129 "Other": {mp("other")}, | |
| 1130 }} | |
| 1131 So(ds.Put(d), ShouldBeNil) | |
| 1132 | |
| 1133 d = &Dup{ID: 10} | |
| 1134 So(ds.Get(d), ShouldBeNil) | |
| 1135 So(d, ShouldResembleV, &Dup{ | |
| 1136 ID: 10, Val: 100, Extra: PropertyMap{"Other": {m p("other")}}, | |
| 1137 }) | |
| 1138 }) | |
| 1139 | |
| 1140 Convey("Can change repeated field to non-repeating field", func( ) { | |
| 1141 initial := PropertyMap{ | |
| 1142 "$key": {mpNI(ds.MakeKey("NonRepeating", 10))}, | |
| 1143 "Val": {mp(100), mp(200), mp(400)}, | |
| 1144 } | |
| 1145 So(ds.Put(initial), ShouldBeNil) | |
| 1146 | |
| 1147 type NonRepeating struct { | |
| 1148 ID int64 `gae:"$id"` | |
| 1149 Val int64 | |
| 1150 Extra PropertyMap `gae:",extra"` | |
| 1151 } | |
| 1152 n := &NonRepeating{ID: 10} | |
| 1153 So(ds.Get(n), ShouldBeNil) | |
| 1154 So(n, ShouldResembleV, &NonRepeating{ | |
| 1155 ID: 10, Val: 0, Extra: PropertyMap{ | |
| 1156 "Val": {mp(100), mp(200), mp(400)}, | |
| 1157 }, | |
| 1158 }) | |
| 1159 }) | |
| 1160 | |
| 1161 Convey("Deals correctly with recursive types", func() { | |
| 1162 initial := PropertyMap{ | |
| 1163 "$key": {mpNI(ds.MakeKey("Outer", 10))}, | |
| 1164 "I.A": {mp(1), mp(2), mp(4)}, | |
| 1165 "I.B": {mp(10), mp(20), mp(40)}, | |
| 1166 "I.C": {mp(100), mp(200), mp(400)}, | |
| 1167 } | |
| 1168 So(ds.Put(initial), ShouldBeNil) | |
| 1169 type Inner struct { | |
| 1170 A int64 | |
| 1171 B int64 | |
| 1172 } | |
| 1173 type Outer struct { | |
| 1174 ID int64 `gae:"$id"` | |
| 1175 | |
| 1176 I []Inner | |
| 1177 Extra PropertyMap `gae:",extra"` | |
| 1178 } | |
| 1179 o := &Outer{ID: 10} | |
| 1180 So(ds.Get(o), ShouldBeNil) | |
| 1181 So(o, ShouldResembleV, &Outer{ | |
| 1182 ID: 10, | |
| 1183 I: []Inner{ | |
| 1184 {1, 10}, | |
| 1185 {2, 20}, | |
| 1186 {4, 40}, | |
| 1187 }, | |
| 1188 Extra: PropertyMap{ | |
| 1189 "I.C": {mp(100), mp(200), mp(400)}, | |
| 1190 }, | |
| 1191 }) | |
| 1192 }) | |
| 1193 | |
| 1194 Convey("Problems", func() { | |
| 1195 Convey("multiple extra fields", func() { | |
| 1196 type Bad struct { | |
| 1197 A PropertyMap `gae:",extra"` | |
| 1198 B PropertyMap `gae:",extra"` | |
| 1199 } | |
| 1200 So(func() { GetPLS(&Bad{}) }, ShouldPanicLike, | |
| 1201 "multiple fields tagged as 'extra'") | |
| 1202 }) | |
| 1203 | |
| 1204 Convey("extra field with name", func() { | |
| 1205 type Bad struct { | |
| 1206 A PropertyMap `gae:"wut,extra"` | |
| 1207 } | |
| 1208 So(func() { GetPLS(&Bad{}) }, ShouldPanicLike, | |
| 1209 "struct 'extra' field has invalid name w ut") | |
| 1210 }) | |
| 1211 | |
| 1212 Convey("extra field with bad type", func() { | |
| 1213 type Bad struct { | |
| 1214 A int64 `gae:",extra"` | |
| 1215 } | |
| 1216 So(func() { GetPLS(&Bad{}) }, ShouldPanicLike, | |
| 1217 "struct 'extra' field has invalid type i nt64") | |
| 1218 }) | |
| 1219 }) | |
| 1220 }) | |
| 1221 } | |
| OLD | NEW |