diff --git a/UnitTests/HttpInterface/api-collection-spec.rb b/UnitTests/HttpInterface/api-collection-mmfiles-spec.rb similarity index 100% rename from UnitTests/HttpInterface/api-collection-spec.rb rename to UnitTests/HttpInterface/api-collection-mmfiles-spec.rb diff --git a/UnitTests/HttpInterface/api-collection-rocksdb-spec.rb b/UnitTests/HttpInterface/api-collection-rocksdb-spec.rb new file mode 100644 index 0000000000..6a433df9ec --- /dev/null +++ b/UnitTests/HttpInterface/api-collection-rocksdb-spec.rb @@ -0,0 +1,937 @@ +# coding: utf-8 + +require 'rspec' +require 'arangodb.rb' + +describe ArangoDB do + api = "/_api/collection" + prefix = "api-collection" + + context "dealing with collections:" do + +################################################################################ +## reading all collections +################################################################################ + + context "all collections:" do + before do + for cn in ["units", "employees", "locations" ] do + ArangoDB.drop_collection(cn) + @cid = ArangoDB.create_collection(cn) + end + end + + after do + for cn in ["units", "employees", "locations" ] do + ArangoDB.drop_collection(cn) + end + end + + it "returns all collections" do + cmd = api + doc = ArangoDB.log_get("#{prefix}-all-collections", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + + collections = doc.parsed_response["result"]; + + total = 0 + realCollections = [ ] + collections.each { |collection| + if [ "units", "employees", "locations" ].include? collection["name"] + realCollections.push(collection) + end + total = total + 1 + } + + realCollections.length.should eq(3) + total.should be > 3 + end + + it "returns all collections, exclude system collections" do + cmd = api + '/?excludeSystem=true' + doc = ArangoDB.log_get("#{prefix}-all-collections-nosystem", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + + collections = doc.parsed_response["result"] + realCollections = [ ] + + total = 0 + collections.each { |collection| + if [ "units", "employees", "locations" ].include? collection["name"] + realCollections.push(collection) + end + total = total + 1 + } + + realCollections.length.should eq(3) + total.should >= 3 + end + + end + +################################################################################ +## error handling +################################################################################ + + context "error handling:" do + it "returns an error if collection identifier is unknown" do + cmd = api + "/123456" + doc = ArangoDB.log_get("#{prefix}-bad-identifier", cmd) + + doc.code.should eq(404) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorNum'].should eq(1203) + doc.parsed_response['code'].should eq(404) + end + + it "creating a collection without name" do + cmd = api + doc = ArangoDB.log_post("#{prefix}-create-missing-name", cmd) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(1208) + end + + it "creating a collection with an illegal name" do + cmd = api + body = "{ \"name\" : \"1\" }" + doc = ArangoDB.log_post("#{prefix}-create-illegal-name", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(1208) + end + + it "creating a collection with a duplicate name" do + cn = "UnitTestsCollectionBasics" + cid = ArangoDB.create_collection(cn) + + cmd = api + body = "{ \"name\" : \"#{cn}\" }" + doc = ArangoDB.log_post("#{prefix}-create-illegal-name", cmd, :body => body) + + doc.code.should eq(409) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(409) + doc.parsed_response['errorNum'].should eq(1207) + end + + it "creating a collection with an illegal body" do + cmd = api + body = "{ name : world }" + doc = ArangoDB.log_post("#{prefix}-create-illegal-body", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(600) + doc.parsed_response['errorMessage'].should eq("SyntaxError: Unexpected token n in JSON at position 2") + end + + it "creating a collection with a null body" do + cmd = api + body = "null" + doc = ArangoDB.log_post("#{prefix}-create-null-body", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + doc.parsed_response['errorNum'].should eq(1208) + end + end + +################################################################################ +## reading a collection +################################################################################ + + context "reading:" do + before do + @cn = "UnitTestsCollectionBasics" + ArangoDB.drop_collection(@cn) + @cid = ArangoDB.create_collection(@cn) + end + + after do + ArangoDB.drop_collection(@cn) + end + + # get + it "finds the collection by identifier" do + cmd = api + "/" + String(@cid) + doc = ArangoDB.log_get("#{prefix}-get-collection-identifier", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + + cmd2 = api + "/" + @cn + "/unload" + doc = ArangoDB.put(cmd2) + + doc = ArangoDB.log_get("#{prefix}-get-collection-identifier", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + [2, 4].should include(doc.parsed_response['status']) + end + + # get + it "finds the collection by name" do + cmd = api + "/" + @cn + doc = ArangoDB.log_get("#{prefix}-get-collection-name", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + + cmd2 = api + "/" + @cn + "/unload" + doc = ArangoDB.put(cmd2) + + doc = ArangoDB.log_get("#{prefix}-get-collection-name", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + [2, 4].should include(doc.parsed_response['status']) + end + + # get count + it "checks the size of a collection" do + cmd = api + "/" + @cn + "/count" + doc = ArangoDB.log_get("#{prefix}-get-collection-count", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_kind_of(Integer) + end + + # get count + it "checks the properties of a collection" do + cmd = api + "/" + @cn + "/properties" + doc = ArangoDB.log_get("#{prefix}-get-collection-properties", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(true) + doc.parsed_response['isSystem'].should eq(false) + end + + describe "figures", :timecritical => true do + # get figures + it "extracting the figures for a collection" do + # flush wal + ArangoDB.put("/_admin/wal/flush?waitForSync=true&waitForCollector=true", { }) + sleep 3 + + cmd = api + "/" + @cn + "/figures" + doc = ArangoDB.log_get("#{prefix}-get-collection-figures", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(0) + doc.parsed_response['figures']['dead']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['dead']['count'].should eq(0) + doc.parsed_response['figures']['alive']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['alive']['count'].should eq(0) + doc.parsed_response['figures']['datafiles']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['datafiles']['fileSize'].should be_kind_of(Integer) + doc.parsed_response['figures']['datafiles']['count'].should eq(0) + doc.parsed_response['figures']['journals']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['journals']['fileSize'].should be_kind_of(Integer) + doc.parsed_response['figures']['journals']['count'].should eq(0) + doc.parsed_response['figures']['compactors']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['compactors']['fileSize'].should be_kind_of(Integer) + doc.parsed_response['figures']['compactors']['count'].should eq(0) + doc.parsed_response['journalSize'].should be_kind_of(Integer) + + # create a few documents, this should increase counts + (0...10).each{|i| + body = "{ \"test\" : " + i.to_s + " }" + doc = ArangoDB.log_post("#{prefix}-get-collection-figures", "/_api/document/?collection=" + @cn, :body => body) + } + + # flush wal + ArangoDB.put("/_admin/wal/flush?waitForSync=true&waitForCollector=true", { }) + sleep 6 + + doc = ArangoDB.log_get("#{prefix}-get-collection-figures", cmd) + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(10) + doc.parsed_response['figures']['dead']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['dead']['count'].should eq(0) + doc.parsed_response['figures']['alive']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['alive']['count'].should eq(10) + doc.parsed_response['figures']['datafiles']['count'].should eq(0) + doc.parsed_response['figures']['journals']['count'].should eq(1) + + # create a few different documents, this should increase counts + (0...10).each{|i| + body = "{ \"test" + i.to_s + "\" : 1 }" + doc = ArangoDB.log_post("#{prefix}-get-collection-figures", "/_api/document/?collection=" + @cn, :body => body) + } + + # flush wal + ArangoDB.put("/_admin/wal/flush?waitForSync=true&waitForCollector=true", { }) + sleep 6 + + doc = ArangoDB.log_get("#{prefix}-get-collection-figures", cmd) + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(20) + doc.parsed_response['figures']['dead']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['dead']['count'].should eq(0) + doc.parsed_response['figures']['alive']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['alive']['count'].should eq(20) + doc.parsed_response['figures']['datafiles']['count'].should eq(0) + doc.parsed_response['figures']['journals']['count'].should eq(1) + + # delete a few documents, this should change counts + body = "{ \"collection\" : \"" + @cn + "\", \"example\": { \"test\" : 5 } }" + doc = ArangoDB.log_put("#{prefix}-get-collection-figures", "/_api/simple/remove-by-example", :body => body) + body = "{ \"collection\" : \"" + @cn + "\", \"example\": { \"test3\" : 1 } }" + doc = ArangoDB.log_put("#{prefix}-get-collection-figures", "/_api/simple/remove-by-example", :body => body) + + # flush wal + ArangoDB.put("/_admin/wal/flush?waitForSync=true&waitForCollector=true", { }) + sleep 3 + + doc = ArangoDB.log_get("#{prefix}-get-collection-figures", cmd) + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(18) + doc.parsed_response['figures']['dead']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['dead']['count'].should eq(2) + doc.parsed_response['figures']['alive']['count'].should be_kind_of(Integer) + doc.parsed_response['figures']['alive']['count'].should eq(18) + doc.parsed_response['figures']['datafiles']['count'].should eq(0) + doc.parsed_response['figures']['journals']['count'].should eq(1) + end + end + + # get revision id + it "extracting the revision id of a collection" do + cmd = api + "/" + @cn + "/revision" + doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + r1 = doc.parsed_response['revision'] + r1.should be_kind_of(String) + r1.should_not eq(""); + + # create a new document + body = "{ \"test\" : 1 }" + doc = ArangoDB.log_post("#{prefix}-get-collection-revision", "/_api/document/?collection=" + @cn, :body => body) + + # fetch revision again + doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['revision'].should be_kind_of(String) + + r2 = doc.parsed_response['revision'] + r2.should_not eq(""); + r2.should_not eq(r1); + + # create another document + doc = ArangoDB.log_post("#{prefix}-get-collection-revision", "/_api/document/?collection=" + @cn, :body => body) + + # fetch revision again + doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['revision'].should be_kind_of(String) + + r3 = doc.parsed_response['revision'] + r3.should_not eq(""); + r3.should_not eq(r1); + r3.should_not eq(r2); + + # truncate + doc = ArangoDB.log_put("#{prefix}-get-collection-revision", "/_api/collection/#{@cn}/truncate", :body => "") + + # fetch revision again + doc = ArangoDB.log_get("#{prefix}-get-collection-revision", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['revision'].should be_kind_of(String) + + r4 = doc.parsed_response['revision'] + r4.should_not eq(""); + r4.should_not eq(r1); + r4.should_not eq(r2); + r4.should_not eq(r3); + end + end + +################################################################################ +## deleting of collection +################################################################################ + + context "deleting:" do + before do + @cn = "UnitTestsCollectionBasics" + end + + it "delete an existing collection by identifier" do + cid = ArangoDB.create_collection(@cn) + cmd = api + "/" + @cn + doc = ArangoDB.log_delete("#{prefix}-delete-collection-identifier", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + + cmd = api + "/" + @cn + doc = ArangoDB.get(cmd) + + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(404) + end + + it "delete an existing collection by name" do + cid = ArangoDB.create_collection(@cn) + cmd = api + "/" + @cn + doc = ArangoDB.log_delete("#{prefix}-delete-collection-name", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + + cmd = api + "/" + @cn + doc = ArangoDB.get(cmd) + + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(404) + end + end + +################################################################################ +## creating a collection +################################################################################ + + context "creating:" do + before do + @cn = "UnitTestsCollectionBasics" + end + + it "create a collection" do + cmd = api + body = "{ \"name\" : \"#{@cn}\" }" + doc = ArangoDB.log_post("#{prefix}-create-collection", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['waitForSync'].should eq(false) + + cmd = api + "/" + @cn + "/figures" + doc = ArangoDB.get(cmd) + + doc.parsed_response['waitForSync'].should eq(false) + + ArangoDB.drop_collection(@cn) + end + + it "create a collection, sync" do + cmd = api + body = "{ \"name\" : \"#{@cn}\", \"waitForSync\" : true }" + doc = ArangoDB.log_post("#{prefix}-create-collection-sync", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['waitForSync'].should eq(true) + + cmd = api + "/" + @cn + "/figures" + doc = ArangoDB.get(cmd) + + doc.parsed_response['waitForSync'].should eq(true) + + ArangoDB.drop_collection(@cn) + end + + it "create a collection, volatile" do + cmd = api + body = "{ \"name\" : \"#{@cn}\", \"isVolatile\" : true }" + doc = ArangoDB.log_post("#{prefix}-create-collection-volatile", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should be_kind_of(String) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['waitForSync'].should eq(false) + doc.parsed_response['isVolatile'].should eq(true) + doc.parsed_response['isSystem'].should eq(false) + + cmd = api + "/" + @cn + "/figures" + doc = ArangoDB.get(cmd) + + doc.parsed_response['waitForSync'].should eq(false) + + ArangoDB.drop_collection(@cn) + end + + it "create a collection, invalid name" do + cmd = api + body = "{ \"name\" : \"_invalid\" }" + doc = ArangoDB.log_post("#{prefix}-create-collection-invalid", cmd, :body => body) + + doc.code.should eq(400) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(400) + end + + it "create a collection, already existing" do + ArangoDB.drop_collection(@cn) + cmd = api + body = "{ \"name\" : \"#{@cn}\" }" + doc = ArangoDB.log_post("#{prefix}-create-collection-existing", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + + body = "{ \"name\" : \"#{@cn}\" }" + doc = ArangoDB.log_post("#{prefix}-create-collection-existing", cmd, :body => body) + + doc.code.should eq(409) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(true) + doc.parsed_response['code'].should eq(409) + + ArangoDB.drop_collection(@cn) + end + end + +################################################################################ +## load a collection +################################################################################ + + context "loading:" do + before do + @cn = "UnitTestsCollectionBasics" + end + + it "load a collection by identifier" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = api + "/" + @cn + "/load" + doc = ArangoDB.log_put("#{prefix}-identifier-load", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(0) + + ArangoDB.drop_collection(@cn) + end + + it "load a collection by name" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = api + "/" + @cn + "/load" + doc = ArangoDB.log_put("#{prefix}-name-load", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(0) + + ArangoDB.drop_collection(@cn) + end + + it "load a collection by name with explicit count" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = "/_api/document?collection=#{@cn}" + body = "{ \"Hallo\" : \"World\" }" + + for i in ( 1 .. 10 ) + doc = ArangoDB.post(cmd, :body => body) + end + + cmd = api + "/" + @cn + "/load" + body = "{ \"count\" : true }" + doc = ArangoDB.log_put("#{prefix}-name-load", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_kind_of(Integer) + doc.parsed_response['count'].should eq(10) + + ArangoDB.drop_collection(@cn) + end + + it "load a collection by name without count" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = "/_api/document?collection=#{@cn}" + body = "{ \"Hallo\" : \"World\" }" + doc = ArangoDB.post(cmd, :body => body) + + cmd = api + "/" + @cn + "/load" + body = "{ \"count\" : false }" + doc = ArangoDB.log_put("#{prefix}-name-load", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['count'].should be_nil + + ArangoDB.drop_collection(@cn) + end + end + +################################################################################ +## unloading a collection +################################################################################ + + context "unloading:" do + before do + @cn = "UnitTestsCollectionBasics" + end + + it "unload a collection by identifier" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = api + "/" + @cn + "/unload" + doc = ArangoDB.log_put("#{prefix}-identifier-unload", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + [2, 4].should include(doc.parsed_response['status']) + + ArangoDB.drop_collection(@cn) + end + + it "unload a collection by name" do + ArangoDB.drop_collection(@cn) + cid = ArangoDB.create_collection(@cn) + + cmd = api + "/" + @cn + "/unload" + doc = ArangoDB.log_put("#{prefix}-name-unload", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(@cn) + [2, 4].should include(doc.parsed_response['status']) + + ArangoDB.drop_collection(@cn) + end + end + +################################################################################ +## truncate a collection +################################################################################ + + context "truncating:" do + before do + @cn = "UnitTestsCollectionBasics" + @cid = ArangoDB.create_collection(@cn) + end + + after do + ArangoDB.drop_collection(@cn) + end + + it "truncate a collection by identifier" do + cmd = "/_api/document?collection=#{@cid}" + body = "{ \"Hallo\" : \"World\" }" + + for i in ( 1 .. 10 ) + doc = ArangoDB.post(cmd, :body => body) + end + + ArangoDB.size_collection(@cid).to_i.should eq(10) + + cmd = api + "/" + @cn + "/truncate" + doc = ArangoDB.log_put("#{prefix}-identifier-truncate", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(@cid) + doc.parsed_response['name'].should eq(@cn) + doc.parsed_response['status'].should eq(3) + + ArangoDB.size_collection(@cid).should eq(0) + + ArangoDB.drop_collection(@cn) + end + end + +################################################################################ +## properties of a collection +################################################################################ + + context "properties:" do + it "changing the properties of a collection by identifier" do + cn = "UnitTestsCollectionBasics" + ArangoDB.drop_collection(cn) + cid = ArangoDB.create_collection(cn) + + cmd = "/_api/document?collection=#{cid}" + body = "{ \"Hallo\" : \"World\" }" + + for i in ( 1 .. 10 ) + doc = ArangoDB.post(cmd, :body => body) + end + + ArangoDB.size_collection(cid).should eq(10) + ArangoDB.size_collection(cn).should eq(10) + + cmd = api + "/" + cn + "/properties" + body = "{ \"waitForSync\" : true }" + doc = ArangoDB.log_put("#{prefix}-identifier-properties-sync", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(true) + doc.parsed_response['isSystem'].should eq(false) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + cmd = api + "/" + cn + "/properties" + body = "{ \"waitForSync\" : false }" + doc = ArangoDB.log_put("#{prefix}-identifier-properties-no-sync", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(false) + doc.parsed_response['isSystem'].should eq(false) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + body = "{ \"doCompact\" : false }" + doc = ArangoDB.log_put("#{prefix}-identifier-properties-no-compact", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(false) + doc.parsed_response['isSystem'].should eq(false) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + ArangoDB.drop_collection(cn) + end + + it "create collection with explicit keyOptions property, traditional keygen" do + cn = "UnitTestsCollectionBasics" + + cmd = "/_api/collection" + body = "{ \"name\" : \"#{cn}\", \"waitForSync\" : false, \"type\" : 2, \"keyOptions\" : {\"type\": \"traditional\", \"allowUserKeys\": true } }" + doc = ArangoDB.log_post("#{prefix}-with-create-options", cmd, :body => body) + + doc.code.should eq(200) + cid = doc.parsed_response['id'] + + cmd = api + "/" + cn + "/properties" + body = "{ \"waitForSync\" : true }" + doc = ArangoDB.log_put("#{prefix}-with-create-options", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(true) + doc.parsed_response['isVolatile'].should eq(false) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + ArangoDB.drop_collection(cn) + end + + it "create a collection with isVolatile property" do + cn = "UnitTestsCollectionBasics" + ArangoDB.drop_collection(cn) + + cmd = "/_api/collection" + body = "{ \"name\" : \"#{cn}\", \"isVolatile\" : true }" + doc = ArangoDB.log_post("#{prefix}-with-volatile", cmd, :body => body) + + doc.code.should eq(200) + cid = doc.parsed_response['id'] + + cmd = api + "/" + cn + "/properties" + doc = ArangoDB.log_get("#{prefix}-with-volatile", cmd) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['waitForSync'].should eq(false) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + ArangoDB.drop_collection(cn) + end + + it "create collection with empty keyOptions property" do + cn = "UnitTestsCollectionBasics" + ArangoDB.drop_collection(cn) + + cmd = "/_api/collection" + body = "{ \"name\" : \"#{cn}\", \"waitForSync\" : false, \"type\" : 2 }" + doc = ArangoDB.log_post("#{prefix}-with-empty-create-options", cmd, :body => body) + + doc.code.should eq(200) + cid = doc.parsed_response['id'] + + cmd = api + "/" + cn + "/properties" + body = "{ \"waitForSync\" : true }" + doc = ArangoDB.log_put("#{prefix}-with-empty-create-options", cmd, :body => body) + + doc.code.should eq(200) + doc.headers['content-type'].should eq("application/json; charset=utf-8") + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['id'].should eq(cid) + doc.parsed_response['name'].should eq(cn) + doc.parsed_response['status'].should eq(3) + doc.parsed_response['waitForSync'].should eq(true) + doc.parsed_response['keyOptions']['type'].should eq("traditional") + doc.parsed_response['keyOptions']['allowUserKeys'].should eq(true) + + ArangoDB.drop_collection(cn) + end + + end + + end +end diff --git a/arangod/RocksDBEngine/RocksDBCollection.cpp b/arangod/RocksDBEngine/RocksDBCollection.cpp index 1653f72efa..bce22e3049 100644 --- a/arangod/RocksDBEngine/RocksDBCollection.cpp +++ b/arangod/RocksDBEngine/RocksDBCollection.cpp @@ -553,8 +553,9 @@ int RocksDBCollection::update(arangodb::transaction::Methods* trx, return res; } - uint8_t const* vpack = previous.vpack(); - VPackSlice oldDoc(vpack); + TRI_ASSERT(!previous.empty()); + + VPackSlice oldDoc(previous.vpack()); TRI_voc_rid_t oldRevisionId = transaction::helpers::extractRevFromDocument(oldDoc); prevRev = oldRevisionId; @@ -574,6 +575,8 @@ int RocksDBCollection::update(arangodb::transaction::Methods* trx, if (newSlice.length() <= 1) { // shortcut. no need to do anything previous.clone(mdr); + + TRI_ASSERT(!mdr.empty()); if (_logicalCollection->waitForSync()) { trx->state()->waitForSync(true); @@ -611,6 +614,8 @@ int RocksDBCollection::update(arangodb::transaction::Methods* trx, if (!result.ok()) { return result.errorNumber(); } + + TRI_ASSERT(!mdr.empty()); static_cast(trx->state())->addOperation(_logicalCollection->cid(), TRI_VOC_DOCUMENT_OPERATION_UPDATE, newDoc.byteSize()); guard.commit(); @@ -643,9 +648,10 @@ int RocksDBCollection::replace( if (res != TRI_ERROR_NO_ERROR) { return res; } + + TRI_ASSERT(!previous.empty()); - uint8_t const* vpack = previous.vpack(); - VPackSlice oldDoc(vpack); + VPackSlice oldDoc(previous.vpack()); TRI_voc_rid_t oldRevisionId = transaction::helpers::extractRevFromDocument(oldDoc); prevRev = oldRevisionId; @@ -689,6 +695,8 @@ int RocksDBCollection::replace( if (!result.ok()) { return result.errorNumber(); } + + TRI_ASSERT(!mdr.empty()); static_cast(trx->state())->addOperation(_logicalCollection->cid(), TRI_VOC_DOCUMENT_OPERATION_REPLACE, VPackSlice(builder->slice()).byteSize()); guard.commit(); @@ -726,9 +734,10 @@ int RocksDBCollection::remove(arangodb::transaction::Methods* trx, if (res != TRI_ERROR_NO_ERROR) { return res; } + + TRI_ASSERT(!previous.empty()); - uint8_t const* vpack = previous.vpack(); - VPackSlice oldDoc(vpack); + VPackSlice oldDoc(previous.vpack()); TRI_voc_rid_t oldRevisionId = arangodb::transaction::helpers::extractRevFromDocument(oldDoc); prevRev = oldRevisionId; @@ -1058,6 +1067,7 @@ arangodb::Result RocksDBCollection::lookupRevisionVPack( << "', OBJECTID: " << _objectId << ", REVISIONID: " << revisionId << " -> NOT FOUND"; */ + mdr.reset(); } return result; } diff --git a/arangod/RocksDBEngine/RocksDBEngine.cpp b/arangod/RocksDBEngine/RocksDBEngine.cpp index f28d3722fa..dba335add8 100644 --- a/arangod/RocksDBEngine/RocksDBEngine.cpp +++ b/arangod/RocksDBEngine/RocksDBEngine.cpp @@ -92,7 +92,15 @@ void RocksDBEngine::validateOptions(std::shared_ptr) {} // preparation phase for storage engine. can be used for internal setup. // the storage engine must not start any threads here or write any files -void RocksDBEngine::prepare() {} +void RocksDBEngine::prepare() { + // get base path from DatabaseServerFeature + auto databasePathFeature = + application_features::ApplicationServer::getFeature( + "DatabasePath"); + _basePath = databasePathFeature->directory(); + + TRI_ASSERT(!_basePath.empty()); +} void RocksDBEngine::start() { // it is already decided that rocksdb is used @@ -342,7 +350,7 @@ int RocksDBEngine::getViews(TRI_vocbase_t* vocbase, } std::string RocksDBEngine::databasePath(TRI_vocbase_t const* vocbase) const { - return std::string(); // no path to be returned here! + return _basePath; } std::string RocksDBEngine::collectionPath(TRI_vocbase_t const* vocbase, @@ -733,9 +741,7 @@ Result RocksDBEngine::dropDatabase(TRI_voc_tick_t id) { // delete index documents uint64_t objectId = basics::VelocyPackHelper::stringUInt64(val.second.slice(), "objectId"); - RocksDBKeyBounds bounds = RocksDBKeyBounds::IndexRange( - objectId, VPackSlice::minKeySlice(), VPackSlice::maxKeySlice() - ); + RocksDBKeyBounds bounds = RocksDBKeyBounds::IndexEntries(objectId); res = rocksutils::removeLargeRange(_db, bounds); if(res.fail()){ return res; diff --git a/arangod/RocksDBEngine/RocksDBEngine.h b/arangod/RocksDBEngine/RocksDBEngine.h index d03511cdb9..7beafe7e8c 100644 --- a/arangod/RocksDBEngine/RocksDBEngine.h +++ b/arangod/RocksDBEngine/RocksDBEngine.h @@ -260,6 +260,7 @@ class RocksDBEngine final : public StorageEngine { rocksdb::Options _options; std::unique_ptr _cmp; std::string _path; + std::string _basePath; std::unique_ptr _counterManager; }; diff --git a/arangod/RocksDBEngine/RocksDBKeyBounds.cpp b/arangod/RocksDBEngine/RocksDBKeyBounds.cpp index a8b88b1a6a..474f7a1d98 100644 --- a/arangod/RocksDBEngine/RocksDBKeyBounds.cpp +++ b/arangod/RocksDBEngine/RocksDBKeyBounds.cpp @@ -70,7 +70,7 @@ RocksDBKeyBounds RocksDBKeyBounds::EdgeIndexVertex( return RocksDBKeyBounds(RocksDBEntryType::EdgeIndexValue, indexId, vertexId); } -RocksDBKeyBounds RocksDBKeyBounds::Index(uint64_t indexId) { +RocksDBKeyBounds RocksDBKeyBounds::IndexEntries(uint64_t indexId) { return RocksDBKeyBounds(RocksDBEntryType::IndexValue, indexId); } diff --git a/arangod/RocksDBEngine/RocksDBKeyBounds.h b/arangod/RocksDBEngine/RocksDBKeyBounds.h index 7c95c8129f..6bfb33b8ee 100644 --- a/arangod/RocksDBEngine/RocksDBKeyBounds.h +++ b/arangod/RocksDBEngine/RocksDBKeyBounds.h @@ -86,7 +86,7 @@ class RocksDBKeyBounds { ////////////////////////////////////////////////////////////////////////////// /// @brief Bounds for all index-entries belonging to a specified non-unique index ////////////////////////////////////////////////////////////////////////////// - static RocksDBKeyBounds Index(uint64_t indexId); + static RocksDBKeyBounds IndexEntries(uint64_t indexId); ////////////////////////////////////////////////////////////////////////////// /// @brief Bounds for all entries belonging to a specified unique index diff --git a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp index 1061497c26..c0a745d386 100644 --- a/arangod/RocksDBEngine/RocksDBVPackIndex.cpp +++ b/arangod/RocksDBEngine/RocksDBVPackIndex.cpp @@ -584,7 +584,7 @@ int RocksDBVPackIndex::drop() { rocksutils::globalRocksDB(), RocksDBKeyBounds::UniqueIndex(_objectId)).errorNumber(); } else { return rocksutils::removeLargeRange(rocksutils::globalRocksDB(), - RocksDBKeyBounds::Index(_objectId)).errorNumber(); + RocksDBKeyBounds::IndexEntries(_objectId)).errorNumber(); } } diff --git a/arangod/Transaction/Methods.cpp b/arangod/Transaction/Methods.cpp index b451c2cf8a..e25d769ec4 100644 --- a/arangod/Transaction/Methods.cpp +++ b/arangod/Transaction/Methods.cpp @@ -826,15 +826,11 @@ OperationResult transaction::Methods::anyLocal(std::string const& collectionName limit, 1000, false); LogicalCollection* collection = cursor->collection(); - auto cb = [&] (DocumentIdentifierToken const& token) { + cursor->getAll([&] (DocumentIdentifierToken const& token) { if (collection->readDocument(this, token, mmdr)) { - uint8_t const* vpack = mmdr.vpack(); - resultBuilder.add(VPackSlice(vpack)); + mmdr.addToBuilder(resultBuilder, false); } - }; - - while (cursor->getMore(cb, 1000)) { - } + }); resultBuilder.close(); @@ -1001,8 +997,6 @@ Result transaction::Methods::documentFastPath(std::string const& collectionName, TRI_ASSERT(isPinned(cid)); - uint8_t const* vpack = mmdr->vpack(); - TRI_ASSERT(vpack != nullptr); mmdr->addToBuilder(result, true); return TRI_ERROR_NO_ERROR; } @@ -1214,10 +1208,8 @@ OperationResult transaction::Methods::documentLocal(std::string const& collectio TRI_ASSERT(isPinned(cid)); - uint8_t const* vpack = result.vpack(); - if (expectedRevision != 0) { - TRI_voc_rid_t foundRevision = transaction::helpers::extractRevFromDocument(VPackSlice(vpack)); + TRI_voc_rid_t foundRevision = transaction::helpers::extractRevFromDocument(VPackSlice(result.vpack())); if (expectedRevision != foundRevision) { if (!isMultiple) { // still return @@ -1359,6 +1351,8 @@ OperationResult transaction::Methods::insertLocal(std::string const& collectionN // in the single document case no body needs to be created at all. return res; } + + TRI_ASSERT(!result.empty()); StringRef keyString(transaction::helpers::extractKeyFromDocument(VPackSlice(result.vpack()))); @@ -1650,6 +1644,9 @@ OperationResult transaction::Methods::modifyLocal( !isLocked(collection, AccessMode::Type::WRITE), actualRevision, previous); } + + TRI_ASSERT(!result.empty()); + TRI_ASSERT(!previous.empty()); if (resultMarkerTick > 0 && resultMarkerTick > maxTick) { maxTick = resultMarkerTick; @@ -1907,6 +1904,8 @@ OperationResult transaction::Methods::removeLocal(std::string const& collectionN if (resultMarkerTick > 0 && resultMarkerTick > maxTick) { maxTick = resultMarkerTick; } + + TRI_ASSERT(!previous.empty()); if (res != TRI_ERROR_NO_ERROR) { if (res == TRI_ERROR_ARANGO_CONFLICT && diff --git a/arangod/VocBase/ManagedDocumentResult.h b/arangod/VocBase/ManagedDocumentResult.h index a407f9c204..2954d5d3ce 100644 --- a/arangod/VocBase/ManagedDocumentResult.h +++ b/arangod/VocBase/ManagedDocumentResult.h @@ -71,6 +71,7 @@ class ManagedDocumentResult { cloned._useString = true; cloned._string = _string; cloned._lastRevisionId = _lastRevisionId; + cloned._vpack = reinterpret_cast(const_cast(cloned._string.data())); } else if (_managed) { cloned.setManaged(_vpack, _lastRevisionId); } else { diff --git a/js/server/upgrade-database.js b/js/server/upgrade-database.js index c16f142199..81311c8311 100644 --- a/js/server/upgrade-database.js +++ b/js/server/upgrade-database.js @@ -94,10 +94,7 @@ constant2name[DATABASE_EXISTING] = 'existing'; // path to version file - let versionFile; - if (internal.db._path()) { - versionFile = internal.db._path() + '/VERSION'; - } + let versionFile = internal.db._path() + '/VERSION'; // all defined tasks const allTasks = []; @@ -287,7 +284,7 @@ lastTasks[task.name] = true; // save/update version info - if (isLocal && versionFile) { + if (isLocal) { fs.write( versionFile, JSON.stringify({ @@ -303,7 +300,7 @@ } // save file so version gets saved even if there are no tasks - if (isLocal && versionFile) { + if (isLocal) { fs.write( versionFile, JSON.stringify({ @@ -350,7 +347,7 @@ } // VERSION file exists, read its contents - if (versionFile && fs.exists(versionFile)) { + if (fs.exists(versionFile)) { var versionInfo = fs.read(versionFile); if (versionInfo === '') { @@ -490,7 +487,7 @@ return false; } - if (db._engine().name != "rocksdb") { + if (db._engine().name !== "rocksdb") { users.ensureIndex({ type: 'hash', fields: ['user'], diff --git a/tests/Basics/RocksDBKeyTest.cpp b/tests/Basics/RocksDBKeyTest.cpp index 2c9fe61a3c..db19f23fde 100644 --- a/tests/Basics/RocksDBKeyTest.cpp +++ b/tests/Basics/RocksDBKeyTest.cpp @@ -79,6 +79,57 @@ SECTION("test_database") { CHECK(s6.size() == sizeof(char) + sizeof(uint64_t)); CHECK(s6 == std::string("0\x35\x1c\xdc\xdf\x02\0\0\0", 9)); + + RocksDBKey key7 = RocksDBKey::Database(0xf0f1f2f3f4f5f6f7ULL); + auto const& s7 = key7.string(); + + CHECK(s7.size() == sizeof(char) + sizeof(uint64_t)); + CHECK(s7 == std::string("0\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0", 9)); +} + +/// @brief test collection +SECTION("test_collection") { + RocksDBKey key1 = RocksDBKey::Collection(0, 0); + auto const& s1 = key1.string(); + + CHECK(s1.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s1 == std::string("1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 17)); + + RocksDBKey key2 = RocksDBKey::Collection(23, 42); + auto const& s2 = key2.string(); + + CHECK(s2.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s2 == std::string("1\x17\0\0\0\0\0\0\0\x2a\0\0\0\0\0\0\0", 17)); + + RocksDBKey key3 = RocksDBKey::Collection(255, 255); + auto const& s3 = key3.string(); + + CHECK(s3.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s3 == std::string("1\xff\0\0\0\0\0\0\0\xff\0\0\0\0\0\0\0", 17)); + + RocksDBKey key4 = RocksDBKey::Collection(256, 257); + auto const& s4 = key4.string(); + + CHECK(s4.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s4 == std::string("1\0\x01\0\0\0\0\0\0\x01\x01\0\0\0\0\0\0", 17)); + + RocksDBKey key5 = RocksDBKey::Collection(49152, 16384); + auto const& s5 = key5.string(); + + CHECK(s5.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s5 == std::string("1\0\xc0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0", 17)); + + RocksDBKey key6 = RocksDBKey::Collection(12345678901, 987654321); + auto const& s6 = key6.string(); + + CHECK(s6.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)) ; + CHECK(s6 == std::string("1\x35\x1c\xdc\xdf\x02\0\0\0\xb1\x68\xde\x3a\0\0\0\0", 17)); + + RocksDBKey key7 = RocksDBKey::Collection(0xf0f1f2f3f4f5f6f7ULL, 0xf0f1f2f3f4f5f6f7ULL); + auto const& s7 = key7.string(); + + CHECK(s7.size() == sizeof(char) + sizeof(uint64_t) + sizeof(uint64_t)); + CHECK(s7 == std::string("1\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0", 17)); } }