github.com/go-kivik/kivik/v4@v4.3.2/kiviktest/db/query.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package db 14 15 import ( 16 "context" 17 "fmt" 18 "sort" 19 20 "gitlab.com/flimzy/testy" 21 22 "github.com/go-kivik/kivik/v4" 23 "github.com/go-kivik/kivik/v4/kiviktest/kt" 24 ) 25 26 func init() { 27 kt.Register("Query", query) 28 } 29 30 func query(ctx *kt.Context) { 31 ctx.RunRW(func(ctx *kt.Context) { 32 testQueryRW(ctx) 33 }) 34 } 35 36 func testQueryRW(ctx *kt.Context) { 37 if ctx.Admin == nil { 38 // Can't do anything here without admin access 39 return 40 } 41 dbName, expected, err := setUpQueryTest(ctx) 42 if err != nil { 43 ctx.Errorf("Failed to set up temp db: %s", err) 44 } 45 ctx.Run("group", func(ctx *kt.Context) { 46 ctx.RunAdmin(func(ctx *kt.Context) { 47 doQueryTest(ctx, ctx.Admin, dbName, 0, expected) 48 }) 49 ctx.RunNoAuth(func(ctx *kt.Context) { 50 doQueryTest(ctx, ctx.NoAuth, dbName, 0, expected) 51 }) 52 }) 53 } 54 55 var ddoc = map[string]interface{}{ 56 "_id": "_design/testddoc", 57 "language": "javascript", 58 "views": map[string]interface{}{ 59 "testview": map[string]interface{}{ 60 "map": `function(doc) { 61 if (doc.include) { 62 emit(doc._id, doc.index); 63 } 64 }`, 65 }, 66 }, 67 } 68 69 func setUpQueryTest(ctx *kt.Context) (dbName string, docIDs []string, err error) { 70 dbName = ctx.TestDB() 71 db := ctx.Admin.DB(dbName, ctx.Options("db")) 72 if err := db.Err(); err != nil { 73 return dbName, nil, fmt.Errorf("fialed to connect to db: %w", err) 74 } 75 if _, err := db.Put(context.Background(), ddoc["_id"].(string), ddoc); err != nil { 76 return dbName, nil, fmt.Errorf("fialed to create design doc: %w", err) 77 } 78 const maxDocs = 10 79 docIDs = make([]string, maxDocs) 80 for i := range docIDs { 81 id := ctx.TestDBName() 82 doc := struct { 83 ID string `json:"id"` 84 Include bool `json:"include"` 85 Index int `json:"index"` 86 }{ 87 ID: id, 88 Include: true, 89 Index: i, 90 } 91 if _, err := db.Put(context.Background(), doc.ID, doc); err != nil { 92 return dbName, nil, fmt.Errorf("failed to create doc: %w", err) 93 } 94 docIDs[i] = id 95 } 96 sort.Strings(docIDs) 97 return dbName, docIDs, nil 98 } 99 100 func doQueryTest(ctx *kt.Context, client *kivik.Client, dbName string, expOffset int64, expected []string) { 101 ctx.Run("WithDocs", func(ctx *kt.Context) { 102 doQueryTestWithDocs(ctx, client, dbName, expOffset, expected) 103 }) 104 ctx.Run("WithoutDocs", func(ctx *kt.Context) { 105 doQueryTestWithoutDocs(ctx, client, dbName, expOffset, expected) 106 }) 107 } 108 109 func doQueryTestWithoutDocs(ctx *kt.Context, client *kivik.Client, dbName string, expOffset int64, expected []string) { 110 ctx.Parallel() 111 db := client.DB(dbName, ctx.Options("db")) 112 // Errors may be deferred here, so only return if we actually get 113 // an error. 114 if err := db.Err(); err != nil && !ctx.IsExpectedSuccess(err) { 115 return 116 } 117 118 rows := db.Query(context.Background(), "testddoc", "testview") 119 if !ctx.IsExpectedSuccess(rows.Err()) { 120 return 121 } 122 docIDs := make([]string, 0, len(expected)) 123 var scanTested bool 124 for rows.Next() { 125 id, _ := rows.ID() 126 docIDs = append(docIDs, id) 127 if !scanTested { 128 scanTested = true 129 ctx.Run("ScanDoc", func(ctx *kt.Context) { 130 var i interface{} 131 ctx.CheckError(rows.ScanDoc(&i)) 132 }) 133 ctx.Run("ScanValue", func(ctx *kt.Context) { 134 var i interface{} 135 ctx.CheckError(rows.ScanValue(&i)) 136 }) 137 } 138 } 139 meta, err := rows.Metadata() 140 if err != nil { 141 ctx.Fatalf("Failed to fetch row: %s", rows.Err()) 142 } 143 if d := testy.DiffTextSlices(expected, docIDs); d != nil { 144 ctx.Errorf("Unexpected document IDs returned:\n%s\n", d) 145 } 146 if expOffset != meta.Offset { 147 ctx.Errorf("offset: Expected %d, got %d", expOffset, meta.Offset) 148 } 149 if int64(len(expected)) != meta.TotalRows { 150 ctx.Errorf("total rows: Expected %d, got %d", len(expected), meta.TotalRows) 151 } 152 } 153 154 func doQueryTestWithDocs(ctx *kt.Context, client *kivik.Client, dbName string, expOffset int64, expected []string) { 155 ctx.Parallel() 156 db := client.DB(dbName, ctx.Options("db")) 157 // Errors may be deferred here, so only return if we actually get 158 // an error. 159 if err := db.Err(); err != nil && !ctx.IsExpectedSuccess(err) { 160 return 161 } 162 opts := kivik.Params(map[string]interface{}{ 163 "include_docs": true, 164 "update_seq": true, 165 }) 166 167 rows := db.Query(context.Background(), "testddoc", "testview", opts) 168 if !ctx.IsExpectedSuccess(rows.Err()) { 169 return 170 } 171 docIDs := make([]string, 0, len(expected)) 172 for rows.Next() { 173 var doc struct { 174 ID string `json:"_id"` 175 Rev string `json:"_rev"` 176 Index int `json:"index"` 177 } 178 if err := rows.ScanDoc(&doc); err != nil { 179 ctx.Errorf("Failed to scan doc: %s", err) 180 } 181 var value int 182 if err := rows.ScanValue(&value); err != nil { 183 ctx.Errorf("Failed to scan value: %s", err) 184 } 185 if value != doc.Index { 186 ctx.Errorf("doc._rev = %d, but value = %d", doc.Index, value) 187 } 188 id, _ := rows.ID() 189 if doc.ID != id { 190 ctx.Errorf("doc._id = %s, but rows.ID = %s", doc.ID, id) 191 } 192 docIDs = append(docIDs, id) 193 } 194 meta, err := rows.Metadata() 195 if err != nil { 196 ctx.Fatalf("Failed to fetch row: %s", rows.Err()) 197 } 198 if d := testy.DiffTextSlices(expected, docIDs); d != nil { 199 ctx.Errorf("Unexpected document IDs returned:\n%s\n", d) 200 } 201 if expOffset != meta.Offset { 202 ctx.Errorf("offset: Expected %d, got %d", expOffset, meta.Offset) 203 } 204 ctx.Run("UpdateSeq", func(ctx *kt.Context) { 205 if meta.UpdateSeq == "" { 206 ctx.Errorf("Expected updated sequence") 207 } 208 }) 209 if int64(len(expected)) != meta.TotalRows { 210 ctx.Errorf("total rows: Expected %d, got %d", len(expected), meta.TotalRows) 211 } 212 }