|
26 | 26 | #include <bsoncxx/types/value.hpp>
|
27 | 27 | #include <mongocxx/client.hpp>
|
28 | 28 | #include <mongocxx/database.hpp>
|
| 29 | +#include <mongocxx/exception/gridfs_exception.hpp> |
29 | 30 | #include <mongocxx/exception/logic_error.hpp>
|
30 | 31 | #include <mongocxx/gridfs/bucket.hpp>
|
31 | 32 | #include <mongocxx/instance.hpp>
|
@@ -103,6 +104,217 @@ TEST_CASE("mongocxx::gridfs::bucket copy assignment operator", "[gridfs::bucket]
|
103 | 104 | }
|
104 | 105 | }
|
105 | 106 |
|
| 107 | +TEST_CASE("database::gridfs_bucket() throws error when options are invalid", "[gridfs::bucket]") { |
| 108 | + instance::current(); |
| 109 | + |
| 110 | + client client{uri{}}; |
| 111 | + database db = client["gridfs_bucket_error_invalid_options"]; |
| 112 | + |
| 113 | + options::gridfs::bucket bucket_options; |
| 114 | + |
| 115 | + SECTION("empty bucket name") { |
| 116 | + bucket_options.bucket_name(""); |
| 117 | + } |
| 118 | + SECTION("zero chunk size") { |
| 119 | + bucket_options.chunk_size_bytes(0); |
| 120 | + } |
| 121 | + SECTION("negative chunk size") { |
| 122 | + bucket_options.chunk_size_bytes(-1); |
| 123 | + } |
| 124 | + |
| 125 | + REQUIRE_THROWS_AS(db.gridfs_bucket(bucket_options), logic_error); |
| 126 | +} |
| 127 | + |
| 128 | +TEST_CASE("uploading throws error when options are invalid", "[gridfs::bucket]") { |
| 129 | + instance::current(); |
| 130 | + |
| 131 | + client client{uri{}}; |
| 132 | + database db = client["gridfs_upload_error_invalid_options"]; |
| 133 | + gridfs::bucket bucket = db.gridfs_bucket(); |
| 134 | + |
| 135 | + options::gridfs::upload upload_options; |
| 136 | + |
| 137 | + SECTION("zero chunk size") { |
| 138 | + upload_options.chunk_size_bytes(0); |
| 139 | + } |
| 140 | + SECTION("negative chunk size") { |
| 141 | + upload_options.chunk_size_bytes(-1); |
| 142 | + } |
| 143 | + |
| 144 | + REQUIRE_THROWS_AS(bucket.open_upload_stream("filename", upload_options), logic_error); |
| 145 | + REQUIRE_THROWS_AS( |
| 146 | + bucket.open_upload_stream_with_id( |
| 147 | + bsoncxx::types::value{bsoncxx::types::b_int32{0}}, "filename", upload_options), |
| 148 | + logic_error); |
| 149 | + |
| 150 | + std::istringstream iss{"foo"}; |
| 151 | + REQUIRE_THROWS_AS(bucket.upload_from_stream("filename", &iss, upload_options), logic_error); |
| 152 | + REQUIRE_THROWS_AS( |
| 153 | + bucket.upload_from_stream_with_id( |
| 154 | + bsoncxx::types::value{bsoncxx::types::b_int32{0}}, "filename", &iss, upload_options), |
| 155 | + logic_error); |
| 156 | +} |
| 157 | + |
| 158 | +TEST_CASE("downloading throws error when files document is corrupt", "[gridfs::bucket]") { |
| 159 | + instance::current(); |
| 160 | + |
| 161 | + client client{uri{}}; |
| 162 | + database db = client["gridfs_files_doc_corrupt"]; |
| 163 | + gridfs::bucket bucket = db.gridfs_bucket(); |
| 164 | + |
| 165 | + db["fs.files"].drop(); |
| 166 | + |
| 167 | + const std::int32_t k_expected_chunk_size_bytes = 255 * 1024; // Default chunk size. |
| 168 | + const std::int64_t k_expected_file_length = 1024 * 1024; |
| 169 | + stdx::optional<bsoncxx::types::value> chunk_size{ |
| 170 | + bsoncxx::types::value{bsoncxx::types::b_int32{k_expected_chunk_size_bytes}}}; |
| 171 | + stdx::optional<bsoncxx::types::value> length{ |
| 172 | + bsoncxx::types::value{bsoncxx::types::b_int64{k_expected_file_length}}}; |
| 173 | + bool expect_success = false; |
| 174 | + |
| 175 | + // Tests for invalid chunk size. |
| 176 | + SECTION("zero chunk size") { |
| 177 | + chunk_size = bsoncxx::types::value{bsoncxx::types::b_int32{0}}; |
| 178 | + } |
| 179 | + SECTION("negative chunk size") { |
| 180 | + chunk_size = bsoncxx::types::value{bsoncxx::types::b_int32{-1}}; |
| 181 | + } |
| 182 | + SECTION("chunk size too large") { |
| 183 | + const std::int32_t k_max_document_size = 16 * 1024 * 1024; |
| 184 | + chunk_size = bsoncxx::types::value{bsoncxx::types::b_int32{k_max_document_size + 1}}; |
| 185 | + } |
| 186 | + SECTION("chunk size of wrong type") { |
| 187 | + chunk_size = bsoncxx::types::value{bsoncxx::types::b_utf8{"invalid"}}; |
| 188 | + } |
| 189 | + SECTION("missing chunk size") { |
| 190 | + chunk_size = stdx::nullopt; |
| 191 | + } |
| 192 | + |
| 193 | + // Tests for invalid length. |
| 194 | + SECTION("negative length") { |
| 195 | + length = bsoncxx::types::value{bsoncxx::types::b_int64{-1}}; |
| 196 | + } |
| 197 | + SECTION("invalid length") { |
| 198 | + length = bsoncxx::types::value{bsoncxx::types::b_utf8{"invalid"}}; |
| 199 | + } |
| 200 | + SECTION("missing length") { |
| 201 | + length = stdx::nullopt; |
| 202 | + } |
| 203 | + |
| 204 | + // Test for too many chunks. |
| 205 | + SECTION("too many chunks") { |
| 206 | + chunk_size = bsoncxx::types::value{bsoncxx::types::b_int32{1}}; |
| 207 | + length = bsoncxx::types::value{bsoncxx::types::b_int64{ |
| 208 | + static_cast<std::int64_t>(std::numeric_limits<std::int32_t>::max()) + 1}}; |
| 209 | + } |
| 210 | + |
| 211 | + // Test for valid length and chunk size. |
| 212 | + SECTION("valid length and chunk size") { |
| 213 | + expect_success = true; |
| 214 | + } |
| 215 | + |
| 216 | + { |
| 217 | + bsoncxx::builder::basic::document files_doc; |
| 218 | + files_doc.append(kvp("_id", 0)); |
| 219 | + if (length) { |
| 220 | + files_doc.append(kvp("length", *length)); |
| 221 | + } |
| 222 | + if (chunk_size) { |
| 223 | + files_doc.append(kvp("chunkSize", *chunk_size)); |
| 224 | + } |
| 225 | + |
| 226 | + db["fs.files"].insert_one(files_doc.extract()); |
| 227 | + } |
| 228 | + |
| 229 | + auto open_download_stream = [&bucket]() { |
| 230 | + return bucket.open_download_stream(bsoncxx::types::value{bsoncxx::types::b_int32{0}}); |
| 231 | + }; |
| 232 | + |
| 233 | + if (expect_success) { |
| 234 | + gridfs::downloader downloader = open_download_stream(); |
| 235 | + REQUIRE(downloader.chunk_size() == k_expected_chunk_size_bytes); |
| 236 | + REQUIRE(downloader.file_length() == k_expected_file_length); |
| 237 | + } else { |
| 238 | + REQUIRE_THROWS_AS(open_download_stream(), gridfs_exception); |
| 239 | + } |
| 240 | +} |
| 241 | + |
| 242 | +TEST_CASE("downloading throws error when chunks document is corrupt", "[gridfs::bucket]") { |
| 243 | + instance::current(); |
| 244 | + |
| 245 | + client client{uri{}}; |
| 246 | + database db = client["gridfs_chunk_doc_corrupt"]; |
| 247 | + gridfs::bucket bucket = db.gridfs_bucket(); |
| 248 | + |
| 249 | + db["fs.files"].drop(); |
| 250 | + db["fs.chunks"].drop(); |
| 251 | + |
| 252 | + const std::uint8_t k_expected_data_byte = 'd'; |
| 253 | + |
| 254 | + stdx::optional<bsoncxx::types::value> n{bsoncxx::types::value{bsoncxx::types::b_int32{0}}}; |
| 255 | + stdx::optional<bsoncxx::types::value> data{bsoncxx::types::value{ |
| 256 | + bsoncxx::types::b_binary{bsoncxx::binary_sub_type::k_binary, 1, &k_expected_data_byte}}}; |
| 257 | + bool expect_success = false; |
| 258 | + |
| 259 | + // Tests for invalid n. |
| 260 | + SECTION("missing n") { |
| 261 | + n = stdx::nullopt; |
| 262 | + } |
| 263 | + SECTION("wrong type for n") { |
| 264 | + n = bsoncxx::types::value{bsoncxx::types::b_int64{0}}; |
| 265 | + } |
| 266 | + |
| 267 | + // Tests for invalid data. |
| 268 | + SECTION("missing data") { |
| 269 | + data = stdx::nullopt; |
| 270 | + } |
| 271 | + SECTION("wrong type for data") { |
| 272 | + data = bsoncxx::types::value{bsoncxx::types::b_bool{true}}; |
| 273 | + } |
| 274 | + |
| 275 | + // Test for valid n and data. |
| 276 | + SECTION("valid n and data") { |
| 277 | + expect_success = true; |
| 278 | + } |
| 279 | + |
| 280 | + { |
| 281 | + bsoncxx::builder::basic::document chunk_doc; |
| 282 | + chunk_doc.append(kvp("files_id", 0)); |
| 283 | + if (n) { |
| 284 | + chunk_doc.append(kvp("n", *n)); |
| 285 | + } |
| 286 | + if (data) { |
| 287 | + chunk_doc.append(kvp("data", *data)); |
| 288 | + } |
| 289 | + |
| 290 | + db["fs.chunks"].insert_one(chunk_doc.extract()); |
| 291 | + db["fs.files"].insert_one( |
| 292 | + make_document(kvp("_id", 0), kvp("length", 1), kvp("chunkSize", 1))); |
| 293 | + } |
| 294 | + |
| 295 | + gridfs::downloader downloader = |
| 296 | + bucket.open_download_stream(bsoncxx::types::value{bsoncxx::types::b_int32{0}}); |
| 297 | + auto downloader_read_one = [&downloader]() { |
| 298 | + stdx::optional<std::uint8_t> result; |
| 299 | + std::uint8_t byte; |
| 300 | + std::size_t bytes_downloaded = downloader.read(1, &byte); |
| 301 | + if (bytes_downloaded > 0) { |
| 302 | + result = byte; |
| 303 | + } |
| 304 | + return result; |
| 305 | + }; |
| 306 | + |
| 307 | + if (expect_success) { |
| 308 | + stdx::optional<std::uint8_t> result = downloader_read_one(); |
| 309 | + REQUIRE(result); |
| 310 | + REQUIRE(*result == k_expected_data_byte); |
| 311 | + result = downloader_read_one(); |
| 312 | + REQUIRE(!result); |
| 313 | + } else { |
| 314 | + REQUIRE_THROWS_AS(downloader_read_one(), gridfs_exception); |
| 315 | + } |
| 316 | +} |
| 317 | + |
106 | 318 | // Add an arbitrary GridFS file with a specified length, chunk size, and id to a database's default
|
107 | 319 | // GridFS bucket (i.e. the "fs.files" and "fs.chunks" collections).
|
108 | 320 | //
|
|
0 commit comments