integration_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  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 datastore
  15. import (
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "sort"
  20. "strings"
  21. "testing"
  22. "time"
  23. "golang.org/x/net/context"
  24. "google.golang.org/cloud"
  25. "google.golang.org/cloud/internal/testutil"
  26. )
  27. // TODO(djd): Make test entity clean up more robust: some test entities may
  28. // be left behind if tests are aborted, the transport fails, etc.
  29. func newClient(ctx context.Context, t *testing.T) *Client {
  30. ts := testutil.TokenSource(ctx, ScopeDatastore, ScopeUserEmail)
  31. if ts == nil {
  32. t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
  33. }
  34. client, err := NewClient(ctx, testutil.ProjID(), cloud.WithTokenSource(ts))
  35. if err != nil {
  36. t.Fatalf("NewClient: %v", err)
  37. }
  38. return client
  39. }
  40. func TestBasics(t *testing.T) {
  41. if testing.Short() {
  42. t.Skip("Integration tests skipped in short mode")
  43. }
  44. ctx := context.Background()
  45. client := newClient(ctx, t)
  46. type X struct {
  47. I int
  48. S string
  49. T time.Time
  50. }
  51. x0 := X{66, "99", time.Now().Truncate(time.Millisecond)}
  52. k, err := client.Put(ctx, NewIncompleteKey(ctx, "BasicsX", nil), &x0)
  53. if err != nil {
  54. t.Fatalf("client.Put: %v", err)
  55. }
  56. x1 := X{}
  57. err = client.Get(ctx, k, &x1)
  58. if err != nil {
  59. t.Errorf("client.Get: %v", err)
  60. }
  61. err = client.Delete(ctx, k)
  62. if err != nil {
  63. t.Errorf("client.Delete: %v", err)
  64. }
  65. if !reflect.DeepEqual(x0, x1) {
  66. t.Errorf("compare: x0=%v, x1=%v", x0, x1)
  67. }
  68. }
  69. func TestListValues(t *testing.T) {
  70. if testing.Short() {
  71. t.Skip("Integration tests skipped in short mode")
  72. }
  73. ctx := context.Background()
  74. client := newClient(ctx, t)
  75. p0 := PropertyList{
  76. {Name: "L", Value: int64(12), Multiple: true},
  77. {Name: "L", Value: "string", Multiple: true},
  78. {Name: "L", Value: true, Multiple: true},
  79. }
  80. k, err := client.Put(ctx, NewIncompleteKey(ctx, "ListValue", nil), &p0)
  81. if err != nil {
  82. t.Fatalf("client.Put: %v", err)
  83. }
  84. var p1 PropertyList
  85. if err := client.Get(ctx, k, &p1); err != nil {
  86. t.Errorf("client.Get: %v", err)
  87. }
  88. if !reflect.DeepEqual(p0, p1) {
  89. t.Errorf("compare:\np0=%v\np1=%#v", p0, p1)
  90. }
  91. if err = client.Delete(ctx, k); err != nil {
  92. t.Errorf("client.Delete: %v", err)
  93. }
  94. }
  95. func TestGetMulti(t *testing.T) {
  96. if testing.Short() {
  97. t.Skip("Integration tests skipped in short mode")
  98. }
  99. ctx := context.Background()
  100. client := newClient(ctx, t)
  101. type X struct {
  102. I int
  103. }
  104. p := NewKey(ctx, "X", "", time.Now().Unix(), nil)
  105. cases := []struct {
  106. key *Key
  107. put bool
  108. }{
  109. {key: NewKey(ctx, "X", "item1", 0, p), put: true},
  110. {key: NewKey(ctx, "X", "item2", 0, p), put: false},
  111. {key: NewKey(ctx, "X", "item3", 0, p), put: false},
  112. {key: NewKey(ctx, "X", "item4", 0, p), put: true},
  113. }
  114. var src, dst []*X
  115. var srcKeys, dstKeys []*Key
  116. for _, c := range cases {
  117. dst = append(dst, &X{})
  118. dstKeys = append(dstKeys, c.key)
  119. if c.put {
  120. src = append(src, &X{})
  121. srcKeys = append(srcKeys, c.key)
  122. }
  123. }
  124. if _, err := client.PutMulti(ctx, srcKeys, src); err != nil {
  125. t.Error(err)
  126. }
  127. err := client.GetMulti(ctx, dstKeys, dst)
  128. if err == nil {
  129. t.Errorf("client.GetMulti got %v, expected error", err)
  130. }
  131. e, ok := err.(MultiError)
  132. if !ok {
  133. t.Errorf("client.GetMulti got %t, expected MultiError", err)
  134. }
  135. for i, err := range e {
  136. got, want := err, (error)(nil)
  137. if !cases[i].put {
  138. got, want = err, ErrNoSuchEntity
  139. }
  140. if got != want {
  141. t.Errorf("MultiError[%d] == %v, want %v", i, got, want)
  142. }
  143. }
  144. }
  145. type Z struct {
  146. S string
  147. T string `datastore:",noindex"`
  148. P []byte
  149. K []byte `datastore:",noindex"`
  150. }
  151. func (z Z) String() string {
  152. var lens []string
  153. v := reflect.ValueOf(z)
  154. for i := 0; i < v.NumField(); i++ {
  155. if l := v.Field(i).Len(); l > 0 {
  156. lens = append(lens, fmt.Sprintf("len(%s)=%d", v.Type().Field(i).Name, l))
  157. }
  158. }
  159. return fmt.Sprintf("Z{ %s }", strings.Join(lens, ","))
  160. }
  161. func TestUnindexableValues(t *testing.T) {
  162. if testing.Short() {
  163. t.Skip("Integration tests skipped in short mode")
  164. }
  165. ctx := context.Background()
  166. client := newClient(ctx, t)
  167. x1500 := strings.Repeat("x", 1500)
  168. x1501 := strings.Repeat("x", 1501)
  169. testCases := []struct {
  170. in Z
  171. wantErr bool
  172. }{
  173. {in: Z{S: x1500}, wantErr: false},
  174. {in: Z{S: x1501}, wantErr: true},
  175. {in: Z{T: x1500}, wantErr: false},
  176. {in: Z{T: x1501}, wantErr: false},
  177. {in: Z{P: []byte(x1500)}, wantErr: false},
  178. {in: Z{P: []byte(x1501)}, wantErr: true},
  179. {in: Z{K: []byte(x1500)}, wantErr: false},
  180. {in: Z{K: []byte(x1501)}, wantErr: false},
  181. }
  182. for _, tt := range testCases {
  183. _, err := client.Put(ctx, NewIncompleteKey(ctx, "BasicsZ", nil), &tt.in)
  184. if (err != nil) != tt.wantErr {
  185. t.Errorf("client.Put %s got err %v, want err %t", tt.in, err, tt.wantErr)
  186. }
  187. }
  188. }
  189. type SQChild struct {
  190. I, J int
  191. T, U int64
  192. }
  193. type SQTestCase struct {
  194. desc string
  195. q *Query
  196. wantCount int
  197. wantSum int
  198. }
  199. func testSmallQueries(t *testing.T, ctx context.Context, client *Client, parent *Key, children []*SQChild,
  200. testCases []SQTestCase, extraTests ...func()) {
  201. keys := make([]*Key, len(children))
  202. for i := range keys {
  203. keys[i] = NewIncompleteKey(ctx, "SQChild", parent)
  204. }
  205. keys, err := client.PutMulti(ctx, keys, children)
  206. if err != nil {
  207. t.Fatalf("client.PutMulti: %v", err)
  208. }
  209. defer func() {
  210. err := client.DeleteMulti(ctx, keys)
  211. if err != nil {
  212. t.Errorf("client.DeleteMulti: %v", err)
  213. }
  214. }()
  215. for _, tc := range testCases {
  216. count, err := client.Count(ctx, tc.q)
  217. if err != nil {
  218. t.Errorf("Count %q: %v", tc.desc, err)
  219. continue
  220. }
  221. if count != tc.wantCount {
  222. t.Errorf("Count %q: got %d want %d", tc.desc, count, tc.wantCount)
  223. continue
  224. }
  225. }
  226. for _, tc := range testCases {
  227. var got []SQChild
  228. _, err := client.GetAll(ctx, tc.q, &got)
  229. if err != nil {
  230. t.Errorf("client.GetAll %q: %v", tc.desc, err)
  231. continue
  232. }
  233. sum := 0
  234. for _, c := range got {
  235. sum += c.I + c.J
  236. }
  237. if sum != tc.wantSum {
  238. t.Errorf("sum %q: got %d want %d", tc.desc, sum, tc.wantSum)
  239. continue
  240. }
  241. }
  242. for _, x := range extraTests {
  243. x()
  244. }
  245. }
  246. func TestFilters(t *testing.T) {
  247. if testing.Short() {
  248. t.Skip("Integration tests skipped in short mode")
  249. }
  250. ctx := context.Background()
  251. client := newClient(ctx, t)
  252. parent := NewKey(ctx, "SQParent", "TestFilters", 0, nil)
  253. now := time.Now().Truncate(time.Millisecond).Unix()
  254. children := []*SQChild{
  255. {I: 0, T: now, U: now},
  256. {I: 1, T: now, U: now},
  257. {I: 2, T: now, U: now},
  258. {I: 3, T: now, U: now},
  259. {I: 4, T: now, U: now},
  260. {I: 5, T: now, U: now},
  261. {I: 6, T: now, U: now},
  262. {I: 7, T: now, U: now},
  263. }
  264. baseQuery := NewQuery("SQChild").Ancestor(parent).Filter("T=", now)
  265. testSmallQueries(t, ctx, client, parent, children, []SQTestCase{
  266. {
  267. "I>1",
  268. baseQuery.Filter("I>", 1),
  269. 6,
  270. 2 + 3 + 4 + 5 + 6 + 7,
  271. },
  272. {
  273. "I>2 AND I<=5",
  274. baseQuery.Filter("I>", 2).Filter("I<=", 5),
  275. 3,
  276. 3 + 4 + 5,
  277. },
  278. {
  279. "I>=3 AND I<3",
  280. baseQuery.Filter("I>=", 3).Filter("I<", 3),
  281. 0,
  282. 0,
  283. },
  284. {
  285. "I=4",
  286. baseQuery.Filter("I=", 4),
  287. 1,
  288. 4,
  289. },
  290. }, func() {
  291. got := []*SQChild{}
  292. want := []*SQChild{
  293. {I: 0, T: now, U: now},
  294. {I: 1, T: now, U: now},
  295. {I: 2, T: now, U: now},
  296. {I: 3, T: now, U: now},
  297. {I: 4, T: now, U: now},
  298. {I: 5, T: now, U: now},
  299. {I: 6, T: now, U: now},
  300. {I: 7, T: now, U: now},
  301. }
  302. _, err := client.GetAll(ctx, baseQuery.Order("I"), &got)
  303. if err != nil {
  304. t.Errorf("client.GetAll: %v", err)
  305. }
  306. if !reflect.DeepEqual(got, want) {
  307. t.Errorf("compare: got=%v, want=%v", got, want)
  308. }
  309. }, func() {
  310. got := []*SQChild{}
  311. want := []*SQChild{
  312. {I: 7, T: now, U: now},
  313. {I: 6, T: now, U: now},
  314. {I: 5, T: now, U: now},
  315. {I: 4, T: now, U: now},
  316. {I: 3, T: now, U: now},
  317. {I: 2, T: now, U: now},
  318. {I: 1, T: now, U: now},
  319. {I: 0, T: now, U: now},
  320. }
  321. _, err := client.GetAll(ctx, baseQuery.Order("-I"), &got)
  322. if err != nil {
  323. t.Errorf("client.GetAll: %v", err)
  324. }
  325. if !reflect.DeepEqual(got, want) {
  326. t.Errorf("compare: got=%v, want=%v", got, want)
  327. }
  328. })
  329. }
  330. func TestEventualConsistency(t *testing.T) {
  331. if testing.Short() {
  332. t.Skip("Integration tests skipped in short mode")
  333. }
  334. ctx := context.Background()
  335. client := newClient(ctx, t)
  336. parent := NewKey(ctx, "SQParent", "TestEventualConsistency", 0, nil)
  337. now := time.Now().Truncate(time.Millisecond).Unix()
  338. children := []*SQChild{
  339. {I: 0, T: now, U: now},
  340. {I: 1, T: now, U: now},
  341. {I: 2, T: now, U: now},
  342. }
  343. query := NewQuery("SQChild").Ancestor(parent).Filter("T =", now).EventualConsistency()
  344. testSmallQueries(t, ctx, client, parent, children, nil, func() {
  345. got, err := client.Count(ctx, query)
  346. if err != nil {
  347. t.Fatalf("Count: %v", err)
  348. }
  349. if got < 0 || 3 < got {
  350. t.Errorf("Count: got %d, want [0,3]", got)
  351. }
  352. })
  353. }
  354. func TestProjection(t *testing.T) {
  355. if testing.Short() {
  356. t.Skip("Integration tests skipped in short mode")
  357. }
  358. ctx := context.Background()
  359. client := newClient(ctx, t)
  360. parent := NewKey(ctx, "SQParent", "TestProjection", 0, nil)
  361. now := time.Now().Truncate(time.Millisecond).Unix()
  362. children := []*SQChild{
  363. {I: 1 << 0, J: 100, T: now, U: now},
  364. {I: 1 << 1, J: 100, T: now, U: now},
  365. {I: 1 << 2, J: 200, T: now, U: now},
  366. {I: 1 << 3, J: 300, T: now, U: now},
  367. {I: 1 << 4, J: 300, T: now, U: now},
  368. }
  369. baseQuery := NewQuery("SQChild").Ancestor(parent).Filter("T=", now).Filter("J>", 150)
  370. testSmallQueries(t, ctx, client, parent, children, []SQTestCase{
  371. {
  372. "project",
  373. baseQuery.Project("J"),
  374. 3,
  375. 200 + 300 + 300,
  376. },
  377. {
  378. "distinct",
  379. baseQuery.Project("J").Distinct(),
  380. 2,
  381. 200 + 300,
  382. },
  383. {
  384. "project on meaningful (GD_WHEN) field",
  385. baseQuery.Project("U"),
  386. 3,
  387. 0,
  388. },
  389. })
  390. }
  391. func TestAllocateIDs(t *testing.T) {
  392. if testing.Short() {
  393. t.Skip("Integration tests skipped in short mode")
  394. }
  395. ctx := context.Background()
  396. client := newClient(ctx, t)
  397. keys := make([]*Key, 5)
  398. for i := range keys {
  399. keys[i] = NewIncompleteKey(ctx, "AllocID", nil)
  400. }
  401. keys, err := client.AllocateIDs(ctx, keys)
  402. if err != nil {
  403. t.Errorf("AllocID #0 failed: %v", err)
  404. }
  405. if want := len(keys); want != 5 {
  406. t.Errorf("Expected to allocate 5 keys, %d keys are found", want)
  407. }
  408. for _, k := range keys {
  409. if k.Incomplete() {
  410. t.Errorf("Unexpeceted incomplete key found: %v", k)
  411. }
  412. }
  413. }
  414. func TestGetAllWithFieldMismatch(t *testing.T) {
  415. if testing.Short() {
  416. t.Skip("Integration tests skipped in short mode")
  417. }
  418. ctx := context.Background()
  419. client := newClient(ctx, t)
  420. type Fat struct {
  421. X, Y int
  422. }
  423. type Thin struct {
  424. X int
  425. }
  426. // Ancestor queries (those within an entity group) are strongly consistent
  427. // by default, which prevents a test from being flaky.
  428. // See https://cloud.google.com/appengine/docs/go/datastore/queries#Go_Data_consistency
  429. // for more information.
  430. parent := NewKey(ctx, "SQParent", "TestGetAllWithFieldMismatch", 0, nil)
  431. putKeys := make([]*Key, 3)
  432. for i := range putKeys {
  433. putKeys[i] = NewKey(ctx, "GetAllThing", "", int64(10+i), parent)
  434. _, err := client.Put(ctx, putKeys[i], &Fat{X: 20 + i, Y: 30 + i})
  435. if err != nil {
  436. t.Fatalf("client.Put: %v", err)
  437. }
  438. }
  439. var got []Thin
  440. want := []Thin{
  441. {X: 20},
  442. {X: 21},
  443. {X: 22},
  444. }
  445. getKeys, err := client.GetAll(ctx, NewQuery("GetAllThing").Ancestor(parent), &got)
  446. if len(getKeys) != 3 && !reflect.DeepEqual(getKeys, putKeys) {
  447. t.Errorf("client.GetAll: keys differ\ngetKeys=%v\nputKeys=%v", getKeys, putKeys)
  448. }
  449. if !reflect.DeepEqual(got, want) {
  450. t.Errorf("client.GetAll: entities differ\ngot =%v\nwant=%v", got, want)
  451. }
  452. if _, ok := err.(*ErrFieldMismatch); !ok {
  453. t.Errorf("client.GetAll: got err=%v, want ErrFieldMismatch", err)
  454. }
  455. }
  456. func TestKindlessQueries(t *testing.T) {
  457. if testing.Short() {
  458. t.Skip("Integration tests skipped in short mode")
  459. }
  460. ctx := context.Background()
  461. client := newClient(ctx, t)
  462. type Dee struct {
  463. I int
  464. Why string
  465. }
  466. type Dum struct {
  467. I int
  468. Pling string
  469. }
  470. parent := NewKey(ctx, "Tweedle", "tweedle", 0, nil)
  471. keys := []*Key{
  472. NewKey(ctx, "Dee", "dee0", 0, parent),
  473. NewKey(ctx, "Dum", "dum1", 0, parent),
  474. NewKey(ctx, "Dum", "dum2", 0, parent),
  475. NewKey(ctx, "Dum", "dum3", 0, parent),
  476. }
  477. src := []interface{}{
  478. &Dee{1, "binary0001"},
  479. &Dum{2, "binary0010"},
  480. &Dum{4, "binary0100"},
  481. &Dum{8, "binary1000"},
  482. }
  483. keys, err := client.PutMulti(ctx, keys, src)
  484. if err != nil {
  485. t.Fatalf("put: %v", err)
  486. }
  487. testCases := []struct {
  488. desc string
  489. query *Query
  490. want []int
  491. wantErr string
  492. }{
  493. {
  494. desc: "Dee",
  495. query: NewQuery("Dee"),
  496. want: []int{1},
  497. },
  498. {
  499. desc: "Doh",
  500. query: NewQuery("Doh"),
  501. want: nil},
  502. {
  503. desc: "Dum",
  504. query: NewQuery("Dum"),
  505. want: []int{2, 4, 8},
  506. },
  507. {
  508. desc: "",
  509. query: NewQuery(""),
  510. want: []int{1, 2, 4, 8},
  511. },
  512. {
  513. desc: "Kindless filter",
  514. query: NewQuery("").Filter("__key__ =", keys[2]),
  515. want: []int{4},
  516. },
  517. {
  518. desc: "Kindless order",
  519. query: NewQuery("").Order("__key__"),
  520. want: []int{1, 2, 4, 8},
  521. },
  522. {
  523. desc: "Kindless bad filter",
  524. query: NewQuery("").Filter("I =", 4),
  525. wantErr: "kind is required for filter: I",
  526. },
  527. {
  528. desc: "Kindless bad order",
  529. query: NewQuery("").Order("-__key__"),
  530. wantErr: "kind is required for all orders except __key__ ascending",
  531. },
  532. }
  533. loop:
  534. for _, tc := range testCases {
  535. q := tc.query.Ancestor(parent)
  536. gotCount, err := client.Count(ctx, q)
  537. if err != nil {
  538. if tc.wantErr == "" || !strings.Contains(err.Error(), tc.wantErr) {
  539. t.Errorf("count %q: err %v, want err %q", tc.desc, err, tc.wantErr)
  540. }
  541. continue
  542. }
  543. if tc.wantErr != "" {
  544. t.Errorf("count %q: want err %q", tc.desc, tc.wantErr)
  545. continue
  546. }
  547. if gotCount != len(tc.want) {
  548. t.Errorf("count %q: got %d want %d", tc.desc, gotCount, len(tc.want))
  549. continue
  550. }
  551. var got []int
  552. for iter := client.Run(ctx, q); ; {
  553. var dst struct {
  554. I int
  555. Why, Pling string
  556. }
  557. _, err := iter.Next(&dst)
  558. if err == Done {
  559. break
  560. }
  561. if err != nil {
  562. t.Errorf("iter.Next %q: %v", tc.desc, err)
  563. continue loop
  564. }
  565. got = append(got, dst.I)
  566. }
  567. sort.Ints(got)
  568. if !reflect.DeepEqual(got, tc.want) {
  569. t.Errorf("elems %q: got %+v want %+v", tc.desc, got, tc.want)
  570. continue
  571. }
  572. }
  573. }
  574. func TestTransaction(t *testing.T) {
  575. if testing.Short() {
  576. t.Skip("Integration tests skipped in short mode")
  577. }
  578. ctx := context.Background()
  579. client := newClient(ctx, t)
  580. type Counter struct {
  581. N int
  582. T time.Time
  583. }
  584. bangErr := errors.New("bang")
  585. tests := []struct {
  586. desc string
  587. causeConflict []bool
  588. retErr []error
  589. want int
  590. wantErr error
  591. }{
  592. {
  593. desc: "no conflicts",
  594. causeConflict: []bool{false},
  595. retErr: []error{nil},
  596. want: 11,
  597. },
  598. {
  599. desc: "user error",
  600. causeConflict: []bool{false},
  601. retErr: []error{bangErr},
  602. wantErr: bangErr,
  603. },
  604. {
  605. desc: "2 conflicts",
  606. causeConflict: []bool{true, true, false},
  607. retErr: []error{nil, nil, nil},
  608. want: 15, // Each conflict increments by 2.
  609. },
  610. {
  611. desc: "3 conflicts",
  612. causeConflict: []bool{true, true, true},
  613. retErr: []error{nil, nil, nil},
  614. wantErr: ErrConcurrentTransaction,
  615. },
  616. }
  617. for _, tt := range tests {
  618. // Put a new counter.
  619. c := &Counter{N: 10, T: time.Now()}
  620. key, err := client.Put(ctx, NewIncompleteKey(ctx, "TransCounter", nil), c)
  621. if err != nil {
  622. t.Errorf("%s: client.Put: %v", tt.desc, err)
  623. continue
  624. }
  625. defer client.Delete(ctx, key)
  626. // Increment the counter in a transaction.
  627. // The test case can manually cause a conflict or return an
  628. // error at each attempt.
  629. var attempts int
  630. _, err = client.RunInTransaction(ctx, func(tx *Transaction) error {
  631. attempts++
  632. if attempts > len(tt.causeConflict) {
  633. return fmt.Errorf("too many attempts. Got %d, max %d", attempts, len(tt.causeConflict))
  634. }
  635. var c Counter
  636. if err := tx.Get(key, &c); err != nil {
  637. return err
  638. }
  639. c.N++
  640. if _, err := tx.Put(key, &c); err != nil {
  641. return err
  642. }
  643. if tt.causeConflict[attempts-1] {
  644. c.N += 1
  645. if _, err := client.Put(ctx, key, &c); err != nil {
  646. return err
  647. }
  648. }
  649. return tt.retErr[attempts-1]
  650. })
  651. // Check the error returned by RunInTransaction.
  652. if err != tt.wantErr {
  653. t.Errorf("%s: got err %v, want %v", tt.desc, err, tt.wantErr)
  654. continue
  655. }
  656. if err != nil {
  657. continue
  658. }
  659. // Check the final value of the counter.
  660. if err := client.Get(ctx, key, c); err != nil {
  661. t.Errorf("%s: client.Get: %v", err)
  662. continue
  663. }
  664. if c.N != tt.want {
  665. t.Errorf("%s: counter N=%d, want N=%d", c.N, tt.want)
  666. }
  667. }
  668. }
  669. func TestNilPointers(t *testing.T) {
  670. if testing.Short() {
  671. t.Skip("Integration tests skipped in short mode")
  672. }
  673. ctx := context.Background()
  674. client := newClient(ctx, t)
  675. type X struct {
  676. S string
  677. }
  678. src := []*X{{"zero"}, {"one"}}
  679. keys := []*Key{NewIncompleteKey(ctx, "NilX", nil), NewIncompleteKey(ctx, "NilX", nil)}
  680. keys, err := client.PutMulti(ctx, keys, src)
  681. if err != nil {
  682. t.Fatalf("PutMulti: %v", err)
  683. }
  684. // It's okay to store into a slice of nil *X.
  685. xs := make([]*X, 2)
  686. if err := client.GetMulti(ctx, keys, xs); err != nil {
  687. t.Errorf("GetMulti: %v", err)
  688. } else if !reflect.DeepEqual(xs, src) {
  689. t.Errorf("GetMulti fetched %v, want %v", xs, src)
  690. }
  691. // It isn't okay to store into a single nil *X.
  692. var x0 *X
  693. if err, want := client.Get(ctx, keys[0], x0), ErrInvalidEntityType; err != want {
  694. t.Errorf("Get: err %v; want %v", err, want)
  695. }
  696. if err := client.DeleteMulti(ctx, keys); err != nil {
  697. t.Errorf("Delete: %v", err)
  698. }
  699. }