diff --git a/app/models/zip_file/reader.rb b/app/models/zip_file/reader.rb index 762e248de..1b9cd3dcb 100644 --- a/app/models/zip_file/reader.rb +++ b/app/models/zip_file/reader.rb @@ -10,7 +10,7 @@ class ZipFile::Reader raise ArgumentError, "Cannot read directory entry: #{file_path}" if entry.filename.end_with?("/") if block_given? - yield ZipFile::Reader::ExtractorIO.new(entry, @io) + yield ZipFile::Reader::IO.new(entry, @io) else entry.extractor_from(@io).extract end diff --git a/app/models/zip_file/reader/extractor_io.rb b/app/models/zip_file/reader/io.rb similarity index 93% rename from app/models/zip_file/reader/extractor_io.rb rename to app/models/zip_file/reader/io.rb index 13640f445..cf28212aa 100644 --- a/app/models/zip_file/reader/extractor_io.rb +++ b/app/models/zip_file/reader/io.rb @@ -1,4 +1,4 @@ -class ZipFile::Reader::ExtractorIO +class ZipFile::Reader::IO def initialize(entry, io) @entry = entry @io = io diff --git a/app/models/zip_file/writer.rb b/app/models/zip_file/writer.rb index 144416c79..8e9665563 100644 --- a/app/models/zip_file/writer.rb +++ b/app/models/zip_file/writer.rb @@ -10,7 +10,7 @@ class ZipFile::Writer end def io - @counting_io ||= CountingIO.new(self) + @io ||= ZipFile::Writer::IO.new(self) end def stream_to(io) @@ -55,19 +55,4 @@ class ZipFile::Writer def streamer @streamer ||= ZipKit::Streamer.new(@output_io) end - - class CountingIO - def initialize(writer) - @writer = writer - end - - def write(data) - @writer.write(data) - end - - def <<(data) - write(data) - self - end - end end diff --git a/app/models/zip_file/writer/io.rb b/app/models/zip_file/writer/io.rb new file mode 100644 index 000000000..451dd0d07 --- /dev/null +++ b/app/models/zip_file/writer/io.rb @@ -0,0 +1,18 @@ +class ZipFile::Writer::IO + def initialize(writer) + @writer = writer + end + + def write(data) + @writer.write(data) + end + + def <<(data) + write(data) + self + end + + def size + @writer.byte_size + end +end diff --git a/test/models/zip_file_test.rb b/test/models/zip_file_test.rb new file mode 100644 index 000000000..257b9adf4 --- /dev/null +++ b/test/models/zip_file_test.rb @@ -0,0 +1,141 @@ +require "test_helper" + +class ZipFileTest < ActiveSupport::TestCase + test "writer io exposes size for S3 multipart upload detection" do + writer = ZipFile::Writer.new + + assert_respond_to writer.io, :size + assert_equal writer.byte_size, writer.io.size + end + + test "writer adds files with content" do + tempfile = Tempfile.new([ "test", ".zip" ]) + tempfile.binmode + + writer = ZipFile::Writer.new(tempfile) + writer.add_file("hello.txt", "Hello, World!") + writer.close + + assert writer.exists?("hello.txt") + assert_not writer.exists?("missing.txt") + end + + test "writer adds files with block" do + tempfile = Tempfile.new([ "test", ".zip" ]) + tempfile.binmode + + writer = ZipFile::Writer.new(tempfile) + writer.add_file("hello.txt") { |sink| sink.write("Hello, World!") } + writer.close + + assert writer.exists?("hello.txt") + end + + test "writer globs entries" do + tempfile = Tempfile.new([ "test", ".zip" ]) + tempfile.binmode + + writer = ZipFile::Writer.new(tempfile) + writer.add_file("docs/readme.txt", "Readme") + writer.add_file("docs/guide.txt", "Guide") + writer.add_file("images/logo.png", "PNG data") + writer.close + + assert_equal [ "docs/guide.txt", "docs/readme.txt" ], writer.glob("docs/*.txt") + assert_equal [ "images/logo.png" ], writer.glob("**/*.png") + end + + test "reader reads file content" do + tempfile = create_test_zip("hello.txt" => "Hello, World!") + + reader = ZipFile::Reader.new(tempfile) + content = reader.read("hello.txt") + + assert_equal "Hello, World!", content + end + + test "reader reads file with block" do + tempfile = create_test_zip("hello.txt" => "Hello, World!") + + reader = ZipFile::Reader.new(tempfile) + content = nil + reader.read("hello.txt") { |io| content = io.read } + + assert_equal "Hello, World!", content + end + + test "reader raises for missing file" do + tempfile = create_test_zip("hello.txt" => "Hello") + + reader = ZipFile::Reader.new(tempfile) + + assert_raises(ArgumentError) { reader.read("missing.txt") } + end + + test "reader checks file existence" do + tempfile = create_test_zip("hello.txt" => "Hello") + + reader = ZipFile::Reader.new(tempfile) + + assert reader.exists?("hello.txt") + assert_not reader.exists?("missing.txt") + end + + test "reader globs entries" do + tempfile = create_test_zip( + "docs/readme.txt" => "Readme", + "docs/guide.txt" => "Guide", + "images/logo.png" => "PNG" + ) + + reader = ZipFile::Reader.new(tempfile) + + assert_equal [ "docs/guide.txt", "docs/readme.txt" ], reader.glob("docs/*.txt") + end + + test "reader io provides size" do + tempfile = create_test_zip("hello.txt" => "Hello, World!") + + reader = ZipFile::Reader.new(tempfile) + reader.read("hello.txt") do |io| + assert_equal 13, io.size + end + end + + test "reader io supports rewind" do + tempfile = create_test_zip("hello.txt" => "Hello, World!") + + reader = ZipFile::Reader.new(tempfile) + reader.read("hello.txt") do |io| + first_read = io.read + io.rewind + second_read = io.read + + assert_equal first_read, second_read + end + end + + test "reader io tracks eof" do + tempfile = create_test_zip("hello.txt" => "Hello") + + reader = ZipFile::Reader.new(tempfile) + reader.read("hello.txt") do |io| + assert_not io.eof? + io.read + assert io.eof? + end + end + + private + def create_test_zip(files) + tempfile = Tempfile.new([ "test", ".zip" ]) + tempfile.binmode + + writer = ZipFile::Writer.new(tempfile) + files.each { |path, content| writer.add_file(path, content) } + writer.close + + tempfile.rewind + tempfile + end +end