Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

551 rinda
15 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. "encoding/json"
  17. "fmt"
  18. "io"
  19. "math"
  20. "reflect"
  21. "strings"
  22. "testing"
  23. "time"
  24. ts "github.com/golang/protobuf/ptypes/timestamp"
  25. pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
  26. "google.golang.org/genproto/googleapis/type/latlng"
  27. )
  28. var (
  29. tm = time.Date(2016, 12, 25, 0, 0, 0, 123456789, time.UTC)
  30. ll = &latlng.LatLng{Latitude: 20, Longitude: 30}
  31. ptm = &ts.Timestamp{Seconds: 12345, Nanos: 67890}
  32. )
  33. func TestCreateFromProtoValue(t *testing.T) {
  34. for _, test := range []struct {
  35. in *pb.Value
  36. want interface{}
  37. }{
  38. {in: nullValue, want: nil},
  39. {in: boolval(true), want: true},
  40. {in: intval(3), want: int64(3)},
  41. {in: floatval(1.5), want: 1.5},
  42. {in: strval("str"), want: "str"},
  43. {in: tsval(tm), want: tm},
  44. {
  45. in: bytesval([]byte{1, 2}),
  46. want: []byte{1, 2},
  47. },
  48. {
  49. in: &pb.Value{ValueType: &pb.Value_GeoPointValue{ll}},
  50. want: ll,
  51. },
  52. {
  53. in: arrayval(intval(1), intval(2)),
  54. want: []interface{}{int64(1), int64(2)},
  55. },
  56. {
  57. in: arrayval(),
  58. want: []interface{}{},
  59. },
  60. {
  61. in: mapval(map[string]*pb.Value{"a": intval(1), "b": intval(2)}),
  62. want: map[string]interface{}{"a": int64(1), "b": int64(2)},
  63. },
  64. {
  65. in: mapval(map[string]*pb.Value{}),
  66. want: map[string]interface{}{},
  67. },
  68. {
  69. in: refval("projects/P/databases/D/documents/c/d"),
  70. want: &DocumentRef{
  71. ID: "d",
  72. Parent: &CollectionRef{
  73. ID: "c",
  74. parentPath: "projects/P/databases/D",
  75. Path: "projects/P/databases/D/documents/c",
  76. Query: Query{collectionID: "c", parentPath: "projects/P/databases/D"},
  77. },
  78. Path: "projects/P/databases/D/documents/c/d",
  79. },
  80. },
  81. } {
  82. got, err := createFromProtoValue(test.in, nil)
  83. if err != nil {
  84. t.Errorf("%v: %v", test.in, err)
  85. continue
  86. }
  87. if !testEqual(got, test.want) {
  88. t.Errorf("%+v:\ngot\n%#v\nwant\n%#v", test.in, got, test.want)
  89. }
  90. }
  91. }
  92. func TestSetFromProtoValue(t *testing.T) {
  93. testSetFromProtoValue(t, "json", jsonTester{})
  94. testSetFromProtoValue(t, "firestore", protoTester{})
  95. }
  96. func testSetFromProtoValue(t *testing.T, prefix string, r tester) {
  97. pi := newfloat(7)
  98. s := []float64{7, 8}
  99. ar1 := [1]float64{7}
  100. ar2 := [2]float64{7, 8}
  101. ar3 := [3]float64{7, 8, 9}
  102. mf := map[string]float64{"a": 7}
  103. type T struct {
  104. I **float64
  105. J float64
  106. }
  107. one := newfloat(1)
  108. six := newfloat(6)
  109. st := []*T{{I: &six}, nil, {I: &six, J: 7}}
  110. vs := interface{}(T{J: 1})
  111. vm := interface{}(map[string]float64{"i": 1})
  112. var (
  113. i int
  114. i8 int8
  115. i16 int16
  116. i32 int32
  117. i64 int64
  118. u8 uint8
  119. u16 uint16
  120. u32 uint32
  121. b bool
  122. ll *latlng.LatLng
  123. mi map[string]interface{}
  124. ms map[string]T
  125. )
  126. for i, test := range []struct {
  127. in interface{}
  128. val interface{}
  129. want interface{}
  130. }{
  131. {&pi, r.Null(), (*float64)(nil)},
  132. {pi, r.Float(1), 1.0},
  133. {&s, r.Null(), ([]float64)(nil)},
  134. {&s, r.Array(r.Float(1), r.Float(2)), []float64{1, 2}},
  135. {&ar1, r.Array(r.Float(1), r.Float(2)), [1]float64{1}},
  136. {&ar2, r.Array(r.Float(1), r.Float(2)), [2]float64{1, 2}},
  137. {&ar3, r.Array(r.Float(1), r.Float(2)), [3]float64{1, 2, 0}},
  138. {&mf, r.Null(), (map[string]float64)(nil)},
  139. {&mf, r.Map("a", r.Float(1), "b", r.Float(2)), map[string]float64{"a": 1, "b": 2}},
  140. {&st, r.Array(
  141. r.Null(), // overwrites st[0] with nil
  142. r.Map("i", r.Float(1)), // sets st[1] to a new struct
  143. r.Map("i", r.Float(2)), // modifies st[2]
  144. ),
  145. []*T{nil, {I: &one}, {I: &six, J: 7}}},
  146. {&mi, r.Map("a", r.Float(1), "b", r.Float(2)), map[string]interface{}{"a": 1.0, "b": 2.0}},
  147. {&ms, r.Map("a", r.Map("j", r.Float(1))), map[string]T{"a": {J: 1}}},
  148. {&vs, r.Map("i", r.Float(2)), map[string]interface{}{"i": 2.0}},
  149. {&vm, r.Map("i", r.Float(2)), map[string]interface{}{"i": 2.0}},
  150. {&ll, r.Null(), (*latlng.LatLng)(nil)},
  151. {&i, r.Int(1), int(1)},
  152. {&i8, r.Int(1), int8(1)},
  153. {&i16, r.Int(1), int16(1)},
  154. {&i32, r.Int(1), int32(1)},
  155. {&i64, r.Int(1), int64(1)},
  156. {&u8, r.Int(1), uint8(1)},
  157. {&u16, r.Int(1), uint16(1)},
  158. {&u32, r.Int(1), uint32(1)},
  159. {&b, r.Bool(true), true},
  160. {&i, r.Float(1), int(1)}, // can put a float with no fractional part into an int
  161. {pi, r.Int(1), float64(1)}, // can put an int into a float
  162. } {
  163. if err := r.Set(test.in, test.val); err != nil {
  164. t.Errorf("%s: #%d: got error %v", prefix, i, err)
  165. continue
  166. }
  167. got := reflect.ValueOf(test.in).Elem().Interface()
  168. if !testEqual(got, test.want) {
  169. t.Errorf("%s: #%d, %v:\ngot\n%+v (%T)\nwant\n%+v (%T)",
  170. prefix, i, test.val, got, got, test.want, test.want)
  171. }
  172. }
  173. }
  174. func TestSetFromProtoValueNoJSON(t *testing.T) {
  175. // Test code paths that we cannot compare to JSON.
  176. var (
  177. bs []byte
  178. tmi time.Time
  179. lli *latlng.LatLng
  180. tmp *ts.Timestamp
  181. )
  182. bytes := []byte{1, 2, 3}
  183. for i, test := range []struct {
  184. in interface{}
  185. val *pb.Value
  186. want interface{}
  187. }{
  188. {&bs, bytesval(bytes), bytes},
  189. {&tmi, tsval(tm), tm},
  190. {&tmp, &pb.Value{ValueType: &pb.Value_TimestampValue{ptm}}, ptm},
  191. {&lli, geoval(ll), ll},
  192. } {
  193. if err := setFromProtoValue(test.in, test.val, &Client{}); err != nil {
  194. t.Errorf("#%d: got error %v", i, err)
  195. continue
  196. }
  197. got := reflect.ValueOf(test.in).Elem().Interface()
  198. if !testEqual(got, test.want) {
  199. t.Errorf("#%d, %v:\ngot\n%+v (%T)\nwant\n%+v (%T)",
  200. i, test.val, got, got, test.want, test.want)
  201. }
  202. }
  203. }
  204. func TestSetFromProtoValueErrors(t *testing.T) {
  205. c := &Client{}
  206. ival := intval(3)
  207. for i, test := range []struct {
  208. in interface{}
  209. val *pb.Value
  210. }{
  211. {3, ival}, // not a pointer
  212. {new(int8), intval(128)}, // int overflow
  213. {new(uint8), intval(256)}, // uint overflow
  214. {new(float32), floatval(2 * math.MaxFloat32)}, // float overflow
  215. {new(uint), ival}, // cannot set type
  216. {new(uint64), ival}, // cannot set type
  217. {new(io.Reader), ival}, // cannot set type
  218. {new(map[int]int),
  219. mapval(map[string]*pb.Value{"x": ival})}, // map key type is not string
  220. // the rest are all type mismatches
  221. {new(bool), ival},
  222. {new(*latlng.LatLng), ival},
  223. {new(time.Time), ival},
  224. {new(string), ival},
  225. {new([]byte), ival},
  226. {new([]int), ival},
  227. {new([1]int), ival},
  228. {new(map[string]int), ival},
  229. {new(*bool), ival},
  230. {new(struct{}), ival},
  231. {new(int), floatval(2.5)}, // float must be integral
  232. {new(uint16), intval(-1)}, // uint cannot be negative
  233. {new(int16), floatval(math.MaxFloat32)}, // doesn't fit
  234. {new(uint16), floatval(math.MaxFloat32)}, // doesn't fit
  235. {new(float32),
  236. &pb.Value{ValueType: &pb.Value_IntegerValue{math.MaxInt64}}}, // overflow
  237. } {
  238. err := setFromProtoValue(test.in, test.val, c)
  239. if err == nil {
  240. t.Errorf("#%d: %v, %v: got nil, want error", i, test.in, test.val)
  241. }
  242. }
  243. }
  244. func TestSetFromProtoValuePointers(t *testing.T) {
  245. // Verify that pointers are set, instead of being replaced.
  246. // Confirm that the behavior matches encoding/json.
  247. testSetPointer(t, "json", jsonTester{})
  248. testSetPointer(t, "firestore", protoTester{&Client{}})
  249. }
  250. func testSetPointer(t *testing.T, prefix string, r tester) {
  251. // If an interface{} holds a pointer, the pointer is set.
  252. set := func(x, val interface{}) {
  253. if err := r.Set(x, val); err != nil {
  254. t.Fatalf("%s: set(%v, %v): %v", prefix, x, val, err)
  255. }
  256. }
  257. p := new(float64)
  258. var st struct {
  259. I interface{}
  260. }
  261. // A pointer in a slice of interface{} is set.
  262. s := []interface{}{p}
  263. set(&s, r.Array(r.Float(1)))
  264. if s[0] != p {
  265. t.Errorf("%s: pointers not identical", prefix)
  266. }
  267. if *p != 1 {
  268. t.Errorf("%s: got %f, want 1", prefix, *p)
  269. }
  270. // Setting a null will set the pointer to nil.
  271. set(&s, r.Array(r.Null()))
  272. if got := s[0]; got != nil {
  273. t.Errorf("%s: got %v, want null", prefix, got)
  274. }
  275. // It doesn't matter how deep the pointers nest.
  276. p = new(float64)
  277. p2 := &p
  278. p3 := &p2
  279. s = []interface{}{p3}
  280. set(&s, r.Array(r.Float(1)))
  281. if s[0] != p3 {
  282. t.Errorf("%s: pointers not identical", prefix)
  283. }
  284. if *p != 1 {
  285. t.Errorf("%s: got %f, want 1", prefix, *p)
  286. }
  287. // A pointer in an interface{} field is set.
  288. p = new(float64)
  289. st.I = p
  290. set(&st, r.Map("i", r.Float(1)))
  291. if st.I != p {
  292. t.Errorf("%s: pointers not identical", prefix)
  293. }
  294. if *p != 1 {
  295. t.Errorf("%s: got %f, want 1", prefix, *p)
  296. }
  297. // Setting a null will set the pointer to nil.
  298. set(&st, r.Map("i", r.Null()))
  299. if got := st.I; got != nil {
  300. t.Errorf("%s: got %v, want null", prefix, got)
  301. }
  302. // A pointer to a slice (instead of to float64) is set.
  303. psi := &[]float64{7, 8, 9}
  304. st.I = psi
  305. set(&st, r.Map("i", r.Array(r.Float(1))))
  306. if st.I != psi {
  307. t.Errorf("%s: pointers not identical", prefix)
  308. }
  309. // The slice itself should be truncated and filled, not replaced.
  310. if got, want := cap(*psi), 3; got != want {
  311. t.Errorf("cap: got %d, want %d", got, want)
  312. }
  313. if want := &[]float64{1}; !testEqual(st.I, want) {
  314. t.Errorf("got %+v, want %+v", st.I, want)
  315. }
  316. // A pointer to a map is set.
  317. pmf := &map[string]float64{"a": 7, "b": 8}
  318. st.I = pmf
  319. set(&st, r.Map("i", r.Map("a", r.Float(1))))
  320. if st.I != pmf {
  321. t.Errorf("%s: pointers not identical", prefix)
  322. }
  323. if want := map[string]float64{"a": 1, "b": 8}; !testEqual(*pmf, want) {
  324. t.Errorf("%s: got %+v, want %+v", prefix, *pmf, want)
  325. }
  326. // Maps are different: since the map values aren't addressable, they
  327. // are always discarded, even if the map element type is not interface{}.
  328. // A map's values are discarded if the value type is a pointer type.
  329. p = new(float64)
  330. m := map[string]*float64{"i": p}
  331. set(&m, r.Map("i", r.Float(1)))
  332. if m["i"] == p {
  333. t.Errorf("%s: pointers are identical", prefix)
  334. }
  335. if got, want := *m["i"], 1.0; got != want {
  336. t.Errorf("%s: got %v, want %v", prefix, got, want)
  337. }
  338. // A map's values are discarded if the value type is interface{}.
  339. p = new(float64)
  340. m2 := map[string]interface{}{"i": p}
  341. set(&m2, r.Map("i", r.Float(1)))
  342. if m2["i"] == p {
  343. t.Errorf("%s: pointers are identical", prefix)
  344. }
  345. if got, want := m2["i"].(float64), 1.0; got != want {
  346. t.Errorf("%s: got %f, want %f", prefix, got, want)
  347. }
  348. }
  349. // An interface for setting and building values, to facilitate comparing firestore deserialization
  350. // with encoding/json.
  351. type tester interface {
  352. Set(x, val interface{}) error
  353. Null() interface{}
  354. Int(int) interface{}
  355. Float(float64) interface{}
  356. Bool(bool) interface{}
  357. Array(...interface{}) interface{}
  358. Map(keysvals ...interface{}) interface{}
  359. }
  360. type protoTester struct {
  361. c *Client
  362. }
  363. func (p protoTester) Set(x, val interface{}) error { return setFromProtoValue(x, val.(*pb.Value), p.c) }
  364. func (protoTester) Null() interface{} { return nullValue }
  365. func (protoTester) Int(i int) interface{} { return intval(i) }
  366. func (protoTester) Float(f float64) interface{} { return floatval(f) }
  367. func (protoTester) Bool(b bool) interface{} { return boolval(b) }
  368. func (protoTester) Array(els ...interface{}) interface{} {
  369. var s []*pb.Value
  370. for _, el := range els {
  371. s = append(s, el.(*pb.Value))
  372. }
  373. return arrayval(s...)
  374. }
  375. func (protoTester) Map(keysvals ...interface{}) interface{} {
  376. m := map[string]*pb.Value{}
  377. for i := 0; i < len(keysvals); i += 2 {
  378. m[keysvals[i].(string)] = keysvals[i+1].(*pb.Value)
  379. }
  380. return mapval(m)
  381. }
  382. type jsonTester struct{}
  383. func (jsonTester) Set(x, val interface{}) error { return json.Unmarshal([]byte(val.(string)), x) }
  384. func (jsonTester) Null() interface{} { return "null" }
  385. func (jsonTester) Int(i int) interface{} { return fmt.Sprint(i) }
  386. func (jsonTester) Float(f float64) interface{} { return fmt.Sprint(f) }
  387. func (jsonTester) Bool(b bool) interface{} {
  388. if b {
  389. return "true"
  390. }
  391. return "false"
  392. }
  393. func (jsonTester) Array(els ...interface{}) interface{} {
  394. var s []string
  395. for _, el := range els {
  396. s = append(s, el.(string))
  397. }
  398. return "[" + strings.Join(s, ", ") + "]"
  399. }
  400. func (jsonTester) Map(keysvals ...interface{}) interface{} {
  401. var s []string
  402. for i := 0; i < len(keysvals); i += 2 {
  403. s = append(s, fmt.Sprintf("%q: %v", keysvals[i], keysvals[i+1]))
  404. }
  405. return "{" + strings.Join(s, ", ") + "}"
  406. }
  407. func newfloat(f float64) *float64 {
  408. p := new(float64)
  409. *p = f
  410. return p
  411. }
  412. func TestParseDocumentPath(t *testing.T) {
  413. for _, test := range []struct {
  414. in string
  415. pid, dbid string
  416. dpath []string
  417. }{
  418. {"projects/foo-bar/databases/db2/documents/c1/d1",
  419. "foo-bar", "db2", []string{"c1", "d1"}},
  420. {"projects/P/databases/D/documents/c1/d1/c2/d2",
  421. "P", "D", []string{"c1", "d1", "c2", "d2"}},
  422. } {
  423. gotPid, gotDbid, gotDpath, err := parseDocumentPath(test.in)
  424. if err != nil {
  425. t.Fatal(err)
  426. }
  427. if got, want := gotPid, test.pid; got != want {
  428. t.Errorf("project ID: got %q, want %q", got, want)
  429. }
  430. if got, want := gotDbid, test.dbid; got != want {
  431. t.Errorf("db ID: got %q, want %q", got, want)
  432. }
  433. if got, want := gotDpath, test.dpath; !testEqual(got, want) {
  434. t.Errorf("doc path: got %q, want %q", got, want)
  435. }
  436. }
  437. }
  438. func TestParseDocumentPathErrors(t *testing.T) {
  439. for _, badPath := range []string{
  440. "projects/P/databases/D/documents/c", // collection path
  441. "/projects/P/databases/D/documents/c/d", // initial slash
  442. "projects/P/databases/D/c/d", // missing "documents"
  443. "project/P/database/D/document/c/d",
  444. } {
  445. // Every prefix of a bad path is also bad.
  446. for i := 0; i <= len(badPath); i++ {
  447. in := badPath[:i]
  448. _, _, _, err := parseDocumentPath(in)
  449. if err == nil {
  450. t.Errorf("%q: got nil, want error", in)
  451. }
  452. }
  453. }
  454. }
  455. func TestPathToDoc(t *testing.T) {
  456. c := &Client{}
  457. path := "projects/P/databases/D/documents/c1/d1/c2/d2"
  458. got, err := pathToDoc(path, c)
  459. if err != nil {
  460. t.Fatal(err)
  461. }
  462. want := &DocumentRef{
  463. ID: "d2",
  464. Path: "projects/P/databases/D/documents/c1/d1/c2/d2",
  465. Parent: &CollectionRef{
  466. ID: "c2",
  467. parentPath: "projects/P/databases/D/documents/c1/d1",
  468. Path: "projects/P/databases/D/documents/c1/d1/c2",
  469. c: c,
  470. Query: Query{c: c, collectionID: "c2", parentPath: "projects/P/databases/D/documents/c1/d1"},
  471. Parent: &DocumentRef{
  472. ID: "d1",
  473. Path: "projects/P/databases/D/documents/c1/d1",
  474. Parent: &CollectionRef{
  475. ID: "c1",
  476. c: c,
  477. parentPath: "projects/P/databases/D",
  478. Path: "projects/P/databases/D/documents/c1",
  479. Parent: nil,
  480. Query: Query{c: c, collectionID: "c1", parentPath: "projects/P/databases/D"},
  481. },
  482. },
  483. },
  484. }
  485. if !testEqual(got, want) {
  486. t.Errorf("\ngot %+v\nwant %+v", got, want)
  487. }
  488. }
  489. func TestTypeString(t *testing.T) {
  490. for _, test := range []struct {
  491. in *pb.Value
  492. want string
  493. }{
  494. {nullValue, "null"},
  495. {intval(1), "int"},
  496. {floatval(1), "float"},
  497. {boolval(true), "bool"},
  498. {strval(""), "string"},
  499. {tsval(tm), "timestamp"},
  500. {geoval(ll), "GeoPoint"},
  501. {bytesval(nil), "bytes"},
  502. {refval(""), "reference"},
  503. {arrayval(nil), "array"},
  504. {mapval(nil), "map"},
  505. } {
  506. got := typeString(test.in)
  507. if got != test.want {
  508. t.Errorf("%+v: got %q, want %q", test.in, got, test.want)
  509. }
  510. }
  511. }