func godeep(field *ast.Field, path string, base *cmap.ConcurrentMap) { alias := field.Name.Value if field.Alias != nil { alias = field.Alias.Value } if field.SelectionSet == nil { // end of line var val []byte if field.Name.Value == "_val" { val, _ = db.GetValueAt(path) } else { val, _ = db.GetValueAt(path + "/" + field.Name.Value) } base.Set(alias, db.FromLevel(val)) } else { // will continue with the following selections next := cmap.New() base.Set(alias, next) godeepAsyncMultiple( field.SelectionSet.Selections, path+"/"+field.Name.Value, &next, ) } }
// this method only exists for compatibility with PouchDB and should not be used elsewhere. func RevsDiff(w http.ResponseWriter, r *http.Request) { ctx := getContext(r) path := db.CleanPath(ctx.path) res := make(map[string]responses.RevsDiffResult) for id, irevs := range ctx.jsonBody { missing := make([]string, 0) currentRevb, err := db.GetValueAt(path + "/" + id + "/_rev") if err != nil { /* no _rev for this id, means it has never been inserted in this database. let's say we miss all the revs. */ for _, irev := range irevs.([]interface{}) { rev := irev.(string) missing = append(missing, rev) } } else { /* otherwise we will say we have the current rev and none of the others, because that's the truth. */ // TODO maybe here we should say that we have these old revs // -- either we actually have them, but they are deleted, or // we don't, but they are unnecessary anyway. currentRev := string(currentRevb) for _, irev := range irevs.([]interface{}) { rev := irev.(string) if rev != currentRev { missing = append(missing, rev) } } } res[id] = responses.RevsDiffResult{Missing: missing} } w.WriteHeader(200) json.NewEncoder(w).Encode(res) }
func TestBasics(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("basics test", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should save a tree", func() { db.SaveTreeAt("/fruits/banana", map[string]interface{}{ "colour": "yellow", "hardness": "low", "_val": "a fruit.", }) Expect(db.GetValueAt("/fruits/banana")).To(BeEquivalentTo(`"a fruit."`)) Expect(db.GetValueAt("/fruits/banana/colour")).To(BeEquivalentTo(`"yellow"`)) Expect(db.GetValueAt("/fruits/banana/hardness")).To(BeEquivalentTo(`"low"`)) _, err := db.GetValueAt("/fruits") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/fruits")).To(Equal(map[string]interface{}{ "banana": map[string]interface{}{ "colour": value("yellow"), "hardness": value("low"), "_val": "a fruit.", }, })) }) g.It("should modify a subvalue of the tree", func() { db.SaveValueAt("/fruits/banana/colour", []byte(`"black-and-yellow"`)) Expect(db.GetValueAt("/fruits/banana/colour")).To(BeEquivalentTo(`"black-and-yellow"`)) Expect(db.GetValueAt("/fruits/banana/hardness")).To(BeEquivalentTo(`"low"`)) }) g.It("should add a value deeply nested in a tree that doesn't exists", func() { db.SaveValueAt("/fruits/mellon/season", []byte(`"spring"`)) Expect(db.GetValueAt("/fruits/mellon/season")).To(BeEquivalentTo(`"spring"`)) Expect(db.GetTreeAt("/fruits")).To(Equal(map[string]interface{}{ "banana": map[string]interface{}{ "colour": value("black-and-yellow"), "hardness": value("low"), "_val": "a fruit.", }, "mellon": map[string]interface{}{ "season": value("spring"), }, })) }) g.It("should add a tree deeply nested like the previous", func() { db.SaveTreeAt("/fruits/orange", map[string]interface{}{ "colour": "orange", "hardness": "medium", "_val": "name == colour", }) Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) Expect(db.GetValueAt("/fruits/orange")).To(BeEquivalentTo(`"name == colour"`)) Expect(db.GetTreeAt("/fruits/orange")).To(Equal(map[string]interface{}{ "_val": "name == colour", "colour": value("orange"), "hardness": value("medium"), })) }) g.It("should delete a key", func() { db.DeleteAt("/fruits/banana/colour") Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/banana/colour") Expect(db.GetValueAt("/fruits/banana/colour/_deleted")).To(BeEquivalentTo("")) Expect(err).To(HaveOccurred()) }) g.It("should delete a value when setting it to null with a tree", func() { db.SaveTreeAt("/fruits/mellon", map[string]interface{}{ "colour": "orange", "season": nil, }) Expect(db.GetValueAt("/fruits/mellon/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/mellon/season") Expect(err).To(HaveOccurred()) db.SaveTreeAt("/fruits", map[string]interface{}{ "mellon": nil, }) _, err = db.GetValueAt("/fruits/mellon/colour") Expect(err).To(HaveOccurred()) _, err = db.GetValueAt("/fruits/mellon") Expect(err).To(HaveOccurred()) }) g.It("should delete a tree", func() { db.DeleteAt("/fruits/banana") Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/banana/hardness") Expect(err).To(HaveOccurred()) rev, err := db.DeleteAt("/fruits") Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("9-")) _, err = db.GetValueAt("/fruits") Expect(err).To(HaveOccurred()) Expect(db.GetValueAt("/fruits/orange/_deleted")).To(BeEquivalentTo("")) _, err = db.GetValueAt("/fruits/orange/colour") Expect(err).To(HaveOccurred()) _, err = db.GetValueAt("/fruits/banana/hardness") Expect(err).To(HaveOccurred()) }) g.It("should error when fetching an untouched tree path", func() { _, err := db.GetTreeAt("/nowhere") Expect(err).To(HaveOccurred()) }) g.It("should return when fetching a deleted tree path", func() { tree, err := db.GetTreeAt("/fruits/banana") Expect(err).ToNot(HaveOccurred()) empty := make(map[string]interface{}) Expect(tree).To(BeEquivalentTo(empty)) }) }) }
func TestCouchDBSpecialEndpoints(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("couchdb db special endpoints", func() { g.BeforeEach(func() { rec = httptest.NewRecorder() server = handle.BuildHandler() }) g.Before(func() { db.Erase() db.Start() populateDB() }) g.After(func() { db.End() }) var rev string var oldrev string var id string g.It("_all_docs for a sub db", func() { r, _ = http.NewRequest("GET", "/vehicles/_all_docs", nil) server.ServeHTTP(rec, r) var res responses.AllDocs json.Unmarshal(rec.Body.Bytes(), &res) Expect(res.Rows).To(HaveLen(3)) Expect(res.Rows[2].Id).To(Equal(res.Rows[2].Key)) rev, _ := res.Rows[2].Value.(map[string]interface{})["rev"] Expect(rev).To(HavePrefix("1-")) keys := []string{res.Rows[0].Id, res.Rows[1].Key, res.Rows[2].Id} Expect(keys).To(ConsistOf("airplane", "boat", "car")) }) g.It("_all_docs with selected keys", func() { r, _ = http.NewRequest("GET", "/vehicles/_all_docs?keys=%5B%22airplane%22,%22boat%22%5D", nil) server.ServeHTTP(rec, r) var res responses.AllDocs json.Unmarshal(rec.Body.Bytes(), &res) Expect(res.Rows).To(HaveLen(2)) keys := []string{res.Rows[0].Id, res.Rows[1].Key} Expect(keys).To(ConsistOf("airplane", "boat")) }) g.It("all_docs with include_docs -- for another sub db", func() { r, _ = http.NewRequest("GET", "/vehicles/airplane/_all_docs?include_docs=true", nil) server.ServeHTTP(rec, r) var res responses.AllDocs json.Unmarshal(rec.Body.Bytes(), &res) Expect(res.Rows).To(HaveLen(3)) docid, _ := res.Rows[1].Doc["_id"] Expect(res.Rows[1].Key).To(Equal(docid)) rev, _ := res.Rows[1].Value.(map[string]interface{})["rev"] docrev, _ := res.Rows[1].Doc["_rev"] Expect(rev).To(Equal(docrev)) keys := []string{res.Rows[0].Id, res.Rows[1].Key, res.Rows[2].Id} sort.Strings(keys) Expect(keys).To(Equal([]string{"air", "land", "water"})) docs := map[string]interface{}{ res.Rows[0].Key: res.Rows[0].Doc, res.Rows[1].Key: res.Rows[1].Doc, res.Rows[2].Key: res.Rows[2].Doc, } Expect(docs).To(HaveKey("air")) Expect(res.Rows[0].Doc).To(HaveKey("_rev")) Expect(res.Rows[0].Doc).To(HaveKeyWithValue("_id", res.Rows[0].Id)) }) g.It("_bulk_get", func() { r, _ = http.NewRequest("POST", "/vehicles/_bulk_get", bytes.NewReader([]byte(`{ "docs": [ {"id": "nonexisting-doc"}, {"id": "car"}, {"_id": "airplane"} ] }`))) server.ServeHTTP(rec, r) var res responses.BulkGet json.Unmarshal(rec.Body.Bytes(), &res) Expect(res.Results[0].Docs[0].Ok).To(BeNil()) Expect(res.Results[0].Docs[0].Error).ToNot(BeNil()) Expect(res.Results[1].Docs[0].Ok).ToNot(BeNil()) Expect(res.Results[1].Docs[0].Error).To(BeNil()) doc := *res.Results[1].Docs[0].Ok id, _ := doc["_id"] irev, _ := doc["_rev"] rev = irev.(string) water, _ := doc["water"] Expect(id.(string)).To(Equal("car")) Expect(res.Results[1].Id).To(Equal(id)) Expect(water).To(BeEquivalentTo(value(false))) Expect(res.Results[2].Docs[0].Ok).To(BeNil()) Expect(res.Results[0].Docs[0].Error).ToNot(BeNil()) }) g.It("_bulk_docs", func() { r, _ = http.NewRequest("POST", "/vehicles/_bulk_docs", bytes.NewReader([]byte(`{ "docs": [ {"everywhere": true}, {"_id": "car", "_rev": "`+rev+`", "space": false, "land": true}, {"_id": "airplane", "nowhere": false}, {"_id": "_local/.abchtru", "replication+data": "k"}, {"_id": "empty-doc"}, {"_id": "doc-with-a-rev-already-set", "_rev": "4-sa98hsa3i4", "val": 33} ] }`))) server.ServeHTTP(rec, r) Expect(rec.Code).To(Equal(201)) var res []responses.BulkDocsResult json.Unmarshal(rec.Body.Bytes(), &res) Expect(res).To(HaveLen(6)) Expect(res[0].Error).To(Equal("")) Expect(res[0].Ok).To(Equal(true)) Expect(res[0].Rev).To(HavePrefix("1-")) id = res[0].Id Expect(res[1].Id).To(Equal("car")) prevn, _ := strconv.Atoi(strings.Split(rev, "-")[0]) Expect(res[1].Rev).To(HavePrefix(fmt.Sprintf("%d-", prevn+1))) oldrev = rev rev = res[1].Rev cfe := responses.ConflictError() Expect(res[2].Error).To(Equal(cfe.Error)) Expect(res[3].Id).To(Equal("_local/.abchtru")) Expect(res[3].Ok).To(Equal(true)) Expect(res[4].Ok).To(Equal(true)) Expect(res[4].Rev).To(HavePrefix("1-")) Expect(res[5].Ok).To(Equal(true)) Expect(res[5].Rev).To(HavePrefix("5-")) }) g.It("_bulk_docs with new_edits=false", func() { r, _ = http.NewRequest("POST", "/animals/_bulk_docs", bytes.NewReader([]byte(`{ "docs": [ {"_id": "0", "_rev": "34-83fsop4", "name": "albatroz"}, {"_id": "1", "_rev": "0-a0a0a0a0", "name": "puppy"}, {"_id": "2"} ], "new_edits": false }`))) server.ServeHTTP(rec, r) Expect(rec.Code).To(Equal(201)) var res []responses.BulkDocsResult json.Unmarshal(rec.Body.Bytes(), &res) Expect(res).To(HaveLen(3)) Expect(res[0].Ok).To(Equal(true)) Expect(res[1].Ok).To(Equal(true)) Expect(res[2].Ok).To(Equal(false)) Expect(db.GetRev("/animals/0")).To(BeEquivalentTo("34-83fsop4")) Expect(db.GetRev("/animals/1")).ToNot(BeEquivalentTo("0-a0a0a0a0")) Expect(db.GetValueAt("/animals/0/name")).To(BeEquivalentTo(`"albatroz"`)) Expect(db.GetValueAt("/animals/1/name")).To(BeEquivalentTo(`"dog"`)) }) g.It("should have the correct docs saved", func() { Expect(db.GetValueAt("/vehicles/" + id + "/everywhere")).To(BeEquivalentTo("true")) Expect(db.GetLocalDocJsonAt("/vehicles/_local/.abchtru")).To(MatchJSON(`{ "_id": "_local/.abchtru", "_rev": "0-1", "replication+data": "k" }`)) }) g.It("shouldn't show _local docs on _all_docs", func() { r, _ = http.NewRequest("GET", "/vehicles/_all_docs", nil) server.ServeHTTP(rec, r) var res responses.AllDocs json.Unmarshal(rec.Body.Bytes(), &res) Expect(res.Rows).To(HaveLen(5)) }) g.It("_revs_diff", func() { r, _ = http.NewRequest("POST", "/vehicles/_revs_diff", bytes.NewReader([]byte(`{ "everywhere": ["2-invalidrev"], "car": ["`+oldrev+`", "`+rev+`", "1-invalidrev"], "airplane": ["1-nonexisting"] }`))) server.ServeHTTP(rec, r) Expect(rec.Code).To(Equal(200)) var res map[string]responses.RevsDiffResult json.Unmarshal(rec.Body.Bytes(), &res) everywhere, _ := res["everywhere"] car, _ := res["car"] airplane, _ := res["airplane"] Expect(everywhere.Missing).To(Equal([]string{"2-invalidrev"})) Expect(car.Missing).To(Equal([]string{oldrev, "1-invalidrev"})) Expect(airplane.Missing).To(Equal([]string{"1-nonexisting"})) }) }) }
func TestRevs(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("_rev", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should generate _rev for a single key", func() { savedrev, _ := db.SaveValueAt("/name", []byte(`"database of vehicles"`)) gottenrev, _ := db.GetValueAt("/name/_rev") Expect(savedrev).To(BeEquivalentTo(gottenrev)) Expect(gottenrev).To(HavePrefix("1-")) }) g.It("should generate _rev for parent keys", func() { db.SaveValueAt("/vehicles/car/land", []byte("true")) db.SaveValueAt("/vehicles/carriage/land", []byte("true")) db.SaveValueAt("/vehicles/carriage/air", []byte("false")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump _rev for single keys", func() { db.SaveValueAt("/name", []byte(`"just a database of vehicles"`)) Expect(db.GetValueAt("/name/_rev")).To(HavePrefix("2-")) }) g.It("should bump _rev for parent keys", func() { db.SaveValueAt("/vehicles/car/water", []byte("false")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) db.SaveValueAt("/vehicles/boat/water", []byte("true")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("5-")) }) g.It("on delete, should bump _rev for parents and sons", func() { db.DeleteAt("/vehicles/car") Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("6-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump rev of all parents of affected keys", func() { db.SaveTreeAt("/vehicles/boat", map[string]interface{}{ "water": true, "land": false, "air": false, }) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("7-")) }) g.It("doing it again to make sure", func() { db.SaveTreeAt("/vehicles", map[string]interface{}{ "car": map[string]interface{}{ "water": true, }, "boat": map[string]interface{}{ "air": true, }, }) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("8-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump the revs correctly when a tree operation involves deleting", func() { db.SaveTreeAt("/vehicles", map[string]interface{}{ "carriage": map[string]interface{}{ "space": false, "land": nil, }, "boat": nil, }) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("9-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("3-")) }) g.It("should bump revs of intermediate paths when modifying a deep field", func() { db.SaveValueAt("/vehicles/train/land/rail", []byte("true")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("10-")) Expect(db.GetValueAt("/vehicles/train/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/train/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/train/land/rail/_rev")).To(HavePrefix("1-")) db.DeleteAt("/vehicles/train") Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("11-")) Expect(db.GetValueAt("/vehicles/train/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/train/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/train/land/rail/_rev")).To(HavePrefix("2-")) db.SaveTreeAt("", map[string]interface{}{ "vehicles": map[string]interface{}{ "skate": map[string]interface{}{ "air": map[string]interface{}{ "carried": map[string]interface{}{ "_val": true, }, }, }, }, }) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("12-")) Expect(db.GetValueAt("/vehicles/skate/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/skate/air/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/skate/air/carried/_rev")).To(HavePrefix("1-")) db.SaveTreeAt("", map[string]interface{}{ "vehicles": map[string]interface{}{ "skate": map[string]interface{}{ "air": map[string]interface{}{ "carried": nil, }, }, }, }) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("13-")) Expect(db.GetValueAt("/vehicles/skate/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/skate/air/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/skate/air/carried/_rev")).To(HavePrefix("2-")) }) g.It("should return rev", func() { sk, err := db.GetSpecialKeysAt("/vehicles/skate") Expect(err).ToNot(HaveOccurred()) Expect(sk.Rev).To(HavePrefix("2-")) }) }) }
func TestCouchDBDocsSpecial(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("couchdb documents special endpoints", func() { g.BeforeEach(func() { rec = httptest.NewRecorder() server = handle.BuildHandler() }) g.Before(func() { db.Erase() db.Start() populateDB() }) g.After(func() { db.End() }) var rev string g.It("should change some values to generate revs", func() { brev, _ := db.GetValueAt("/vehicles/boat/air/_rev") r, _ = http.NewRequest("PUT", "/vehicles/boat/air/_val", bytes.NewReader([]byte("true"))) r.Header.Add("If-Match", string(brev)) server.ServeHTTP(rec, r) Expect(rec.Code).To(Equal(201)) var res responses.Success json.Unmarshal(rec.Body.Bytes(), &res) rev = res.Rev }) g.It("once more:", func() { r, _ = http.NewRequest("PATCH", "/vehicles/boat/air", bytes.NewReader([]byte(`{ "_rev": "`+rev+`", "really?": false }`))) server.ServeHTTP(rec, r) Expect(rec.Code).To(Equal(200)) }) g.It("should fetch some key with the _revisions special field", func() { r, _ = http.NewRequest("GET", "/vehicles?revs=true", nil) server.ServeHTTP(rec, r) var res map[string]interface{} json.Unmarshal(rec.Body.Bytes(), &res) irevisions, ok := res["_revisions"] Expect(ok).To(Equal(true)) revisions := irevisions.(map[string]interface{}) start, _ := revisions["start"] ids, _ := revisions["ids"] Expect(start).To(BeEquivalentTo(1)) Expect(ids).To(HaveLen(3)) }) g.It("should fetch some key with the _revs_info special field", func() { r, _ = http.NewRequest("GET", "/vehicles/boat?revs_info=true", nil) server.ServeHTTP(rec, r) var res map[string]interface{} json.Unmarshal(rec.Body.Bytes(), &res) irevsinfo, ok := res["_revs_info"] Expect(ok).To(Equal(true)) revsinfo := irevsinfo.([]interface{}) Expect(revsinfo).To(HaveLen(3)) first := revsinfo[0].(map[string]interface{}) second := revsinfo[1].(map[string]interface{}) status, _ := first["status"] Expect(status).To(BeEquivalentTo("available")) status, _ = second["status"] Expect(status).To(BeEquivalentTo("missing")) }) }) }
func Get(w http.ResponseWriter, r *http.Request) { ctx := getContext(r) /* here we handle special endpoints that, in CouchDB, refer to a database, but here are treated as documents (or better, subtrees) methods */ if ctx.wantsDatabaseInfo { DatabaseInfo(w, r) return } else if ctx.lastKey == "_changes" { Changes(w, r) return } else if ctx.lastKey == "_all_docs" { AllDocs(w, r) return } else if ctx.lastKey == "_security" { ReadSecurity(w, r) return } else if ctx.lastKey == "_missing_revs" { return } var response []byte var err error if !ctx.exists { res := responses.NotFound() w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } if ctx.localDoc { jsondoc, err := db.GetLocalDocJsonAt(ctx.path) if err != nil { // ctx.exists doesn't work for _local docs res := responses.NotFound() w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } w.WriteHeader(200) w.Header().Add("Content-Type", "application/json") w.Write(jsondoc) return } if ctx.wantsTree { var tree map[string]interface{} tree, err = db.GetTreeAt(ctx.path) if err == nil { tree["_id"] = ctx.lastKey tree["_rev"] = ctx.currentRev if flag(r, "revs") { var revs []string revs, err = db.RevsAt(ctx.path) if err == nil { var start int start, err = strconv.Atoi(strings.Split(revs[0], "-")[0]) revids := make([]string, len(revs)) for i, rev := range revs { revids[i] = strings.Split(rev, "-")[1] } tree["_revisions"] = responses.Revisions{ Start: start, Ids: revids, } } } if flag(r, "revs_info") { var revs []string revs, err = db.RevsAt(ctx.path) if err == nil { revsInfo := make([]responses.RevInfo, len(revs)) i := 0 for r := len(revs) - 1; r >= 0; r-- { revsInfo[i] = responses.RevInfo{ Rev: revs[r], Status: "missing", } i++ } revsInfo[0].Status = "available" tree["_revs_info"] = revsInfo } } response, err = json.Marshal(tree) } } else { if ctx.lastKey == "_rev" { response = []byte(ctx.currentRev) } else { response, err = db.GetValueAt(ctx.path) if err != nil { res := responses.NotFound() w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } } } if err != nil { res := responses.UnknownError(err.Error()) w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } w.WriteHeader(200) w.Header().Add("Content-Type", "application/json") w.Write(response) }
func TestIdempotent(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("idempotent put", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should put a tree", func() { Expect(db.ReplaceTreeAt("/fruits/banana", map[string]interface{}{ "colour": "yellow", "hardness": "low", "_val": "a fruit.", }, false)).To(HavePrefix("1-")) Expect(db.GetValueAt("/fruits/banana")).To(BeEquivalentTo(`"a fruit."`)) Expect(db.GetValueAt("/fruits/banana/colour")).To(BeEquivalentTo(`"yellow"`)) }) g.It("should replace it with a totally different object with arrays", func() { rev, err := db.ReplaceTreeAt("", map[string]interface{}{ "what": "numbers", "numbers": []interface{}{"zero", "one", "two", "three"}, }, false) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("2-")) Expect(db.GetValueAt("/numbers/3")).To(BeEquivalentTo(`"three"`)) _, err = db.GetValueAt("/numbers") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("")).To(Equal(map[string]interface{}{ "what": value("numbers"), "numbers": map[string]interface{}{ "0": value("zero"), "1": value("one"), "2": value("two"), "3": value("three"), }, })) _, err = db.GetValueAt("/fruits") Expect(err).To(HaveOccurred()) }) g.It("should replace it again with a totally different object", func() { Expect(db.ReplaceTreeAt("/fruits/orange", map[string]interface{}{ "colour": "orange", }, false)).To(HavePrefix("1-")) Expect(db.GetTreeAt("")).To(Equal(map[string]interface{}{ "what": value("numbers"), "numbers": map[string]interface{}{ "0": value("zero"), "1": value("one"), "2": value("two"), "3": value("three"), }, "fruits": map[string]interface{}{ "orange": map[string]interface{}{ "colour": value("orange"), }, }, })) }) g.It("should put an empty object", func() { rev, err := db.ReplaceTreeAt("/fruits/watermellon", map[string]interface{}{}, false) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("1-")) }) }) }
func TestArrays(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("array values", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should save a tree with a simple array", func() { rev, err := db.SaveTreeAt("", map[string]interface{}{ "numbers": []interface{}{"zero", "one", "two", "three"}, }) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("1-")) Expect(db.GetValueAt("/numbers/0")).To(BeEquivalentTo(`"zero"`)) Expect(db.GetValueAt("/numbers/3")).To(BeEquivalentTo(`"three"`)) _, err = db.GetValueAt("/numbers") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/numbers")).To(Equal(map[string]interface{}{ "0": value("zero"), "1": value("one"), "2": value("two"), "3": value("three"), })) }) g.It("should save a tree with a complex array", func() { rev, err := db.SaveTreeAt("", map[string]interface{}{ "letters": []interface{}{ map[string]interface{}{ "name": "á", "variations": []interface{}{"a", "A"}, }, map[string]interface{}{ "name": "bê", "variations": []interface{}{"b", "B"}, }, }, }) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("2-")) Expect(db.GetValueAt("/letters/0/name")).To(BeEquivalentTo(`"á"`)) Expect(db.GetValueAt("/letters/1/variations/1")).To(BeEquivalentTo(`"B"`)) _, err = db.GetValueAt("/letters/0/variations") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/letters")).To(Equal(map[string]interface{}{ "0": map[string]interface{}{ "name": value("á"), "variations": map[string]interface{}{ "0": value("a"), "1": value("A"), }, }, "1": map[string]interface{}{ "name": value("bê"), "variations": map[string]interface{}{ "0": value("b"), "1": value("B"), }, }, })) }) }) }