You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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