func TestQueryExecution(t *testing.T) {
	t.Parallel()

	Convey("Test query execution", t, func() {
		c, err := info.Get(Use(context.Background())).Namespace("ns")
		if err != nil {
			panic(err)
		}

		data := ds.Get(c)
		testing := data.Raw().Testable()

		for _, tc := range queryExecutionTests {
			Convey(tc.name, func() {
				for i, stage := range tc.test {
					// outside of Convey, since these must always happen
					testing.CatchupIndexes()

					testing.AddIndexes(stage.addIdxs...)
					if err := data.PutMulti(stage.putEnts); err != nil {
						// prevent Convey from thinking this assertion should show up in
						// every test loop.
						panic(err)
					}

					if err := data.DeleteMulti(stage.delEnts); err != nil {
						panic(err)
					}

					Convey(fmt.Sprintf("stage %d", i), func() {
						for j, expect := range stage.expect {
							runner := func(f func(ic context.Context) error, _ *ds.TransactionOptions) error {
								return f(c)
							}
							if expect.inTxn {
								runner = data.RunInTransaction
							}

							if expect.keys != nil {
								runner(func(c context.Context) error {
									data := ds.Get(c)
									Convey(fmt.Sprintf("expect %d (keys)", j), func() {
										rslt := []ds.Key(nil)
										So(data.GetAll(expect.q, &rslt), ShouldBeNil)
										So(len(rslt), ShouldEqual, len(expect.keys))
										for i, r := range rslt {
											So(r, ShouldResemble, expect.keys[i])
										}
									})
									return nil
								}, &ds.TransactionOptions{XG: true})
							}

							if expect.get != nil {
								Convey(fmt.Sprintf("expect %d (data)", j), func() {
									runner(func(c context.Context) error {
										rslt := []ds.PropertyMap(nil)
										So(data.GetAll(expect.q, &rslt), ShouldBeNil)
										So(len(rslt), ShouldEqual, len(expect.get))
										for i, r := range rslt {
											So(r, ShouldResemble, expect.get[i])
										}
										return nil
									}, &ds.TransactionOptions{XG: true})
								})
							}
						}

						for j, fn := range stage.extraFns {
							Convey(fmt.Sprintf("extraFn %d", j), func() {
								fn(c)
							})
						}
					})
				}
			})
		}
	})
}
func TestQueryExecution(t *testing.T) {
	t.Parallel()

	Convey("Test query execution", t, func() {
		c, err := info.Get(Use(context.Background())).Namespace("ns")
		if err != nil {
			panic(err)
		}

		So(info.Get(c).FullyQualifiedAppID(), ShouldEqual, "dev~app")
		So(info.Get(c).GetNamespace(), ShouldEqual, "ns")

		data := ds.Get(c)
		testing := data.Testable()

		for _, tc := range queryExecutionTests {
			Convey(tc.name, func() {
				for i, stage := range tc.test {
					// outside of Convey, since these must always happen
					testing.CatchupIndexes()

					testing.AddIndexes(stage.addIdxs...)
					if err := data.PutMulti(stage.putEnts); err != nil {
						// prevent Convey from thinking this assertion should show up in
						// every test loop.
						panic(err)
					}

					if err := data.DeleteMulti(stage.delEnts); err != nil {
						panic(err)
					}

					Convey(fmt.Sprintf("stage %d", i), func() {
						for j, expect := range stage.expect {
							runner := func(f func(ic context.Context) error, _ *ds.TransactionOptions) error {
								return f(c)
							}
							if expect.inTxn {
								runner = data.RunInTransaction
							}

							if expect.count == 0 {
								if len(expect.keys) > 0 {
									expect.count = len(expect.keys)
								} else {
									expect.count = len(expect.get)
								}
							}

							if expect.keys != nil {
								Convey(fmt.Sprintf("expect %d (keys)", j), func() {
									err := runner(func(c context.Context) error {
										data := ds.Get(c)
										count, err := data.Count(expect.q)
										So(err, ShouldBeNil)
										So(count, ShouldEqual, expect.count)

										rslt := []*ds.Key(nil)
										So(data.GetAll(expect.q, &rslt), ShouldBeNil)
										So(len(rslt), ShouldEqual, len(expect.keys))
										for i, r := range rslt {
											So(r, ShouldResemble, expect.keys[i])
										}
										return nil
									}, &ds.TransactionOptions{XG: true})
									So(err, ShouldBeNil)
								})
							}

							if expect.get != nil {
								Convey(fmt.Sprintf("expect %d (data)", j), func() {
									err := runner(func(c context.Context) error {
										data := ds.Get(c)
										count, err := data.Count(expect.q)
										So(err, ShouldBeNil)
										So(count, ShouldEqual, expect.count)

										rslt := []ds.PropertyMap(nil)
										So(data.GetAll(expect.q, &rslt), ShouldBeNil)
										So(len(rslt), ShouldEqual, len(expect.get))
										for i, r := range rslt {
											So(r, ShouldResemble, expect.get[i])
										}
										return nil
									}, &ds.TransactionOptions{XG: true})
									So(err, ShouldBeNil)
								})
							}
						}

						for j, fn := range stage.extraFns {
							Convey(fmt.Sprintf("extraFn %d", j), func() {
								fn(c)
							})
						}
					})
				}
			})
		}
	})

	Convey("Test AutoIndex", t, func() {
		c, err := info.Get(Use(context.Background())).Namespace("ns")
		if err != nil {
			panic(err)
		}

		data := ds.Get(c)
		testing := data.Testable()
		testing.Consistent(true)

		So(data.Put(pmap("$key", key("Kind", 1), Next,
			"Val", 1, 2, 3, Next,
			"Extra", "hello",
		)), ShouldBeNil)

		So(data.Put(pmap("$key", key("Kind", 2), Next,
			"Val", 2, 3, 9, Next,
			"Extra", "ace", "hello", "there",
		)), ShouldBeNil)

		q := nq("Kind").Gt("Val", 2).Order("Val", "Extra")

		count, err := data.Count(q)
		So(err, ShouldErrLike, "Insufficient indexes")

		testing.AutoIndex(true)

		count, err = data.Count(q)
		So(err, ShouldBeNil)
		So(count, ShouldEqual, 2)
	})
}