Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

277 rader
6.9 KiB

  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package firestore
  15. import (
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "time"
  20. ts "github.com/golang/protobuf/ptypes/timestamp"
  21. pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
  22. "google.golang.org/genproto/googleapis/type/latlng"
  23. )
  24. type testStruct1 struct {
  25. B bool
  26. I int
  27. U uint32
  28. F float64
  29. S string
  30. Y []byte
  31. T time.Time
  32. Ts *ts.Timestamp
  33. G *latlng.LatLng
  34. L []int
  35. M map[string]int
  36. P *int
  37. }
  38. var (
  39. p = new(int)
  40. testVal1 = testStruct1{
  41. B: true,
  42. I: 1,
  43. U: 2,
  44. F: 3.0,
  45. S: "four",
  46. Y: []byte{5},
  47. T: tm,
  48. Ts: ptm,
  49. G: ll,
  50. L: []int{6},
  51. M: map[string]int{"a": 7},
  52. P: p,
  53. }
  54. mapVal1 = mapval(map[string]*pb.Value{
  55. "B": boolval(true),
  56. "I": intval(1),
  57. "U": intval(2),
  58. "F": floatval(3),
  59. "S": {ValueType: &pb.Value_StringValue{"four"}},
  60. "Y": bytesval([]byte{5}),
  61. "T": tsval(tm),
  62. "Ts": {ValueType: &pb.Value_TimestampValue{ptm}},
  63. "G": geoval(ll),
  64. "L": arrayval(intval(6)),
  65. "M": mapval(map[string]*pb.Value{"a": intval(7)}),
  66. "P": intval(8),
  67. })
  68. )
  69. func TestToProtoValue(t *testing.T) {
  70. *p = 8
  71. for _, test := range []struct {
  72. in interface{}
  73. want *pb.Value
  74. }{
  75. {nil, nullValue},
  76. {[]int(nil), nullValue},
  77. {map[string]int(nil), nullValue},
  78. {(*testStruct1)(nil), nullValue},
  79. {(*ts.Timestamp)(nil), nullValue},
  80. {(*latlng.LatLng)(nil), nullValue},
  81. {(*DocumentRef)(nil), nullValue},
  82. {true, boolval(true)},
  83. {3, intval(3)},
  84. {uint32(3), intval(3)},
  85. {1.5, floatval(1.5)},
  86. {"str", strval("str")},
  87. {[]byte{1, 2}, bytesval([]byte{1, 2})},
  88. {tm, tsval(tm)},
  89. {ptm, &pb.Value{ValueType: &pb.Value_TimestampValue{ptm}}},
  90. {ll, geoval(ll)},
  91. {[]int{1, 2}, arrayval(intval(1), intval(2))},
  92. {&[]int{1, 2}, arrayval(intval(1), intval(2))},
  93. {[]int{}, arrayval()},
  94. {map[string]int{"a": 1, "b": 2},
  95. mapval(map[string]*pb.Value{"a": intval(1), "b": intval(2)})},
  96. {map[string]int{}, mapval(map[string]*pb.Value{})},
  97. {p, intval(8)},
  98. {&p, intval(8)},
  99. {map[string]interface{}{"a": 1, "p": p, "s": "str"},
  100. mapval(map[string]*pb.Value{"a": intval(1), "p": intval(8), "s": strval("str")})},
  101. {map[string]fmt.Stringer{"a": tm},
  102. mapval(map[string]*pb.Value{"a": tsval(tm)})},
  103. {testVal1, mapVal1},
  104. {
  105. &DocumentRef{
  106. ID: "d",
  107. Path: "projects/P/databases/D/documents/c/d",
  108. Parent: &CollectionRef{
  109. ID: "c",
  110. parentPath: "projects/P/databases/D",
  111. Path: "projects/P/databases/D/documents/c",
  112. Query: Query{collectionID: "c", parentPath: "projects/P/databases/D"},
  113. },
  114. },
  115. refval("projects/P/databases/D/documents/c/d"),
  116. },
  117. // ServerTimestamps are removed, possibly leaving nil.
  118. {map[string]interface{}{"a": ServerTimestamp}, nil},
  119. {
  120. map[string]interface{}{
  121. "a": map[string]interface{}{
  122. "b": map[string]interface{}{
  123. "c": ServerTimestamp,
  124. },
  125. },
  126. },
  127. nil,
  128. },
  129. {
  130. map[string]interface{}{
  131. "a": map[string]interface{}{
  132. "b": map[string]interface{}{
  133. "c": ServerTimestamp,
  134. "d": ServerTimestamp,
  135. },
  136. },
  137. },
  138. nil,
  139. },
  140. {
  141. map[string]interface{}{
  142. "a": map[string]interface{}{
  143. "b": map[string]interface{}{
  144. "c": ServerTimestamp,
  145. "d": ServerTimestamp,
  146. "e": 1,
  147. },
  148. },
  149. },
  150. mapval(map[string]*pb.Value{
  151. "a": mapval(map[string]*pb.Value{
  152. "b": mapval(map[string]*pb.Value{"e": intval(1)}),
  153. }),
  154. }),
  155. },
  156. } {
  157. got, _, err := toProtoValue(reflect.ValueOf(test.in))
  158. if err != nil {
  159. t.Errorf("%v (%T): %v", test.in, test.in, err)
  160. continue
  161. }
  162. if !testEqual(got, test.want) {
  163. t.Errorf("%+v (%T):\ngot\n%+v\nwant\n%+v", test.in, test.in, got, test.want)
  164. }
  165. }
  166. }
  167. type stringy struct{}
  168. func (stringy) String() string { return "stringy" }
  169. func TestToProtoValueErrors(t *testing.T) {
  170. for _, in := range []interface{}{
  171. uint64(0), // a bad fit for int64
  172. map[int]bool{}, // map key type is not string
  173. make(chan int), // can't handle type
  174. map[string]fmt.Stringer{"a": stringy{}}, // only empty interfaces
  175. ServerTimestamp, // ServerTimestamp can only be a field value
  176. []interface{}{ServerTimestamp},
  177. map[string]interface{}{"a": []interface{}{ServerTimestamp}},
  178. map[string]interface{}{"a": []interface{}{
  179. map[string]interface{}{"b": ServerTimestamp},
  180. }},
  181. Delete, // Delete should never appear
  182. []interface{}{Delete},
  183. map[string]interface{}{"a": Delete},
  184. map[string]interface{}{"a": []interface{}{Delete}},
  185. } {
  186. _, _, err := toProtoValue(reflect.ValueOf(in))
  187. if err == nil {
  188. t.Errorf("%v: got nil, want error", in)
  189. }
  190. }
  191. }
  192. type testStruct2 struct {
  193. Ignore int `firestore:"-"`
  194. Rename int `firestore:"a"`
  195. OmitEmpty int `firestore:",omitempty"`
  196. OmitEmptyTime time.Time `firestore:",omitempty"`
  197. }
  198. func TestToProtoValueTags(t *testing.T) {
  199. in := &testStruct2{
  200. Ignore: 1,
  201. Rename: 2,
  202. OmitEmpty: 3,
  203. OmitEmptyTime: aTime,
  204. }
  205. got, _, err := toProtoValue(reflect.ValueOf(in))
  206. if err != nil {
  207. t.Fatal(err)
  208. }
  209. want := mapval(map[string]*pb.Value{
  210. "a": intval(2),
  211. "OmitEmpty": intval(3),
  212. "OmitEmptyTime": tsval(aTime),
  213. })
  214. if !testEqual(got, want) {
  215. t.Errorf("got %+v, want %+v", got, want)
  216. }
  217. got, _, err = toProtoValue(reflect.ValueOf(testStruct2{}))
  218. if err != nil {
  219. t.Fatal(err)
  220. }
  221. want = mapval(map[string]*pb.Value{"a": intval(0)})
  222. if !testEqual(got, want) {
  223. t.Errorf("got\n%+v\nwant\n%+v", got, want)
  224. }
  225. }
  226. func TestToProtoValueEmbedded(t *testing.T) {
  227. // Embedded time.Time, LatLng, or Timestamp should behave like non-embedded.
  228. type embed struct {
  229. time.Time
  230. *latlng.LatLng
  231. *ts.Timestamp
  232. }
  233. got, _, err := toProtoValue(reflect.ValueOf(embed{tm, ll, ptm}))
  234. if err != nil {
  235. t.Fatal(err)
  236. }
  237. want := mapval(map[string]*pb.Value{
  238. "Time": tsval(tm),
  239. "LatLng": geoval(ll),
  240. "Timestamp": {ValueType: &pb.Value_TimestampValue{ptm}},
  241. })
  242. if !testEqual(got, want) {
  243. t.Errorf("got %+v, want %+v", got, want)
  244. }
  245. }
  246. func TestIsEmpty(t *testing.T) {
  247. for _, e := range []interface{}{int(0), float32(0), false, "", []int{}, []int(nil), (*int)(nil)} {
  248. if !isEmptyValue(reflect.ValueOf(e)) {
  249. t.Errorf("%v (%T): want true, got false", e, e)
  250. }
  251. }
  252. i := 3
  253. for _, n := range []interface{}{int(1), float32(1), true, "x", []int{1}, &i} {
  254. if isEmptyValue(reflect.ValueOf(n)) {
  255. t.Errorf("%v (%T): want false, got true", n, n)
  256. }
  257. }
  258. }