%PDF- %PDF-
Direktori : /home/opti/optisoftcom/.cpanm/work/1544535406.20768/MongoDB-v2.0.2/t/ |
Current File : /home/opti/optisoftcom/.cpanm/work/1544535406.20768/MongoDB-v2.0.2/t/gridfsbucket.t |
# Copyright 2015 - present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; use Test::More; use Test::Fatal; use Test::Deep; use IO::File; use File::Compare; use Encode; use Time::HiRes qw/usleep/; use MongoDB; use MongoDB::GridFSBucket; use BSON::Time; use Path::Tiny; use lib "t/lib"; use MongoDBTest qw/skip_unless_mongod build_client get_test_db/; skip_unless_mongod(); my $testdb = get_test_db( build_client() ); my $txtfile = "t/data/gridfs/input.txt"; my $pngfile = "t/data/gridfs/data.bin"; my $bigfile = "t/data/gridfs/big.txt"; my ( $img_id, $img_meta ); my $img_length = -s $pngfile; my $img_md5 = path($pngfile)->digest("MD5"); my ( $txt_id, $txt_meta ); my $txt_length = -s $txtfile; my $txt_md5 = path($txtfile)->digest("MD5"); my ( $big_id, $big_meta ); my $big_length = -s $bigfile; my $big_md5 = path($bigfile)->digest("MD5"); sub setup_gridfs { my $bucket = $testdb->get_gridfsbucket; $bucket->drop; my $img = new IO::File( $pngfile, "r" ) or die $!; binmode($img); $img_id = $bucket->upload_from_stream( 'data.bin', $img ); $img->close; my $txt = new IO::File( $txtfile, "r" ) or die $!; binmode($txt); $txt_id = $bucket->upload_from_stream( 'input.txt', $txt ); close $txt; my $big = new IO::File( $bigfile, "r" ) or die $!; binmode($big); $big_id = $bucket->upload_from_stream( 'big.txt', $big ); close $big; } # options { my $bucket = $testdb->get_gridfsbucket; cmp_deeply( $bucket->read_preference, $testdb->read_preference, 'read preference' ); cmp_deeply( $bucket->write_concern, $testdb->write_concern, 'write concern' ); is( $bucket->bucket_name, 'fs', 'default bucket name' ); is( $bucket->chunk_size_bytes, 255 * 1024, 'default chunk size bytes' ); } # test file upload { my $dumb_str = path($txtfile)->slurp_raw; my $bucket = $testdb->get_gridfsbucket; open( my $file, '<:raw', $txtfile ) or die $!; ok( my $id = $bucket->upload_from_stream( 'input.txt', $file ), 'upload small file' ); my $time = BSON::Time->new(); close $file; my @chunks = $bucket->_chunks->find( { files_id => $id } )->result->all; is( scalar @chunks, 1, 'upload small file has 1 chunk' ); my $chunk = shift @chunks; is( $chunk->{'n'}, 0, 'upload small file chunk n' ); is( $chunk->{'data'}, $dumb_str, 'upload small file data' ); ok( my $filedoc = $bucket->_files->find_id($id), 'upload small file files document' ); is( $filedoc->{'md5'}, $txt_md5, 'upload small file md5' ); is( $filedoc->{'length'}, $txt_length, 'upload small file length' ); is( $filedoc->{'filename'}, 'input.txt', 'upload small file length' ); ok( $time->epoch - $filedoc->{'uploadDate'}->epoch < 10, 'upload small file uploadDate' ); open( $file, '<:raw', $pngfile ) or die $!; binmode($file); ok( $id = $bucket->upload_from_stream( 'data.bin', $file, { metadata => { airspeed_velocity => '11m/s' }, content_type => 'data.bin', aliases => ['screenshot.png'], } ), 'upload large file' ); $time = BSON::Time->new(); seek $file, 0, 0; my $chunks = $bucket->_chunks->find( { files_id => $id }, { sort => { n => 1 } } )->result; my $n = 0; subtest 'upload large file' => sub { while ( $chunks->has_next ) { $chunk = $chunks->next; is( $chunk->{'n'}, $n, "upload large file chunk $n n" ); read $file, $dumb_str, $bucket->chunk_size_bytes; is( $chunk->{'data'}, $dumb_str, "upload large file chunk $n data" ); $n += 1; } }; ok( eof $file, 'upload large file whole file' ); close $file; ok( $filedoc = $bucket->_files->find_id($id), 'upload large file files document' ); is( $filedoc->{'md5'}, $img_md5, 'upload large file md5' ); is( $filedoc->{'length'}, $img_length, 'upload large file length' ); is( $filedoc->{'filename'}, 'data.bin', 'upload large file filename' ); ok( $time->epoch - $filedoc->{'uploadDate'}->epoch < 10, 'upload large file uploadDate' ); cmp_deeply( $filedoc->{metadata}, { airspeed_velocity => str('11m/s') }, 'upload large file metadta' ); is( $filedoc->{'contentType'}, 'data.bin', 'upload large file content_type' ); cmp_deeply( $filedoc->{aliases}, [str('screenshot.png')], 'upload large file aliases' ); } # test file upload with custom id { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; # upload_from_stream_with_id() open( my $file, '<:raw', $txtfile ) or die $!; $bucket->upload_from_stream_with_id( 5, "file_5.txt", $file ); close $file; my $doc = $bucket->find_id(5); is( $doc->{"md5"}, $txt_md5, "upload custom id md5" ); is( $doc->{"length"}, $txt_length, "upload custom id length" ); is( $doc->{"filename"}, "file_5.txt", "upload custom id filename" ); # open_upload_stream_with_id() my $uploadstream = $bucket->open_upload_stream_with_id( 6, "file_6.txt" ); $uploadstream->print( "a" x 12 ); $uploadstream->print( "b" x 8 ); my $doc2 = $uploadstream->close; is( $uploadstream->id, 6, "created file has correct custom file id" ); my $doc3 = $bucket->find_id(6); for my $k ( sort keys %$doc2 ) { is ( $doc2->{$k}, $doc3->{$k}, "$k" ); } } # test file upload without md5 subtest "disable_md5" => sub { setup_gridfs; my $bucket = $testdb->get_gridfsbucket( {disable_md5 => 1 } ); # upload_from_stream_with_id() open( my $file, '<:raw', $txtfile ) or die $!; $bucket->upload_from_stream_with_id( 5, "file_5.txt", $file ); close $file; my $doc = $bucket->find_id(5); ok( !exists $doc->{"md5"}, "upload_from_stream_with_id: md5 omitted" ); # open_upload_stream_with_id() my $uploadstream = $bucket->open_upload_stream_with_id( 6, "file_6.txt" ); $uploadstream->print( "a" x 12 ); $uploadstream->print( "b" x 8 ); my $doc2 = $uploadstream->close; my $doc3 = $bucket->find_id(6); ok( !exists $doc2->{"md5"}, "open_upload_stream_with_id: md5 omitted in returned doc" ); ok( !exists $doc3->{"md5"}, "open_upload_stream_with_id: md5 omitted in uploaded doc" ); }; # delete { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; $bucket->delete($img_id); is( $bucket->_files->find_id($img_id), undef, 'bucket delete files' ); is( $bucket->_chunks->find_id($img_id), undef, 'bucket delete chunks' ); # should throw error if file does not exist my $error; like( exception { $bucket->delete('nonsense') }, qr/FileNotFound: no file found for id .+/, 'delete nonexistant file', ); } # find { setup_gridfs; my $bucket = $testdb->gfs; my $results = $bucket->find( { length => $img_meta->{'length'} } ); my $file = $results->next; is( $file->{'length'}, $img_meta->{'length'}, "found file length" ); ok( !$results->has_next, "only one document found" ); } # drop { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; $bucket->drop; is( $bucket->_files->find_one, undef, "drop leaves files empty" ); is( $bucket->_chunks->find_one, undef, "drop leaves chunks empty" ); } # index creation { my $bucket = $testdb->get_gridfsbucket; $bucket->drop; $bucket = $testdb->get_gridfsbucket; cmp_deeply( [ $bucket->_chunks->indexes->list->all ], [], "new bucket doesn't create indexes" ); my $img = new IO::File( $pngfile, "r" ) or die $!; binmode($img); $img_id = $bucket->upload_from_stream( 'data.bin', $img ); $img->close; my %files_idx = map { $_->{name} => $_ } $bucket->_files->indexes->list->all; my %chunks_idx = map { $_->{name} => $_ } $bucket->_chunks->indexes->list->all; my $idx; $idx = $files_idx{"filename_1_uploadDate_1"}; ok( $idx && $idx->{unique}, "unique files index on filename+uploadDate created" ) or diag explain $idx; $idx = $idx = $chunks_idx{"files_id_1_n_1"}; ok( $idx && $idx->{unique}, "unique chunks index on files_id+n created" ) or diag explain $idx; # subsequent writes should not trigger index creation no warnings 'redefine'; local *MongoDB::IndexView::create_one = sub { die "re-indexing shouldn't be called" }; # next insert should not recreate index eval { $img = new IO::File( $pngfile, "r" ) or die $!; binmode($img); $img_id = $bucket->upload_from_stream( 'img2.png', $img ); $img->close; }; is( $@, "", "upload on same bucket doesn't reindex" ); # even new gridfs object should not recreate index on first upload eval { $bucket = $testdb->get_gridfsbucket; $img = new IO::File( $pngfile, "r" ) or die $!; binmode($img); $img_id = $bucket->upload_from_stream( 'img3.png', $img ); $img->close; }; is( $@, "", "upload on same bucket doesn't reindex" ); } # download_to_stream { setup_gridfs; my $tmp = Path::Tiny->tempfile; my $tmp_fh = $tmp->openw_raw; my $bucket = $testdb->get_gridfsbucket; $bucket->download_to_stream( $big_id, $tmp_fh ); isnt( fileno $tmp_fh, undef, 'download_to_stream does not close file handle' ); close $tmp_fh; is( compare( $bigfile, "$tmp" ), 0, 'download_to_stream writes to disk' ); } sub _hexify { my ($str) = @_; $str =~ s{([^[:graph:]])}{sprintf("\\x{%02x}",ord($1))}ge; return $str; } sub text_is { my ($got, $exp, $label) = @_; local $Test::Builder::Level = $Test::Builder::Level + 1; is( _hexify($got), _hexify($exp), $label ); } # open_download_stream { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; my $dl_stream; my $str; $dl_stream = $bucket->open_download_stream($txt_id); $dl_stream->read( $str, 4 ); text_is( $str, "abc\n", 'simple read' ); $dl_stream->read( $str, 1, 10 ); text_is( $str, "abc\n\0\0\0\0\0\0\n", 'read with null byte padding' ); $dl_stream->read( $str, 3, -10 ); text_is( $str, "azyw", 'read with negative offset' ); $dl_stream->read( $str, 1, -999 ); text_is( $str, "\n", 'read with large negative offset' ); $dl_stream = $bucket->open_download_stream($txt_id); text_is( scalar $dl_stream->readline, "abc\n", 'readline 1' ); text_is( scalar $dl_stream->readline, "\n", 'readline 2' ); text_is( scalar $dl_stream->readline, "zyw\n", 'readline 3' ); $dl_stream = $bucket->open_download_stream($txt_id); my @arr = $dl_stream->readline; cmp_deeply( \@arr, [ "abc\n", "\n", "zyw\n" ], 'readline in ltext_ist context' ); $dl_stream = $bucket->open_download_stream($txt_id); { local $/ = undef; $str = $dl_stream->readline; text_is( $str, "abc\n\nzyw\n", 'readline slurp mode' ); } my $tmp = Path::Tiny->tempfile; my $tmp_fh = $tmp->openw_raw; $dl_stream = $bucket->open_download_stream($big_id); my $data; while ( $dl_stream->read( $data, -s $bigfile ) ) { print $tmp_fh $data; } close $tmp_fh; is( compare( $bigfile, $tmp ), 0, 'DownloadStream complex read' ); } # open_download_stream fh magic { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; my $dl_stream = $bucket->open_download_stream($txt_id); my $fh = $dl_stream->fh; is( fileno($fh), -1, "fileno on fh returns -1" ); my $result; read $fh, $result, 3; is( $result, 'abc', 'simple fh read' ); $dl_stream = $bucket->open_download_stream($img_id); $fh = $dl_stream->fh; open( my $png_fh, '<:raw', $pngfile ); is( compare( $fh, $png_fh ), 0, 'complex fh read' ); close $png_fh; $dl_stream = $bucket->open_download_stream($big_id); $fh = $dl_stream->fh; open( my $big_fh, '<:raw', $bigfile ); my $ok = 1; while ( my $line = <$fh> ) { if ( $line ne <$big_fh> ) { is( $line, <$big_fh>, 'complex fh readline' ); $ok = 0; last; } } ok( $ok, "complex fh readline as expected" ); do { local $/; <$fh> }; ok( eof($fh), "EOF" ); close $fh; is( fileno($fh), undef, "fileno on closed fh returns undef" ); close $big_fh; } # DownloadStream close { setup_gridfs; no warnings; my $bucket = $testdb->get_gridfsbucket; my $fh = $bucket->open_download_stream($big_id)->fh; ok( scalar <$fh>, 'fh readline before close' ); close $fh; is( scalar <$fh>, undef, 'fh readline after close' ); } # Custom chunk sizes { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; my $uploadstream = $bucket->open_upload_stream( 'customChunks.txt', { chunk_size_bytes => 12, }, ); $uploadstream->print( 'a' x 12 ); $uploadstream->print( 'b' x 8 ); my $doc = $uploadstream->close; my $id = $uploadstream->id; my $doc2 = $bucket->find_id($id); for my $k ( sort keys %$doc ) { is ( $doc->{$k}, $doc2->{$k}, "$k" ); } is( $bucket->_chunks->count_documents( { files_id => $id } ), 2, 'custom chunk size num chunks' ); my @results = $bucket->_chunks->find( { files_id => $id } )->all; is( $results[0]->{data}, 'a' x 12, 'custom chunk size boundries 1' ); is( $results[1]->{data}, 'b' x 8, 'custom chunk size boundries 2' ); my $str; is( $bucket->open_download_stream($id)->read( $str, 100 ), 20, 'custom chunk size read' ); is( $str, 'a' x 12 . 'b' x 8, 'custom chunk size download' ); } # Unicode { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; my $uploadstream = $bucket->open_upload_stream( 'unicode.txt', { chunk_size_bytes => 12 }, ); my $fh = $uploadstream->fh; is( fileno($fh), -1, "fileno on open fh returns -1" ); my $beer = "\x{1f37a}"; my $teststr = 'abcdefghijk' . $beer; my $testlen = length Encode::encode_utf8($teststr); $uploadstream->print($teststr); $uploadstream->close; is( fileno($fh), undef, "fileno on closed fh returns undef" ); my $id = $uploadstream->id; is( $bucket->_chunks->count_documents( { files_id => $id } ), 2, 'unicode upload' ); is( $bucket->_files->find_id($id)->{length}, $testlen, 'unicode upload file length' ); my $str; is( $bucket->open_download_stream($id)->read( $str, 100 ), $testlen, 'unicode read length' ); $str = Encode::decode_utf8($str); is( $str, $teststr, 'unicode read content' ); } # High resolution upload date { setup_gridfs; my $bucket = $testdb->get_gridfsbucket; my $upload1 = $bucket->open_upload_stream( 'same.txt' ); my $upload2 = $bucket->open_upload_stream( 'same.txt' ); $_->print("Hello World") for $upload1, $upload2; $upload1->close ; usleep(2000); # get past next millisecond for unique upload time eval { $upload2->close }; is( $@, '', "Uploads >1 ms apart are allowed" ); } done_testing;