From 0f90bfc8b1e38d86f74c38c070cac6a01d07aa69 Mon Sep 17 00:00:00 2001 From: Sebastian Friston Date: Wed, 16 Oct 2024 18:31:57 +0100 Subject: [PATCH] ISSUE #703 updated RepoBSON unit tests --- .../handler/repo_database_handler_mongo.cpp | 28 +- .../src/repo/core/model/bson/repo_bson.cpp | 272 ++-- bouncer/src/repo/core/model/bson/repo_bson.h | 263 +--- .../core/model/bson/repo_bson_builder.cpp | 16 +- .../repo/core/model/bson/repo_bson_builder.h | 11 +- .../repo/core/model/bson/ut_repo_bson.cpp | 1099 +++++++++++++---- .../core/model/bson/ut_repo_bson_builder.cpp | 33 - 7 files changed, 1056 insertions(+), 666 deletions(-) diff --git a/bouncer/src/repo/core/handler/repo_database_handler_mongo.cpp b/bouncer/src/repo/core/handler/repo_database_handler_mongo.cpp index 31b5204d2..b1a5d5f8a 100644 --- a/bouncer/src/repo/core/handler/repo_database_handler_mongo.cpp +++ b/bouncer/src/repo/core/handler/repo_database_handler_mongo.cpp @@ -26,6 +26,7 @@ #include "fileservice/repo_file_manager.h" #include "fileservice/repo_blob_files_handler.h" #include "repo/core/model/bson/repo_bson_builder.h" +#include "repo/core/model/bson/repo_bson_element.h" #include "repo/lib/repo_log.h" using namespace repo::core::handler; @@ -209,25 +210,28 @@ repo::core::model::RepoBSON createRepoBSON( if (!ignoreExtFile) { if (orgBson.hasFileReference()) { auto ref = orgBson.getBinaryReference(); - auto buffer = blobHandler.readToBuffer(fileservice::DataRef::deserialise(ref)); orgBson.initBinaryBuffer(buffer); } - else if (orgBson.hasLegacyFileReference()) { - std::unordered_map< std::string, std::pair> > binMap; - std::vector> extFileList = orgBson.getFileList(); - for (const auto &pair : extFileList) - { - auto fileManager = fileservice::FileManager::getManager(); - - auto file = fileManager->getFile(database, collection, pair.second); + else if (orgBson.hasField(REPO_LABEL_OVERSIZED_FILES)) { + // The _extRef support has been deprecated, and collections should have been + // updated by the 5.4 migration scripts. + // In case we have any old documents remaining, this snippet can read the data, + // though the filenames will not be accessible. - binMap[pair.first] = std::pair>(pair.second, file); + auto extRefbson = orgBson.getObjectField(REPO_LABEL_OVERSIZED_FILES); + std::set fieldNames = extRefbson.getFieldNames(); + repo::core::model::RepoBSON::BinMapping map; + auto fileManager = fileservice::FileManager::getManager(); + for (const auto& name : fieldNames) + { + auto fname = extRefbson.getStringField(name); + auto file = fileManager->getFile(database, collection, fname); + map[name] = file; } - return repo::core::model::RepoBSON(obj, binMap); + return repo::core::model::RepoBSON(obj, map); } } - return orgBson; } diff --git a/bouncer/src/repo/core/model/bson/repo_bson.cpp b/bouncer/src/repo/core/model/bson/repo_bson.cpp index 9513e60c8..66f6b4f2a 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson.cpp +++ b/bouncer/src/repo/core/model/bson/repo_bson.cpp @@ -18,13 +18,14 @@ #include "repo_bson.h" #include +#include -#include "../../../lib/repo_exception.h" +#include "repo/lib/repo_exception.h" using namespace repo::core::model; RepoBSON::RepoBSON(const RepoBSON &obj, - const std::unordered_map>> &binMapping) : + const BinMapping &binMapping) : mongo::BSONObj(obj), bigFiles(binMapping) { auto existingFiles = obj.getFilesMapping(); @@ -38,54 +39,150 @@ RepoBSON::RepoBSON(const RepoBSON &obj, RepoBSON::RepoBSON( const mongo::BSONObj &obj, - const std::unordered_map>> &binMapping) + const BinMapping &binMapping) : mongo::BSONObj(obj), bigFiles(binMapping) { } -int64_t RepoBSON::getCurrentTimestamp() +RepoBSON::RepoBSON() + : mongo::BSONObj() { - mongo::Date_t date = mongo::Date_t(time(NULL) * 1000); - return date.asInt64(); } -RepoBSON RepoBSON::fromJSON(const std::string &json) +RepoBSON::RepoBSON(mongo::BSONObjBuilder& builder) + : mongo::BSONObj(builder.obj()) { - return RepoBSON(mongo::fromjson(json)); } -RepoBSON RepoBSON::cloneAndAddFields( - const RepoBSON *changes) const +RepoBSON::RepoBSON(const std::vector& rawData) + : mongo::BSONObj(rawData.data()) { - mongo::BSONObjBuilder builder; +} - builder.appendElementsUnique(*changes); +bool RepoBSON::couldBeArray() const { + return mongo::BSONObj::couldBeArray(); +} - builder.appendElementsUnique(*this); +bool RepoBSON::hasField(const std::string& label) const { + return mongo::BSONObj::hasField(label); +} - return RepoBSON(builder.obj()); +int RepoBSON::nFields() const { + return mongo::BSONObj::nFields(); } -RepoBSON RepoBSON::cloneAndShrink() const +RepoBSONElement RepoBSON::getField(const std::string& label) const { - std::set fields = getFieldNames(); - std::unordered_map< std::string, std::pair>> rawFiles(bigFiles.begin(), bigFiles.end()); - std::string uniqueIDStr = hasField(REPO_LABEL_ID) ? getUUIDField(REPO_LABEL_ID).toString() : repo::lib::RepoUUID::createUUID().toString(); + return RepoBSONElement(mongo::BSONObj::getField(label)); +} - RepoBSON resultBson = *this; +bool RepoBSON::getBoolField(const std::string& label) const +{ + return mongo::BSONObj::getBoolField(label); +} - for (const std::string &field : fields) +std::string RepoBSON::getStringField(const std::string& label) const { + if (hasField(label)) { - if (getField(field).type() == ElementType::BINARY) + auto f = getField(label); + if (f.type() == ElementType::STRING) + { + return f.String(); + } + else { - std::string fileName = uniqueIDStr + "_" + field; - rawFiles[field] = std::pair>(fileName, std::vector()); - getBinaryFieldAsVector(field, rawFiles[field].second); - resultBson = resultBson.removeField(field); + throw repo::lib::RepoFieldTypeException(label); } } - return RepoBSON(resultBson, rawFiles); + throw repo::lib::RepoFieldNotFoundException(label); +} + +RepoBSON RepoBSON::getObjectField(const std::string& label) const { + if (hasField(label)) + { + return mongo::BSONObj::getObjectField(label); + } + else + { + throw repo::lib::RepoFieldNotFoundException(label); + } +} + +std::vector RepoBSON::getBounds3D(const std::string& label) { + auto field = getObjectField(label); + return std::vector< lib::RepoVector3D>({ + lib::RepoVector3D(field.getFloatVectorField("0")), + lib::RepoVector3D(field.getFloatVectorField("1")), + }); +} + +bool RepoBSON::isEmpty() const { + return mongo::BSONObj::isEmpty(); +} + +int RepoBSON::getIntField(const std::string& label) const { + return mongo::BSONObj::getIntField(label); +} + +std::set RepoBSON::getFieldNames() const { + std::set fieldNames; + mongo::BSONObj::getFieldNames(fieldNames); + return fieldNames; +} + + +bool RepoBSON::isValid() const { + return mongo::BSONObj::isValid(); +} + +uint64_t RepoBSON::objsize() const { + return mongo::BSONObj::objsize(); +} + +RepoBSON RepoBSON::removeField(const std::string& label) const { + return mongo::BSONObj::removeField(label); +} + +std::string RepoBSON::toString() const { + return mongo::BSONObj::toString(); +} + +bool RepoBSON::operator==(const RepoBSON other) const { + return mongo::BSONObj::operator==((mongo::BSONObj)other); +} + +bool RepoBSON::operator!=(const RepoBSON other) const { + return mongo::BSONObj::operator!=((mongo::BSONObj)other); +} + + +RepoBSON& RepoBSON::operator=(RepoBSON otherCopy) { + swap(otherCopy); + return *this; +} + +void RepoBSON::swap(RepoBSON otherCopy) +{ + mongo::BSONObj::swap(otherCopy); + bigFiles = otherCopy.bigFiles; +} + +std::vector RepoBSON::getFileList(const std::string& label) const +{ + std::vector fileList; + RepoBSON arraybson = getObjectField(label); + std::set fields = arraybson.getFieldNames(); + for (const auto& field : fields) + { + fileList.push_back(arraybson.getStringField(field)); + } + return fileList; +} + +RepoBSON RepoBSON::fromJSON(const std::string &json) +{ + return RepoBSON(mongo::fromjson(json)); } std::pair> RepoBSON::getBinariesAsBuffer() const { @@ -98,8 +195,8 @@ std::pair> RepoBSON::getBinari mongo::BSONObjBuilder entryBuilder; entryBuilder << REPO_LABEL_BINARY_START << (unsigned int)buffer.size(); - buffer.insert(buffer.end(), entry.second.second.begin(), entry.second.second.end()); - entryBuilder << REPO_LABEL_BINARY_SIZE << (unsigned int)entry.second.second.size(); + buffer.insert(buffer.end(), entry.second.begin(), entry.second.end()); + entryBuilder << REPO_LABEL_BINARY_SIZE << (unsigned int)entry.second.size(); elemsBuilder << entry.first << entryBuilder.obj(); } @@ -155,6 +252,7 @@ std::vector RepoBSON::getFloatVectorField(const std::string& label) const if (!array.isEmpty()) { std::set fields = array.getFieldNames(); + results.resize(fields.size()); std::set::iterator it; for (it = fields.begin(); it != fields.end(); ++it) results.push_back(array.getDoubleField(*it)); @@ -244,45 +342,39 @@ std::vector RepoBSON::getUUIDFieldArray(const std::string & return results; } -std::vector RepoBSON::getBigBinary( - const std::string &key) const +const std::vector& RepoBSON::getBinary(const std::string& field) const { - std::vector binary; - const auto &it = bigFiles.find(key); - - if (it != bigFiles.end()) + if (!hasField(field) || getField(field).type() == ElementType::STRING) { - binary = it->second.second; - } - else - { - repoError << "External binary not found for key " << key << "! (size of mapping is : " << bigFiles.size() << ")"; - } - - return binary; -} - -std::vector> RepoBSON::getFileList() const -{ - std::vector> fileList; - - if (bigFiles.size()) { - for (const auto &entry : bigFiles) { - fileList.push_back({ entry.first, entry.second.first }); + const auto& it = bigFiles.find(field); + if (it != bigFiles.end()) + { + return it->second; } } - else if (hasField(REPO_LABEL_OVERSIZED_FILES)) - { - RepoBSON extRefbson = getObjectField(REPO_LABEL_OVERSIZED_FILES); - - std::set fieldNames = extRefbson.getFieldNames(); - for (const auto &name : fieldNames) + else { + RepoBSONElement bse = getField(field); + if (bse.type() == ElementType::BINARY && bse.binDataType() == mongo::BinDataGeneral) + { + bse.value(); + int length; + auto binData = (const uint8_t*)bse.binData(length); + if (length > 0) + { + return std::vector(binData, binData + length); + } + else + { + throw repo::lib::RepoFieldTypeException(field + "; has a length of zero bytes"); + } + } + else { - fileList.push_back(std::pair(name, extRefbson.getStringField(name))); + throw repo::lib::RepoFieldTypeException(field); } } - return fileList; + throw repo::lib::RepoFieldNotFoundException(field); } repo::core::model::RepoBSON RepoBSON::getBinaryReference() const { @@ -308,7 +400,7 @@ void RepoBSON::initBinaryBuffer(const std::vector &buffer) { size_t start = elemRefBson.getIntField(REPO_LABEL_BINARY_START); size_t size = elemRefBson.getIntField(REPO_LABEL_BINARY_SIZE); - bigFiles[elem] = { std::string(), std::vector(buffer.begin() + start, buffer.begin() + start + size) }; + bigFiles[elem] = std::vector(buffer.begin() + start, buffer.begin() + start + size); } } } @@ -318,9 +410,6 @@ bool RepoBSON::hasBinField(const std::string &label) const return hasField(label) || bigFiles.find(label) != bigFiles.end(); } -bool RepoBSON::hasLegacyFileReference() const { - return hasField(REPO_LABEL_OVERSIZED_FILES); -} bool RepoBSON::hasFileReference() const { return hasField(REPO_LABEL_BINARY_REFERENCE); } @@ -379,51 +468,6 @@ time_t RepoBSON::getTimeStampField(const std::string &label) const return time; } -std::list > RepoBSON::getListStringPairField( - const std::string &arrLabel, - const std::string &fstLabel, - const std::string &sndLabel) const -{ - std::list > list; - mongo::BSONElement arrayElement; - if (hasField(arrLabel)) - { - arrayElement = mongo::BSONObj::getField(arrLabel); - } - - if (!arrayElement.eoo() && arrayElement.type() == mongo::BSONType::Array) - { - std::vector array = arrayElement.Array(); - for (const auto &element : array) - { - if (element.type() == mongo::BSONType::Object) - { - mongo::BSONObj obj = element.embeddedObject(); - if (obj.hasField(fstLabel) && obj.hasField(sndLabel)) - { - std::string field1 = obj.getField(fstLabel).String(); - std::string field2 = obj.getField(sndLabel).String(); - list.push_back(std::make_pair(field1, field2)); - } - } - } - } - return list; -} - -double RepoBSON::getEmbeddedDouble( - const std::string &embeddedObjName, - const std::string &fieldName, - const double &defaultValue) const -{ - double value = defaultValue; - if (hasEmbeddedField(embeddedObjName, fieldName)) - { - value = (getObjectField(embeddedObjName)).getDoubleField(fieldName); - } - return value; -} - double RepoBSON::getDoubleField(const std::string &label) const { double ret = 0.0; if (mongo::BSONObj::hasField(label)) { @@ -439,16 +483,4 @@ long long RepoBSON::getLongField(const std::string& label) const { auto field = mongo::BSONObj::getField(label); return field.Long(); -} - -bool RepoBSON::hasEmbeddedField( - const std::string &embeddedObjName, - const std::string &fieldName) const -{ - bool found = false; - if (hasField(embeddedObjName)) - { - found = (getObjectField(embeddedObjName)).hasField(fieldName); - } - return found; } \ No newline at end of file diff --git a/bouncer/src/repo/core/model/bson/repo_bson.h b/bouncer/src/repo/core/model/bson/repo_bson.h index fc06ef170..11d99b0b2 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson.h +++ b/bouncer/src/repo/core/model/bson/repo_bson.h @@ -33,18 +33,18 @@ #define strcasecmp _stricmp #endif -#include #include -#include "../../../lib/repo_log.h" -#include "../../../repo_bouncer_global.h" -#include "../repo_model_global.h" -#include "../../../lib/datastructure/repo_uuid.h" -#include "repo_bson_element.h" +#include "repo/repo_bouncer_global.h" +#include "repo/core/model/repo_model_global.h" +#include "repo/lib/repo_log.h" +#include "repo/lib/datastructure/repo_uuid.h" +#include "repo/lib/datastructure/repo_vector.h" +#include "repo/lib/datastructure/repo_matrix.h" +#include "repo/lib/repo_exception.h" +#include "repo/core/model/bson/repo_bson_element.h" -#include "../../../lib/datastructure/repo_vector.h" -#include "../../../lib/datastructure/repo_matrix.h" -#include "../../../lib/repo_exception.h" +#include #define REPO_BSON_MAX_BYTE_SIZE 16770000 //max size is 16MB,but leave a bit for buffer @@ -62,40 +62,41 @@ namespace repo { { friend class RepoBSONBuilder; friend class repo::core::handler::MongoDatabaseHandler; + public: + using BinMapping = std::unordered_map>; + /** * Default empty constructor. */ - RepoBSON() : mongo::BSONObj() {} + RepoBSON(); /** * Constructor from Mongo BSON object. * @param mongo BSON object */ RepoBSON(const RepoBSON &obj, - const std::unordered_map>> &binMapping = - std::unordered_map>>()); + const BinMapping& binMapping = {}); /** * Constructor from Mongo BSON object. * @param mongo BSON object */ RepoBSON(const mongo::BSONObj &obj, - const std::unordered_map>> &binMapping = - std::unordered_map>>()); + const BinMapping& binMapping = {}); /** * Constructor from Mongo BSON object builder. * @param mongo BSON object builder */ - RepoBSON(mongo::BSONObjBuilder &builder) : mongo::BSONObj(builder.obj()) {} + RepoBSON(mongo::BSONObjBuilder& builder); /** * Constructor from raw data buffer. * @param rawData raw data */ - RepoBSON(const std::vector &rawData) : mongo::BSONObj(rawData.data()) {} + RepoBSON(const std::vector& rawData); /** * Default empty deconstructor. @@ -111,86 +112,28 @@ namespace repo { * Override the equals operator to perform the swap just like mongo bson * but also retrieve the mapping information */ - RepoBSON& operator=(RepoBSON otherCopy) { - swap(otherCopy); - return *this; - } - /** - * Override the swap operator to perform the swap just like mongo bson - * but also carry over the mapping information - */ - void swap(RepoBSON otherCopy) - { - mongo::BSONObj::swap(otherCopy); - bigFiles = otherCopy.bigFiles; - } + RepoBSON& operator=(RepoBSON otherCopy); - bool couldBeArray() const { - return mongo::BSONObj::couldBeArray(); - } + bool couldBeArray() const; - bool hasField(const std::string &label) const { - return mongo::BSONObj::hasField(label); - } + bool hasField(const std::string& label) const; - int nFields() const { - return mongo::BSONObj::nFields(); - } - - /** - * Returns the current time in the form of int64 timestamp - */ - static int64_t getCurrentTimestamp(); + int nFields() const; /** * returns a field from the BSON * @param label name of the field to retrieve * @return returns a RepoBSONElement */ - RepoBSONElement getField(const std::string &label) const - { - return RepoBSONElement(mongo::BSONObj::getField(label)); - } + class RepoBSONElement getField(const std::string& label) const; - bool getBoolField(const std::string &label) const - { - return mongo::BSONObj::getBoolField(label); - } + bool getBoolField(const std::string& label) const; - std::string getStringField(const std::string &label) const { - if (hasField(label)) - { - auto f = getField(label); - if (f.type() == ElementType::STRING) - { - return f.String(); - } - else - { - throw repo::lib::RepoFieldTypeException(label); - } - } - throw repo::lib::RepoFieldNotFoundException(label); - } + std::string getStringField(const std::string& label) const; - RepoBSON getObjectField(const std::string &label) const { - if (hasField(label)) - { - return mongo::BSONObj::getObjectField(label); - } - else - { - throw repo::lib::RepoFieldNotFoundException(label); - } - } + RepoBSON getObjectField(const std::string& label) const; - std::vector getBounds3D(const std::string& label) { - auto field = getObjectField(label); - return std::vector< lib::RepoVector3D>({ - lib::RepoVector3D(field.getFloatVectorField("0")), - lib::RepoVector3D(field.getFloatVectorField("1")), - }); - } + std::vector getBounds3D(const std::string& label); lib::RepoVector3D getVector3DField(const std::string& label) const; @@ -200,25 +143,13 @@ namespace repo { std::vector getDoubleVectorField(const std::string& label) const; - std::vector getFileList(const std::string& label) const - { - std::vector fileList; - RepoBSON arraybson = getObjectField(label); - std::set fields = arraybson.getFieldNames(); - for (const auto& field : fields) - { - fileList.push_back(arraybson.getStringField(field)); - } - return fileList; - } + std::vector getFileList(const std::string& label) const; double getDoubleField(const std::string &label) const; long long getLongField(const std::string& label) const; - bool isEmpty() const { - return mongo::BSONObj::isEmpty(); - } + bool isEmpty() const; /** * get a binary field in the form of vector of T @@ -232,49 +163,12 @@ namespace repo { std::vector &vec) const { - bool success = false; - - if (!hasField(field) || getField(field).type() == ElementType::STRING) - { - //Try to get it from file mapping. - std::vector bin = getBigBinary(field); - if (bin.size() > 0) - { - vec.resize(bin.size() / sizeof(T)); - memcpy(vec.data(), &bin[0], bin.size()); - success = true; - } - else - { - throw repo::lib::RepoFieldNotFoundException(field); - } - } - else { - RepoBSONElement bse = getField(field); - if (bse.type() == ElementType::BINARY && bse.binDataType() == mongo::BinDataGeneral) - { - bse.value(); - int length; - const char *binData = bse.binData(length); - if (length > 0) - { - vec.resize(length / sizeof(T)); - memcpy(vec.data(), binData, length); - success = true; - } - else - { - throw repo::lib::RepoFieldTypeException(field + "; has a length of zero bytes"); - } - } - else - { - throw repo::lib::RepoFieldTypeException(field); - } - } - - return success; + auto& buf = getBinary(field); + vec.resize(buf.size() / sizeof(T)); + memcpy(vec.data(), buf.data(), buf.size()); + return true; } + /** * Overload of getField function to retreve repo::lib::RepoUUID * @param label name of the field @@ -296,9 +190,7 @@ namespace repo { */ std::vector getFloatArray(const std::string &label) const; - int getIntField(const std::string &label) const { - return mongo::BSONObj::getIntField(label); - } + int getIntField(const std::string& label) const; /** * Get an array of fields given an element label @@ -314,36 +206,7 @@ namespace repo { */ time_t getTimeStampField(const std::string &label) const; - std::set getFieldNames() const { - std::set fieldNames; - mongo::BSONObj::getFieldNames(fieldNames); - return fieldNames; - } - - /** - * Get a field as a List of string pairs - * This is for scenarios were there is an element that is an array of objects - * and you want to get 2 fields within all the objects. - * @param arrLabel the element where this data is stored - * @param fstLabel label for #1 in pair - * @param sndLabel label for #2 in pair - * @return a List of pairs of strings - */ - std::list > getListStringPairField( - const std::string &arrLabel, - const std::string &fstLabel, - const std::string &sndLabel) const; - - //! Gets double from embedded sub-object based on name fields. - double getEmbeddedDouble( - const std::string &embeddedObjName, - const std::string &fieldName, - const double &defaultValue = 0) const; - - //! Returns true if embedded object contains given fieldName. - bool hasEmbeddedField( - const std::string &embeddedObjName, - const std::string &fieldName) const; + std::set getFieldNames() const; /** * Checks if a binary field exists within the RepoBSON @@ -354,32 +217,17 @@ namespace repo { */ bool hasBinField(const std::string &label) const; - virtual RepoBSON cloneAndAddFields( - const RepoBSON *changes) const; + bool isValid() const; - bool isValid() const { - return mongo::BSONObj::isValid(); - } + uint64_t objsize() const; - uint64_t objsize() const { - return mongo::BSONObj::objsize(); - } - - RepoBSON removeField(const std::string &label) const { - return mongo::BSONObj::removeField(label); - } + RepoBSON removeField(const std::string& label) const; - std::string toString() const { - return mongo::BSONObj::toString(); - } + std::string toString() const; - inline bool operator==(const RepoBSON other) const { - return mongo::BSONObj::operator==((mongo::BSONObj)other); - } + bool operator==(const RepoBSON other) const; - inline bool operator!=(const RepoBSON other) const { - return mongo::BSONObj::operator!=((mongo::BSONObj)other); - } + bool operator!=(const RepoBSON other) const; public: @@ -387,27 +235,11 @@ namespace repo { * ----------------- BIG FILE MANIPULATION ---------------------------------- */ - /** - * Clone and attempt the shrink the bson by offloading binary files to big - * file storage - * @return returns the shrunk BSON - */ - RepoBSON cloneAndShrink() const; - - std::vector getBigBinary(const std::string &key) const; - - /** - * Get the list of file names for the big files - * needs to be stored for this bson - * @return returns a list of {field name, file name} needed to be stored - */ - std::vector> getFileList() const; - /** * Get the mapping files from the bson object * @return returns the map of external (gridFS) files */ - std::unordered_map< std::string, std::pair > > getFilesMapping() const + const BinMapping& getFilesMapping() const { return bigFiles; } @@ -428,12 +260,19 @@ namespace repo { repo::core::model::RepoBSON getBinaryReference() const; void initBinaryBuffer(const std::vector &buffer); - bool hasLegacyFileReference() const; bool hasFileReference() const; + const std::vector& getBinary(const std::string& label) const; + protected: - std::unordered_map< std::string, std::pair > > bigFiles; + /** + * Override the swap operator to perform the swap just like mongo bson + * but also carry over the mapping information + */ + void swap(RepoBSON otherCopy); + + BinMapping bigFiles; }; // end }// end namespace model } // end namespace core diff --git a/bouncer/src/repo/core/model/bson/repo_bson_builder.cpp b/bouncer/src/repo/core/model/bson/repo_bson_builder.cpp index 9392df832..5b248e5f7 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_builder.cpp +++ b/bouncer/src/repo/core/model/bson/repo_bson_builder.cpp @@ -124,16 +124,8 @@ void RepoBSONBuilder::appendVector3DObject( void RepoBSONBuilder::appendLargeArray(std::string name, const void* data, size_t size) { - auto obj = this->tempObj(); - if (!obj.hasField(REPO_NODE_LABEL_ID)) - { - throw std::invalid_argument("appendLargeArray called before the builder is assigned a unique Id. Ensure appendDefaults has been called before appending large arrays."); - } - - std::string bName = obj.getUUIDField(REPO_NODE_LABEL_ID).toString() + "_" + name; - - binMapping[name] = - std::pair>(bName, std::vector()); - binMapping[name].second.resize(size); //uint8_t will ensure it is a byte addrressing - memcpy(binMapping[name].second.data(), data, size); + binMapping[name] = std::vector(); + auto& buf = binMapping[name]; + buf.resize(size); + memcpy(buf.data(), data, size); } \ No newline at end of file diff --git a/bouncer/src/repo/core/model/bson/repo_bson_builder.h b/bouncer/src/repo/core/model/bson/repo_bson_builder.h index 47238453b..accba5936 100644 --- a/bouncer/src/repo/core/model/bson/repo_bson_builder.h +++ b/bouncer/src/repo/core/model/bson/repo_bson_builder.h @@ -30,15 +30,14 @@ #define strcasecmp _stricmp #endif -#include -#include -#include "../../../lib/datastructure/repo_matrix.h" -#include "../../../lib/datastructure/repo_uuid.h" #include "repo_bson.h" +#include "repo/lib/datastructure/repo_matrix.h" +#include "repo/lib/datastructure/repo_uuid.h" +#include "repo/lib/datastructure/repo_variant.h" #include -#include "repo/lib/datastructure/repo_variant.h" +#include #include namespace repo { @@ -50,7 +49,7 @@ namespace repo { RepoBSONBuilder(); ~RepoBSONBuilder(); - using BinMapping = std::unordered_map>>; + using BinMapping = RepoBSON::BinMapping; BinMapping& mapping() { diff --git a/test/src/unit/repo/core/model/bson/ut_repo_bson.cpp b/test/src/unit/repo/core/model/bson/ut_repo_bson.cpp index 11dd664b0..314a66e37 100644 --- a/test/src/unit/repo/core/model/bson/ut_repo_bson.cpp +++ b/test/src/unit/repo/core/model/bson/ut_repo_bson.cpp @@ -15,23 +15,110 @@ * along with this program. If not, see . */ +#define NOMINMAX + #include #include #include #include +#include "../../../../repo_test_utils.h" +#include "../../../../repo_test_mesh_utils.h" #include +#include #include #include using namespace repo::core::model; using namespace testing; +namespace repo { + namespace core { + namespace model { + // This method takes precedence over all the other gtest Printers when printing + // RepoBSON. This is required as otherwise gtest will try and use begin/end, + // which are privately inherited and so inaccessible. + void PrintTo(const RepoBSON& point, std::ostream* os) { + *os << point.toString(); + } + } + } +} + static auto mongoTestBSON = BSON("ice" << "lolly" << "amount" << 100.0); static const RepoBSON testBson = RepoBSON(BSON("ice" << "lolly" << "amount" << 100)); static const RepoBSON emptyBson; +template +mongo::BSONObj makeBsonArray(std::vector a) +{ + mongo::BSONObjBuilder arrbuilder; + for (auto i = 0; i < a.size(); i++) + { + arrbuilder.append(std::to_string(i), a[i]); + } + return arrbuilder.obj(); +} + +template<> +mongo::BSONObj makeBsonArray(std::vector uuids) +{ + mongo::BSONObjBuilder arrbuilder; + for (auto i = 0; i < uuids.size(); i++) + { + arrbuilder.appendBinData(std::to_string(i), uuids[i].data().size(), mongo::bdtUUID, (char*)uuids[i].data().data()); + } + return arrbuilder.obj(); +} + +repo::lib::RepoVector3D makeRepoVector() +{ + repo::lib::RepoVector3D v; + v.x = (double)(rand() - rand()) / rand(); + v.y = (double)(rand() - rand()) / rand(); + v.z = (double)(rand() - rand()) / rand(); + return v; +} + +mongo::BSONObj makeRepoVectorObj(const repo::lib::RepoVector3D& v) +{ + mongo::BSONObjBuilder builder; + builder.append("x", v.x); + builder.append("y", v.y); + builder.append("z", v.z); + return builder.obj(); +} + +std::vector makeRandomBinary() +{ + std::vector bin; + for (int i = 0; i < 1000; i++) + { + bin.push_back(rand() % 256); + } + return bin; +} + +mongo::BSONObj makeBoundsObj(const repo::lib::RepoVector3D64& min, const repo::lib::RepoVector3D64& max) +{ + mongo::BSONObjBuilder builder; + builder.append("0", makeBsonArray(min.toStdVector())); + builder.append("1", makeBsonArray(max.toStdVector())); + return builder.obj(); +} + +mongo::BSONObj makeMatrixObj(const repo::lib::RepoMatrix& m) +{ + auto d = m.getData(); + mongo::BSONObjBuilder builder; + builder.append("0", makeBsonArray(std::vector({ d[0], d[1], d[2], d[3]}))); + builder.append("1", makeBsonArray(std::vector({ d[4], d[5], d[6], d[7] }))); + builder.append("2", makeBsonArray(std::vector({ d[8], d[9], d[10], d[11] }))); + builder.append("3", makeBsonArray(std::vector({ d[12], d[13], d[14], d[15] }))); + return builder.obj(); +} + /** * Construct from mongo builder and mongo bson should give me the same bson */ @@ -46,9 +133,9 @@ TEST(RepoBSONTest, ConstructFromMongo) RepoBSON bson2(builder); RepoBSON bsonDiff(BSON("something" << "different")); - //EXPECT_EQ(bson1, bson2); + EXPECT_EQ(bson1, bson2); EXPECT_EQ(bson1.toString(), bson2.toString()); - //EXPECT_NE(bson1, bsonDiff); + EXPECT_NE(bson1, bsonDiff); } TEST(RepoBSONTest, ConstructFromMongoSizeExceeds) { @@ -81,15 +168,11 @@ TEST(RepoBSONTest, GetBinDataGeneralAsVectorEmbedded) // legacy documents may still have them, therefore RepoBSON should be able // to return these fields if they exist. - mongo::BSONObjBuilder builder; + std::vector in, out; - std::vector < uint8_t > in, out; - - size_t size = 100; - - for (size_t i = 0; i < size; ++i) - in.push_back(i); + in = makeRandomBinary(); + mongo::BSONObjBuilder builder; builder << "stringTest" << "hello"; builder << "numTest" << 1.35; builder.appendBinData("binDataTest", in.size(), mongo::BinDataGeneral, &in[0]); @@ -97,7 +180,6 @@ TEST(RepoBSONTest, GetBinDataGeneralAsVectorEmbedded) RepoBSON bson(builder.obj()); EXPECT_TRUE(bson.getBinaryFieldAsVector("binDataTest", out)); - EXPECT_THAT(in, Eq(out)); EXPECT_THROW({ bson.getBinaryFieldAsVector("numTest", out); }, repo::lib::RepoFieldTypeException); @@ -105,426 +187,901 @@ TEST(RepoBSONTest, GetBinDataGeneralAsVectorEmbedded) EXPECT_THROW({ bson.getBinaryFieldAsVector("doesn'tExist", out); }, repo::lib::RepoFieldNotFoundException); } -TEST(RepoBSONTest, GetBinaryAsVectorReferenced) +TEST(RepoBSONTest, AssignOperator) { - mongo::BSONObjBuilder builder; - - std::vector < uint8_t > in, out; + // Simple object with no mappings - size_t size = 100; - - for (size_t i = 0; i < size; ++i) - in.push_back(i); + RepoBSON test = testBson; - std::unordered_map>> map; - std::string fname = "testingfile"; - map["binDataTest"] = std::pair>(fname, in); + EXPECT_TRUE(test.toString() == testBson.toString()); + EXPECT_EQ(test.getFilesMapping().size(), testBson.getFilesMapping().size()); - //FIXME: This needs to work until we stop supporting it - RepoBSON bson(BSON("binDataTest" << fname), map); + RepoBSON::BinMapping map; + map["field1"] = makeRandomBinary(); + map["field2"] = makeRandomBinary(); - RepoBSON bson2(RepoBSON(), map); + RepoBSON test2(testBson, map); - EXPECT_TRUE(bson.getBinaryFieldAsVector("binDataTest", out)); - EXPECT_FALSE(bson.getBinaryFieldAsVector(fname, out)); //make sure fieldname/filename are not mixed up. + // With mappings + EXPECT_THAT(test2.toString(), Eq(testBson.toString())); + EXPECT_THAT(test2.getFilesMapping(), Eq(map)); - EXPECT_THAT(in, Eq(out)); + RepoBSON test3 = test2; + EXPECT_THAT(test3.toString(), Eq(test2.toString())); + EXPECT_THAT(test3.getFilesMapping(), Eq(test2.getFilesMapping())); - EXPECT_TRUE(bson2.getBinaryFieldAsVector("binDataTest", out)); - EXPECT_FALSE(bson2.getBinaryFieldAsVector(fname, out)); //make sure fieldname/filename are not mixed up. + // Original instance should be independent - EXPECT_THAT(in, Eq(out)); + test2.removeField("ice"); + EXPECT_THAT(test3.toString(), Not(Eq(test2.toString()))); } -TEST(RepoBSONTest, AssignOperator) +TEST(RepoBSONTest, GetUUIDField) { - RepoBSON test = testBson; - - EXPECT_TRUE(test.toString() == testBson.toString()); - - EXPECT_EQ(test.getFilesMapping().size(), testBson.getFilesMapping().size()); + repo::lib::RepoUUID uuid = repo::lib::RepoUUID::createUUID(); - //Test with bigfile mapping - std::vector < uint8_t > in; + // Test legacy uuid + { + mongo::BSONObjBuilder builder; + builder.appendBinData("uuid", uuid.data().size(), mongo::bdtUUID, (char*)uuid.data().data()); - in.resize(100); + RepoBSON bson = RepoBSON(builder.obj()); + EXPECT_EQ(uuid, bson.getUUIDField("uuid")); + EXPECT_THROW({ bson.getUUIDField("empty"); }, repo::lib::RepoFieldNotFoundException); + } - std::unordered_map>> map, mapout; - map["testingfile"] = std::pair>("field", in); + // Test new UUID + { + mongo::BSONObjBuilder builder; + builder.appendBinData("uuid", uuid.data().size(), mongo::newUUID, (char*)uuid.data().data()); - RepoBSON test2(testBson, map); + RepoBSON bson = RepoBSON(builder.obj()); + EXPECT_EQ(uuid, bson.getUUIDField("uuid")); + EXPECT_THROW({ bson.getUUIDField("empty"); }, repo::lib::RepoFieldNotFoundException); + } - mapout = test2.getFilesMapping(); - ASSERT_EQ(mapout.size(), map.size()); + // Test type checking + { + mongo::BSONObjBuilder builder; + builder.appendBinData("uuid", uuid.data().size(), mongo::bdtCustom, (char*)uuid.data().data()); - auto mapIt = map.begin(); - auto mapoutIt = mapout.begin(); + RepoBSON bson = RepoBSON(builder.obj()); + EXPECT_THROW({ bson.getUUIDField("uuid"); }, repo::lib::RepoFieldTypeException); + } - for (; mapIt != map.end(); ++mapIt, ++mapoutIt) { - EXPECT_EQ(mapIt->first, mapIt->first); - EXPECT_EQ(mapIt->second.first, mapIt->second.first); - std::vector dataOut = mapoutIt->second.second; - std::vector dataIn = mapIt->second.second; - EXPECT_EQ(dataOut.size(), dataIn.size()); - if (dataIn.size() > 0) - EXPECT_EQ(0, strncmp((char*)&dataOut[0], (char*)&dataIn[0], dataIn.size())); + mongo::BSONObjBuilder builder; + builder.append("uuid", uuid.toString()); + + RepoBSON bson = RepoBSON(builder.obj()); + EXPECT_THROW({ bson.getUUIDField("uuid"); }, repo::lib::RepoFieldTypeException); } } -TEST(RepoBSONTest, Swap) +TEST(RepoBSONTest, GetUUIDFieldArray) { - RepoBSON test = testBson; + std::vector uuids = { + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID() + }; + + mongo::BSONObjBuilder builder; + builder.appendArray("uuids", makeBsonArray(uuids)); + RepoBSON bson = builder.obj(); + EXPECT_THAT(bson.getUUIDFieldArray("uuids"), ElementsAreArray(uuids)); // We expect the order to be preserved here - //Test with bigfile mapping - std::vector < uint8_t > in; + // The getUUIDFieldArray doesn't throw, but returns an empty array if the + // field does not exist. - in.resize(100); + EXPECT_THAT(bson.getUUIDFieldArray("none"), IsEmpty()); +} - std::unordered_map>> map, mapout; - map["testingfile"] = std::pair>("blah", in); +TEST(RepoBSONTest, GetFloatArray) +{ + size_t size = 10; - RepoBSON testDiff_org(BSON("entirely" << "different"), map); - RepoBSON testDiff = testDiff_org; + { + std::vector arr; + for (size_t i = 0; i < size; ++i) + { + arr.push_back((float)rand() / 100.); + } - EXPECT_TRUE(testDiff_org.toString() == testDiff.toString()); - EXPECT_TRUE(testDiff_org.getFilesMapping().size() == testDiff.getFilesMapping().size()); + mongo::BSONObjBuilder builder; + builder.appendArray("array", makeBsonArray(arr)); - test.swap(testDiff); - EXPECT_EQ(testDiff_org.toString(), test.toString()); + RepoBSON bson = builder.obj(); - mapout = test.getFilesMapping(); - ASSERT_EQ(map.size(), mapout.size()); + EXPECT_THAT(bson.getFloatArray("array"), ElementsAreArray(arr)); + EXPECT_THAT(bson.getFloatArray("none"), IsEmpty()); + } - auto mapIt = map.begin(); - auto mapoutIt = mapout.begin(); + // We expect a type error if the field is an array, but of the wrong type - for (; mapIt != map.end(); ++mapIt, ++mapoutIt) { - EXPECT_EQ(mapIt->first, mapIt->first); - EXPECT_EQ(mapIt->second.first, mapIt->second.first); - std::vector dataOut = mapoutIt->second.second; - std::vector dataIn = mapIt->second.second; - EXPECT_EQ(dataIn.size(), dataOut.size()); - if (dataIn.size() > 0) - EXPECT_EQ(0, strncmp((char*)dataOut.data(), (char*)dataIn.data(), dataIn.size())); + std::vector arr; + mongo::BSONObjBuilder builder; + + arr.reserve(size); + for (size_t i = 0; i < size; ++i) + { + arr.push_back(std::to_string((float)rand() / 100.)); + } + + builder.appendArray("array", makeBsonArray(arr)); + + RepoBSON bson = builder.obj(); + + EXPECT_THROW({ bson.getFloatArray("array"); }, repo::lib::RepoFieldTypeException); + EXPECT_THAT(bson.getFloatArray("none"), IsEmpty()); } } -TEST(RepoBSONTest, GetUUIDField) +TEST(RepoBSONTest, GetTimeStampField) { - repo::lib::RepoUUID uuid = repo::lib::RepoUUID::createUUID(); - mongo::BSONObjBuilder builder; - builder.appendBinData("uuid", uuid.data().size(), mongo::bdtUUID, (char*)uuid.data().data()); + auto posixTimestamp = time(NULL); + + // By convention, Date_t takes a posix timestamp in milliseconds + mongo::Date_t date = mongo::Date_t(posixTimestamp * 1000); - RepoBSON test = RepoBSON(builder.obj()); - EXPECT_EQ(uuid, test.getUUIDField("uuid")); + { + mongo::BSONObjBuilder builder; + builder.append("ts", date); + RepoBSON bson = builder.obj(); + EXPECT_THAT(bson.getTimeStampField("ts"), Eq(posixTimestamp)); + } - //Shouldn't fail if trying to get a uuid field that doesn't exist - EXPECT_NE(uuid, test.getUUIDField("hello")); - EXPECT_NE(uuid, testBson.getUUIDField("ice")); - EXPECT_NE(uuid, emptyBson.getUUIDField("ice")); + { + mongo::BSONObjBuilder builder; + builder.append("ts", "hello"); + RepoBSON bson = builder.obj(); + EXPECT_THROW({ bson.getTimeStampField("ts"); }, repo::lib::RepoFieldTypeException); + } - //Test new UUID - mongo::BSONObjBuilder builder2; - builder2.appendBinData("uuid", uuid.data().size(), mongo::newUUID, (char*)uuid.data().data()); - RepoBSON test2 = RepoBSON(builder2.obj()); - EXPECT_EQ(uuid, test2.getUUIDField("uuid")); + { + mongo::BSONObjBuilder builder; + RepoBSON bson = builder.obj(); + EXPECT_THROW({ bson.getTimeStampField("ts"); }, repo::lib::RepoFieldNotFoundException); + } } -TEST(RepoBSONTest, GetUUIDFieldArray) +TEST(RepoBSONTest, GetBinary) { - std::vector uuids; + { + auto in = makeRandomBinary(); + RepoBSON::BinMapping mapping; + mapping["blah"] = in; - size_t size = 10; - mongo::BSONObjBuilder builder, arrbuilder; + RepoBSON bson(testBson, mapping); - uuids.reserve(size); - for (size_t i = 0; i < size; ++i) - { - uuids.push_back(repo::lib::RepoUUID::createUUID()); - arrbuilder.appendBinData(std::to_string(i), uuids[i].data().size(), mongo::bdtUUID, (char*)uuids[i].data().data()); + EXPECT_THAT(testBson.getBinary("blah"), Eq(mapping["blah"])); } - builder.appendArray("uuid", arrbuilder.obj()); + // Check that if we pass in formatted data such as strings, it doesn't mess + // with them + { + std::string s = "Ut velit leo, lobortis eget nibh quis, imperdiet \0consectetur orci. Donec accumsan\0 tortor odio, vel imperdiet metus viverra id. Maecenas efficitur vulputate diam id molestie. Cras molestie, orci eget consectetur auctor, leo velit auctor lacus, nec dictum velit nibh et diam. Donec ac sapien euismod, vulputate odio quis, bibendum nulla. \"Sed aliquet \"rhoncus pulvinar\0. Donec consectetur tristiq\"ue nibh non pulvinar."; + auto data = std::vector(s.c_str(), s.c_str() + s.size()); - RepoBSON bson = builder.obj(); + RepoBSON::BinMapping mapping; + mapping["string"] = data; - std::vector outUUIDS = bson.getUUIDFieldArray("uuid"); + RepoBSON bson(testBson, mapping); - EXPECT_EQ(outUUIDS.size(), uuids.size()); - for (size_t i = 0; i < size; i++) - { - EXPECT_EQ(uuids[i], outUUIDS[i]); + auto out = bson.getBinary("string"); + EXPECT_THAT(std::string((const char*)out.data(), out.size()), Eq(s)); } - - //Shouldn't fail if trying to get a uuid field that doesn't exist - EXPECT_EQ(0, bson.getUUIDFieldArray("hello").size()); - EXPECT_EQ(0, testBson.getUUIDFieldArray("ice").size()); - EXPECT_EQ(0, emptyBson.getUUIDFieldArray("ice").size()); } -TEST(RepoBSONTest, GetFloatArray) +struct randomType { - std::vector floatArrIn; + int a; + char b; + double c; +}; - size_t size = 10; - mongo::BSONObjBuilder builder, arrbuilder; +static bool operator== (randomType a, randomType b) +{ + return a.a == b.a && a.b == b.b && a.c == b.c; +} - floatArrIn.reserve(size); - for (size_t i = 0; i < size; ++i) +TEST(RepoBSONTest, GetBinaryAsVector) +{ { - floatArrIn.push_back((float)rand() / 100.); - arrbuilder << std::to_string(i) << floatArrIn[i]; + auto in = makeRandomBinary(); + RepoBSON::BinMapping mapping; + mapping["blah"] = in; + + RepoBSON bson(testBson, mapping); + + std::vector out; + bson.getBinaryFieldAsVector("blah", out); + + EXPECT_THAT(out, Eq(in)); + EXPECT_THROW({ bson.getBinaryFieldAsVector("hello", out); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getBinaryFieldAsVector("nofield", out); }, repo::lib::RepoFieldNotFoundException); } - builder.appendArray("floatarr", arrbuilder.obj()); + // Check that the typecasting works + { + // Write this test using std::vectors to handle the buffers, because that + // is most likely what will be used for real - RepoBSON bson = builder.obj(); + std::vector in; - std::vector floatArrOut = bson.getFloatArray("floatarr"); + for (int i = 0; i < 100; i++) + { + randomType t; + t.a = rand(); + t.b = rand() % 256; + t.c = rand() * 10000 / 10843.7; + in.push_back(t); + } - EXPECT_EQ(floatArrIn.size(), floatArrOut.size()); + RepoBSON::BinMapping mapping; + mapping["randomType"] = std::vector( + (const char*)in.data(), + (const char*)in.data() + (in.size() * sizeof(randomType))); - for (size_t i = 0; i < size; i++) - { - EXPECT_EQ(floatArrIn[i], floatArrOut[i]); - } + RepoBSON bson(testBson, mapping); - //Shouldn't fail if trying to get a uuid field that doesn't exist - EXPECT_EQ(0, bson.getFloatArray("hello").size()); - EXPECT_EQ(0, testBson.getFloatArray("ice").size()); - EXPECT_EQ(0, emptyBson.getFloatArray("ice").size()); + std::vector out; + bson.getBinaryFieldAsVector("randomType", out); + + EXPECT_THAT(out, Eq(in)); + } } -TEST(RepoBSONTest, GetTimeStampField) +TEST(RepoBSONTest, BinaryFilesUpdated) { + // Checks that providing a mapping along with a RepoBSON that already has a + // big files array will join the two, and prefer the *original* mapping + mongo::BSONObjBuilder builder; + builder.append("field", "value"); + + RepoBSON::BinMapping existing; + existing["bin1"] = makeRandomBinary(); + existing["bin2"] = makeRandomBinary(); - mongo::Date_t date = mongo::Date_t(time(NULL) * 1000); + RepoBSON::BinMapping additional; + additional["bin1"] = makeRandomBinary(); + additional["bin2"] = makeRandomBinary(); + additional["bin3"] = makeRandomBinary(); - builder.append("ts", date); + RepoBSON bson(builder.obj(), existing); - RepoBSON tsBson = builder.obj(); + EXPECT_THAT(bson.getBinary("bin1"), Eq(existing["bin1"])); + EXPECT_THAT(bson.getBinary("bin2"), Eq(existing["bin2"])); + EXPECT_THROW({ bson.getBinary("bin3"); }, repo::lib::RepoFieldNotFoundException); - EXPECT_EQ(date.asInt64(), tsBson.getTimeStampField("ts")); + RepoBSON bson2(bson, additional); - //Shouldn't fail if trying to get a uuid field that doesn't exist - EXPECT_EQ(-1, tsBson.getTimeStampField("hello")); - EXPECT_EQ(-1, testBson.getTimeStampField("ice")); - EXPECT_EQ(-1, emptyBson.getTimeStampField("ice")); + EXPECT_THAT(bson2.getBinary("bin1"), Eq(existing["bin1"])); + EXPECT_THAT(bson2.getBinary("bin2"), Eq(existing["bin2"])); + EXPECT_THAT(bson2.getBinary("bin3"), Eq(additional["bin3"])); + EXPECT_THAT(bson2.getBinary("bin1"), Not(Eq(additional["bin1"]))); + EXPECT_THAT(bson2.getBinary("bin2"), Not(Eq(additional["bin2"]))); } -TEST(RepoBSONTest, GetListStringPairField) +TEST(RepoBSONTest, GetFilesMapping) { - std::vector> vecIn; + RepoBSON::BinMapping mapping, outMapping; + mapping["orgRef"] = makeRandomBinary(); - size_t size = 10; - mongo::BSONObjBuilder builder, arrbuilder; + RepoBSON binBson(testBson, mapping); + outMapping = binBson.getFilesMapping(); - vecIn.reserve(size); - for (size_t i = 0; i < size; ++i) + EXPECT_EQ(1, outMapping.size()); + EXPECT_FALSE(outMapping.find("orgRef") == outMapping.end()); + + EXPECT_EQ(0, testBson.getFilesMapping().size()); + EXPECT_EQ(0, emptyBson.getFilesMapping().size()); +} + +TEST(RepoBSONTest, HasOversizeFiles) +{ + RepoBSON::BinMapping mapping; + mapping["orgRef"] = makeRandomBinary(); + + RepoBSON binBson(testBson, mapping); + + EXPECT_TRUE(binBson.hasOversizeFiles()); + EXPECT_FALSE(testBson.hasOversizeFiles()); + EXPECT_FALSE(emptyBson.hasOversizeFiles()); +} + +TEST(RepoBSONTest, GetBoolField) +{ { - int n1 = rand(); - int n2 = rand(); - vecIn.push_back({ std::to_string(n1), std::to_string(n2) }); + mongo::BSONObjBuilder builder; + builder.append("bool", true); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getBoolField("bool"), Eq(true)); + } - arrbuilder << std::to_string(i) << BSON("first" << std::to_string(n1) << "second" << std::to_string(n2) << "third" << 1); + { + mongo::BSONObjBuilder builder; + builder.append("bool", false); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getBoolField("bool"), Eq(false)); // Get the value false - not throwing based on not finding the field } - builder.appendArray("bsonArr", arrbuilder.obj()); + { + mongo::BSONObjBuilder builder; + builder.append("bool", "string"); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getBoolField("bool"); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getBoolField("none"); }, repo::lib::RepoFieldNotFoundException); + } +} - RepoBSON bson = builder.obj(); +TEST(RepoBSONTest, GetStringField) +{ + { + mongo::BSONObjBuilder builder; + builder.append("string", "value"); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getStringField("string"), Eq("value")); + } + + { + mongo::BSONObjBuilder builder; + builder.append("string", ""); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getStringField("string"), Eq("")); // Get the 'empty' value - not throwing based on an empty field + } + + { + mongo::BSONObjBuilder builder; + builder.append("string", true); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getStringField("string"); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getStringField("string"); }, repo::lib::RepoFieldNotFoundException); + } +} + +TEST(RepoBSONTest, GetObjectField) +{ + // A single object + { + mongo::BSONObjBuilder sub; + sub.append("string", "value"); + sub.append("int", 0); + auto subObj = sub.obj(); - std::list> vecOut = - bson.getListStringPairField("bsonArr", "first", "second"); + mongo::BSONObjBuilder builder; + builder.append("object", subObj); - EXPECT_EQ(vecIn.size(), vecOut.size()); - auto listIt = vecOut.begin(); + RepoBSON bson(builder.obj()); - for (size_t i = 0; i < size; i++) + EXPECT_THAT(bson.getObjectField("object").toString(), Eq(subObj.toString())); + } + + // A simple array (as an object) + { + std::vector strings = { + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + }; + + mongo::BSONObjBuilder builder; + builder.append("object", makeBsonArray(strings)); + + RepoBSON bson(builder.obj()); + + auto o = bson.getObjectField("object"); + + for (int i = 0; i < strings.size(); i++) + { + EXPECT_THAT(o.getStringField(std::to_string(i)), Eq(strings[i])); + } + } + + // A complex array of objects { - EXPECT_EQ(vecIn[i][0], listIt->first); - EXPECT_EQ(vecIn[i][1], listIt->second); - ++listIt; + mongo::BSONObjBuilder obj1; + std::vector strings = { + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + }; + obj1.append("array", makeBsonArray(strings)); + obj1.append("field", "value"); + + mongo::BSONObjBuilder obj2; + std::vector uuids = { + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + }; + obj2.append("array", makeBsonArray(uuids)); + obj2.append("field", "value"); + + mongo::BSONObjBuilder obj3; + obj3.append("field", "value"); + obj3.append("field2", 0); + + std::vector objs = { + obj1.obj(), + obj2.obj(), + obj3.obj() + }; + + mongo::BSONObjBuilder builder; + builder.append("object", makeBsonArray(objs)); + + RepoBSON bson(builder.obj()); + + auto o = bson.getObjectField("object"); + + EXPECT_THAT(o.getObjectField("0").getStringArray("array"), Eq(strings)); + EXPECT_THAT(o.getObjectField("0").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("1").getUUIDFieldArray("array"), Eq(uuids)); + EXPECT_THAT(o.getObjectField("1").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("2").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("2").getIntField("field2"), Eq(0)); } - //Shouldn't fail if trying to get a uuid field that doesn't exist - EXPECT_EQ(0, bson.getListStringPairField("hello", "first", "third").size()); - EXPECT_EQ(0, bson.getListStringPairField("hello", "hi", "bye").size()); - EXPECT_EQ(0, bson.getListStringPairField("hello", "first", "second").size()); - EXPECT_EQ(0, testBson.getListStringPairField("ice", "first", "second").size()); - EXPECT_EQ(0, emptyBson.getListStringPairField("ice", "first", "second").size()); + // A nested hierarchy of objects + { + mongo::BSONObjBuilder obj1; + std::vector strings = { + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + }; + obj1.append("array", makeBsonArray(strings)); + obj1.append("field", "value"); + + mongo::BSONObjBuilder obj2; + std::vector uuids = { + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + repo::lib::RepoUUID::createUUID(), + }; + obj2.append("array", makeBsonArray(uuids)); + obj2.append("field", "value"); + obj2.append("obj1", obj1.obj()); + + mongo::BSONObjBuilder obj3; + obj3.append("field", "value"); + obj3.append("obj2", obj2.obj()); + + mongo::BSONObjBuilder builder; + builder.append("object", obj3.obj()); + + RepoBSON bson(builder.obj()); + + auto o = bson.getObjectField("object"); + + EXPECT_THAT(o.getObjectField("field").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("obj2").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("obj2").getUUIDFieldArray("array"), Eq(uuids)); + EXPECT_THAT(o.getObjectField("obj2").getObjectField("obj1").getStringField("field"), Eq("value")); + EXPECT_THAT(o.getObjectField("obj2").getObjectField("obj1").getStringArray("array"), Eq(strings)); + } + + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getObjectField("object"); }, repo::lib::RepoFieldNotFoundException); + } + + // Should throw if this is a primitive type - objects are explicitly BSON + // structures; if looking for something encapsulating both primitives and + // these, the caller should be looking at a BSON element. + // Only arrays are polymorphic with objects. + { + mongo::BSONObjBuilder builder; + builder.append("object", "string"); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getObjectField("object"); }, repo::lib::RepoFieldTypeException); + } } -TEST(RepoBSONTest, CloneAndShrink) +TEST(RepoBSONTest, GetBounds3D) { - //shrinking a bson without any binary fields should yield an identical bson - RepoBSON shrunkBson = testBson.cloneAndShrink(); + // Bounds are encoded as two nested arrays, and parse to two (float) RepoVectors + + repo::lib::RepoVector3D64 a(rand() - rand(), rand() - rand(), rand() - rand()); + repo::lib::RepoVector3D64 b(rand() - rand(), rand() - rand(), rand() - rand()); + auto min = repo::lib::RepoVector3D64::min(a, b); + auto max = repo::lib::RepoVector3D64::max(a, b); - //EXPECT_EQ(testBson, shrunkBson); - EXPECT_EQ(testBson.getFilesMapping().size(), shrunkBson.getFilesMapping().size()); + auto bounds = makeBoundsObj(min, max); mongo::BSONObjBuilder builder; - std::vector < uint8_t > in, out, ref; + builder.append("bounds", bounds); - size_t size = 100; + RepoBSON bson(builder.obj()); - in.resize(size); - ref.resize(size); + auto expected = std::vector({ + repo::lib::RepoVector3D((float)min.x, (float)min.y, (float)min.z), + repo::lib::RepoVector3D((float)max.x, (float)max.y, (float)max.z), + }); - builder << "stringTest" << "hello"; - builder << "numTest" << 1.35; - builder.appendBinData("binDataTest", in.size(), mongo::BinDataGeneral, in.data()); + EXPECT_THAT(bson.getBounds3D("bounds"), Eq(expected)); +} - std::unordered_map < std::string, std::pair>> mapping, outMapping; - mapping["orgRef"] = std::pair>("blah", ref); +TEST(RepoBSONTest, GetVector3DField) +{ + // RepoVectors are encoded by component names instead of indices - RepoBSON binBson(builder.obj(), mapping); + mongo::BSONObjBuilder builder; + auto v = makeRepoVector(); + builder.append("vector", makeRepoVectorObj(v)); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getVector3DField("vector"), Eq(v)); +} - shrunkBson = binBson.cloneAndShrink(); - outMapping = shrunkBson.getFilesMapping(); +TEST(RepoBSONTest, GetMatrixField) +{ + // Matrices are encoded as row-major, nested arrays + auto m = repo::test::utils::mesh::makeTransform(true, true); -// EXPECT_NE(shrunkBson, binBson); - EXPECT_FALSE(shrunkBson.hasField("binDataTest")); - EXPECT_EQ(2, outMapping.size()); - EXPECT_TRUE(outMapping.find("orgRef") != outMapping.end()); + mongo::BSONObjBuilder builder; + builder.append("matrix", makeMatrixObj(m)); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getMatrixField("matrix"), Eq(m)); +} - //Check the binary still obtainable - EXPECT_TRUE(shrunkBson.getBinaryFieldAsVector("binDataTest", out)); +TEST(RepoBSONTest, GetFloatVector) +{ + // This is typically used to intiialise a 2d or 3d vector, though for now + // this is not enforced and it will return fields of arbitrary length - ASSERT_EQ(in.size(), out.size()); - for (size_t i = 0; i < out.size(); ++i) { - EXPECT_EQ(in[i], out[i]); + std::vector arr; + for (int i = 0; i < 10; i++) + { + arr.push_back(rand() / 1.23); + } + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THAT(bson.getFloatVectorField("vector"), Eq(arr)); } - //Check the out referenced bigfile is still sane + // Undernath, this retrieves values as doubles, so it should also work for + // double arrays with ranges inside those of floats - EXPECT_EQ(ref.size(), outMapping["orgRef"].second.size()); - for (size_t i = 0; i < ref.size(); ++i) { - EXPECT_EQ(ref[i], outMapping["orgRef"].second[i]); + std::vector arr; + for (int i = 0; i < 10; i++) + { + arr.push_back(rand() / 1.23); + } + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THAT(bson.getFloatVectorField("vector"), ElementsAreArray(arr)); } + + // The cast means it will silently fail if the doubles are outside the range + // of a float however - this is accepted and it is expected the caller is aware + // of this and will use the correct method + { + std::vector arr; + arr.push_back(DBL_MAX); + arr.push_back(DBL_MIN); + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THAT(bson.getFloatVectorField("vector"), ElementsAreArray(arr)); + } + + // As these are intended to be used with vectors, they differ in the usual way + // from the 'Array' methods, in that if the field is missing it will throw, + // in addition to throwing if the type is wrong + { + std::vector arr; + arr.push_back(repo::lib::RepoUUID::createUUID().toString()); + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THROW({ bson.getFloatVectorField("vector"); }, repo::lib::RepoFieldTypeException); + } + + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getFloatVectorField("vector"); }, repo::lib::RepoFieldNotFoundException); + } + } -TEST(RepoBSONTest, GetBigBinary) +TEST(RepoBSONTest, GetDoubleVectorField) { - std::vector < uint8_t > in, out; + // This is typically used to intialise a 2d or 3d vector, though for now + // this is not enforced and it will return fields of arbitrary length - size_t size = 100; + // Should return floats as doubles + { + std::vector arr; + for (int i = 0; i < 10; i++) + { + arr.push_back(rand() / 1.23); + } + arr.push_back(FLT_MIN); + arr.push_back(FLT_MAX); + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THAT(bson.getDoubleVectorField("vector"), ElementsAreArray(arr)); + } - in.resize(size); + { + std::vector arr; + for (int i = 0; i < 10; i++) + { + arr.push_back(rand() / 1.23); + } + arr.push_back(DBL_MIN); + arr.push_back(DBL_MAX); + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THAT(bson.getDoubleVectorField("vector"), Eq(arr)); + } - std::unordered_map < std::string, std::pair>> mapping; - mapping["blah"] = std::pair>("orgRef", in); + { + std::vector arr; + for (int i = 0; i < 10; i++) + { + arr.push_back(rand()); + } - RepoBSON binBson(testBson, mapping); + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); - out = binBson.getBigBinary("blah"); - EXPECT_EQ(in.size(), out.size()); - for (size_t i = 0; i < out.size(); ++i) + EXPECT_THAT(bson.getDoubleVectorField("vector"), ElementsAreArray(arr)); + } + + // As these are intended to be used with vectors, they differ in the usual way + // from the 'Array' methods, in that if the field is missing it will throw, + // in addition to throwing if the type is wrong { - EXPECT_EQ(in[i], out[i]); + std::vector arr; + arr.push_back(repo::lib::RepoUUID::createUUID().toString()); + + mongo::BSONObjBuilder builder; + builder.append("vector", makeBsonArray(arr)); + RepoBSON bson(builder.obj()); + + EXPECT_THROW({ bson.getDoubleVectorField("vector"); }, repo::lib::RepoFieldTypeException); } - EXPECT_EQ(0, binBson.getBigBinary("hello").size()); - EXPECT_EQ(0, binBson.getBigBinary("ice").size()); - EXPECT_EQ(0, emptyBson.getBigBinary("ice").size()); + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getDoubleVectorField("vector"); }, repo::lib::RepoFieldNotFoundException); + } } TEST(RepoBSONTest, GetFileList) { - std::vector < uint8_t > in; + // This is effectively thes same as getStringArray - it is intended to be used with the rFile field. + + std::vector files = { + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString() + "_rvt", + }; - size_t size = 100; + { + mongo::BSONObjBuilder builder; + builder.append("rFile", makeBsonArray(files)); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getFileList("rFile"), Eq(files)); + } - in.resize(size); + // If there is no field, this method should throw an exception + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getFileList("rFile"); }, repo::lib::RepoFieldNotFoundException); + } - std::unordered_map < std::string, std::pair>> mapping; - mapping["orgRef"] = std::pair>("blah", in); + // Or if it is the wrong type + { + mongo::BSONObjBuilder builder; - RepoBSON binBson(testBson, mapping); - auto fileList = binBson.getFileList(); + builder.append("rFile", makeBsonArray(std::vector({ + 1, + 2, + 3 + }))); - EXPECT_EQ(1, fileList.size()); - EXPECT_TRUE(fileList[0].first == "orgRef"); - EXPECT_EQ(0, testBson.getFileList().size()); - EXPECT_EQ(0, emptyBson.getFileList().size()); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getFileList("rFile"); }, repo::lib::RepoFieldTypeException); + } } -TEST(RepoBSONTest, GetFilesMapping) +TEST(RepoBSONTest, GetDoubleField) { - std::vector < uint8_t > in; + { + mongo::BSONObjBuilder builder; + builder.append("double", 1.29348); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getDoubleField("double"), Eq(1.29348)); + } - size_t size = 100; + // Double field should also return floats (but not integers). + { + mongo::BSONObjBuilder builder; + builder.append("double", (float)1.29348); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getDoubleField("double"), Eq(1.29348)); + } - in.resize(size); + // This is because doubles can contain floats losslessly, but not do the same + // for integers. + { + mongo::BSONObjBuilder builder; + builder.append("double", (long long)1); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getDoubleField("double"); }, repo::lib::RepoFieldTypeException); + } - std::unordered_map < std::string, std::pair>> mapping, outMapping; - mapping["orgRef"] = std::pair>("blah", in); + { + mongo::BSONObjBuilder builder; + builder.append("double", 0); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getDoubleField("double"), Eq(0)); // Get the 'empty' value - not throwing based on an empty field + } - RepoBSON binBson(testBson, mapping); - outMapping = binBson.getFilesMapping(); + { + mongo::BSONObjBuilder builder; + builder.append("double", true); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getDoubleField("double"); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getDoubleField("double"); }, repo::lib::RepoFieldNotFoundException); + } +} - EXPECT_EQ(1, outMapping.size()); - EXPECT_FALSE(outMapping.find("orgRef") == outMapping.end()); +TEST(RepoBSONTest, GetLongField) +{ + { + mongo::BSONObjBuilder builder; + builder.append("long", (long long)LONG_MAX); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getLongField("long"), Eq(LONG_MAX)); + } - EXPECT_EQ(0, testBson.getFilesMapping().size()); - EXPECT_EQ(0, emptyBson.getFilesMapping().size()); + { + mongo::BSONObjBuilder builder; + builder.append("long", 0); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getLongField("long"), Eq(0)); // Get the 'empty' value - not throwing based on an empty field + } + + { + mongo::BSONObjBuilder builder; + builder.append("long", true); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getLongField("long"); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getLongField("long"); }, repo::lib::RepoFieldNotFoundException); + } } -TEST(RepoBSONTest, HasOversizeFiles) +TEST(RepoBSONTest, IsEmpty) { - std::vector < uint8_t > in; + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.isEmpty(), IsTrue()); + } - size_t size = 100; + // Only regular fields + { + mongo::BSONObjBuilder builder; + builder.append("field", "value"); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.isEmpty(), IsFalse()); + } - in.resize(size); + // Only big files + { + mongo::BSONObjBuilder builder; + RepoBSON::BinMapping map; + map["bin"] = makeRandomBinary(); + RepoBSON bson(builder.obj(), map); + EXPECT_THAT(bson.isEmpty(), IsFalse()); + } +} - std::unordered_map < std::string, std::pair>> mapping; - mapping["orgRef"] = std::pair>("blah", in); +TEST(RepoBSONTest, GetIntField) +{ + { + mongo::BSONObjBuilder builder; + builder.append("int", (int)INT_MIN); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getLongField("int"), Eq(INT_MIN)); + } - RepoBSON binBson(testBson, mapping); + { + mongo::BSONObjBuilder builder; + builder.append("int", 0); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getLongField("int"), Eq(0)); // Get the 'empty' value - not throwing based on an empty field + } - EXPECT_TRUE(binBson.hasOversizeFiles()); - EXPECT_FALSE(testBson.hasOversizeFiles()); - EXPECT_FALSE(emptyBson.hasOversizeFiles()); + { + mongo::BSONObjBuilder builder; + builder.append("int", true); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getLongField("int"); }, repo::lib::RepoFieldTypeException); + EXPECT_THROW({ bson.getLongField("int"); }, repo::lib::RepoFieldNotFoundException); + } } -TEST(RepoBSONTest, GetEmbeddedDoubleTest) +TEST(RepoBSONTest, GetStringArray) { - RepoBSON empty; - - //Shouldn't fail. - EXPECT_EQ(empty.getEmbeddedDouble("something", "somethingElse"), 0); - EXPECT_EQ(empty.getEmbeddedDouble("something", "somethingElse", 10), 10); + std::vector files = { + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + repo::lib::RepoUUID::createUUID().toString(), + }; - RepoBSON hasFieldWrongTypeBson(BSON("field" << 1)); - EXPECT_EQ(hasFieldWrongTypeBson.getEmbeddedDouble("field", "somethingElse"), 0); + { + mongo::BSONObjBuilder builder; + builder.append("strings", makeBsonArray(files)); + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getStringArray("strings"), Eq(files)); + } - RepoBSON hasFieldNoEmbeddedField(BSON("field" << mongoTestBSON)); - EXPECT_EQ(hasFieldNoEmbeddedField.getEmbeddedDouble("field", "somethingElse"), 0); + // If there is no field, this method should return an empty array + { + mongo::BSONObjBuilder builder; + RepoBSON bson(builder.obj()); + EXPECT_THAT(bson.getFileList("strings"), IsEmpty()); + } - RepoBSON hasEmbeddedFieldWrongType(BSON("field" << mongoTestBSON)); - EXPECT_EQ(hasEmbeddedFieldWrongType.getEmbeddedDouble("field", "ice"), 0); + // Or if it is the wrong type + { + mongo::BSONObjBuilder builder; - RepoBSON expectNumber(BSON("field" << mongoTestBSON)); - EXPECT_EQ(expectNumber.getEmbeddedDouble("field", "amount"), 100); + builder.append("strings", makeBsonArray(std::vector({ + 1, + 2, + 3 + }))); - auto innerBson = BSON("amount" << 1.10101); - RepoBSON expectNumber2(BSON("field" << innerBson)); - EXPECT_EQ(expectNumber2.getEmbeddedDouble("field", "amount"), 1.10101); + RepoBSON bson(builder.obj()); + EXPECT_THROW({ bson.getFileList("strings"); }, repo::lib::RepoFieldTypeException); + } } -TEST(RepoBSONTest, HasEmbeddedFieldTest) +TEST(RepoBSONTest, HasBinField) { - EXPECT_FALSE(emptyBson.hasEmbeddedField("hi", "bye")); + RepoBSON::BinMapping map; + map["bin1"] = makeRandomBinary(); + + mongo::BSONObjBuilder builder; + builder.append("bin2", 1000); - RepoBSON hasFieldWrongTypeBson(BSON("field" << 1)); - EXPECT_FALSE(hasFieldWrongTypeBson.hasEmbeddedField("field", "bye")); + RepoBSON bson(builder.obj(), map); - RepoBSON expectTrue(BSON("field" << mongoTestBSON)); - EXPECT_TRUE(expectTrue.hasEmbeddedField("field", "ice")); - EXPECT_FALSE(expectTrue.hasEmbeddedField("field", "NonExistent")); + EXPECT_THAT(bson.hasBinField("bin1"), IsTrue()); + EXPECT_THAT(bson.hasBinField("bin2"), IsFalse()); + EXPECT_THAT(bson.hasBinField("bin3"), IsFalse()); } \ No newline at end of file diff --git a/test/src/unit/repo/core/model/bson/ut_repo_bson_builder.cpp b/test/src/unit/repo/core/model/bson/ut_repo_bson_builder.cpp index e3982461e..666b43645 100644 --- a/test/src/unit/repo/core/model/bson/ut_repo_bson_builder.cpp +++ b/test/src/unit/repo/core/model/bson/ut_repo_bson_builder.cpp @@ -125,39 +125,6 @@ TEST(RepoBSONBuilderTest, AppendRepoVectorT) } -TEST(RepoBSONBuilderTest, AppendArrayPair) -{ - std::list> list; - int size = 10; - for (int i = 0; i < size; ++i) - { - std::pair p(std::to_string(std::rand()), std::to_string(std::rand())); - list.push_back(p); - } - - RepoBSONBuilder builder; - builder.appendArrayPair("arrayPairTest", list, "first", "second"); - - RepoBSON testBson = builder.obj(); - - std::list > outList = testBson.getListStringPairField("arrayPairTest", "first", "second"); - - EXPECT_EQ(outList.size(), list.size()); - - auto inIt = list.begin(); - auto outIt = outList.begin(); - - for (; inIt != list.end(); ++inIt, ++outIt) - { - EXPECT_EQ(outIt->first, inIt->first); - EXPECT_EQ(outIt->second, inIt->second); - } - - //Ensure this doesn't crash and die. - builder.appendArrayPair("blah", std::list >(), "first", "second"); - -} - TEST(RepoBSONBuilderTest, appendBinary) { size_t binSize = 1024 * 1024;