require 'spec_helper' describe FontProcessor::ProcessFontJob do before(:each) do @status_key = "status:/a00000000000000000000001/20/1/woffsvgswf" @font_base_id = "a00000000000000000000001" @request_data = '{"version":"0.3","request_type":"web_processing_request","font_base_id":"a00000000000000000000001","fpv":"21","charset":{"charset_id":"1","unicode":"0x0020..0x007e,0x00a0..0xffff","features":"ALL"},"formats":{"process_original":true,"convert":true,"derivatives":["dyna_base","woff","woff2","inst","svg","swf"]}}' @no_convert = '{"version":"0.3","request_type":"web_processing_request","font_base_id":"a00000000000000000000001","fpv":"21","charset":{"charset_id":"1","unicode":"0x0020..0x007e,0x00a0..0xffff","features":"ALL"},"formats":{"process_original":true,"convert":false,"derivatives":["dyna_base","woff","woff2","inst","svg","swf"]}}' @charset_id = "1" @fpv = "21" @directory = "/tmp/#{@font_base_id}-#{@charset_id}" end subject { FontProcessor::ProcessFontJob } describe "integration tests" do it "can successfully process a font" do expect(subject).to receive(:fetch_files).ordered expect(subject).to receive(:process_files).ordered expect(subject).to receive(:upload_metadata).ordered expect(subject).to receive(:upload_files).ordered redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "success") expect(redis).to receive(:expire).with(@status_key, 86400) expect(subject).to receive(:redis).ordered.twice.and_return(redis) subject.perform(@status_key, @request_data) end it "can successfully process a font without conversion" do expect(subject).to receive(:fetch_files).ordered expect(subject).to receive(:process_files).ordered expect(subject).to receive(:upload_metadata).ordered expect(subject).to receive(:upload_files).ordered redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "success") expect(redis).to receive(:expire).with(@status_key, 86400) expect(subject).to receive(:redis).ordered.twice.and_return(redis) subject.perform(@status_key, @no_convert) end it "can handle an error while processing the font" do expect(subject).to receive(:fetch_files).ordered expect(subject).to receive(:process_files).ordered.and_raise(RuntimeError) redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).ordered.twice.and_return(redis) expect { subject.perform(@status_key, @request_data) }.to raise_exception(Exception) end it "can handle an error while retrieving the font data" do expect(subject).to receive(:fetch_files).and_raise(FontBase::Error) redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) expect { subject.perform(@status_key, @request_data) }.to raise_exception(FontBase::Error) end it "can handle an error while uploading the processed font metadata" do expect(subject).to receive(:fetch_files).ordered expect(subject).to receive(:process_files).ordered expect(subject).to receive(:upload_metadata).and_raise(FontBase::Error) redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).ordered.twice.and_return(redis) expect { subject.perform(@status_key, @request_data) }.to raise_exception(FontBase::Error) end it "can handle an error while uploading processed font data" do expect(subject).to receive(:fetch_files).ordered expect(subject).to receive(:process_files).ordered expect(subject).to receive(:upload_metadata).ordered expect(subject).to receive(:upload_files).ordered.and_raise(Aws::S3::Errors::ServiceError.new('message', 'service error')) redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).ordered.twice.and_return(redis) expect { subject.perform(@status_key, @request_data) }.to raise_exception(Aws::S3::Errors::ServiceError) end it "can handle an error for an invalid request type" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req_type = JSON.parse(@request_data) bad_req_type['request_type'] = "desktop_processing_request" expect { subject.perform(@status_key, bad_req_type) }.to raise_exception(RuntimeError) end it "can handle an error for a request without charset data" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req = JSON.parse(@request_data) bad_req.delete('charset') expect { subject.perform(@status_key, bad_req) }.to raise_exception(RuntimeError, /No charset in JSON Request/) end it "can handle an error for a request without charset_id in the charset data" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req = JSON.parse(@request_data) bad_req['charset'].delete('charset_id') expect { subject.perform(@status_key, bad_req) }.to raise_exception(RuntimeError, /No charset_id set in JSON Request's charset/) end it "can handle an error for a request without unicode in the charset data" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req = JSON.parse(@request_data) bad_req['charset'].delete('unicode') expect { subject.perform(@status_key, bad_req) }.to raise_exception(RuntimeError, /No unicode set in JSON Request's charset/) end it "can handle an error for a request without features in the charset data" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req = JSON.parse(@request_data) bad_req['charset'].delete('features') expect { subject.perform(@status_key, bad_req) }.to raise_exception(RuntimeError, /No features set in JSON Request's charset/) end it "can handle an error for a request without an fpv" do redis = double("Redis") expect(redis).to receive(:set).with(@status_key, "error") expect(redis).to receive(:expire).with(@status_key, 30) expect(subject).to receive(:redis).twice.and_return(redis) bad_req = JSON.parse(@request_data) bad_req.delete('fpv') expect { subject.perform(@status_key, bad_req) }.to raise_exception(RuntimeError, /No fpv set on JSON Request/) end end describe "fetching files" do before(:each) do FileUtils.mkdir_p(@directory) end after(:each) do FileUtils.rm_r(@directory) end it "successfully handles a single PostScript file" do client = double("FontBase::Client") expect(client).to receive(:get_files).with(@font_base_id).and_return({"cff" => "cff-data"}) expect(subject).to receive(:client).and_return(client) subject.fetch_files(@font_base_id, @directory) expect(File.read(File.join(@directory, "original.otf"))).to eq "cff-data" end it "successfully handles a single TrueType file" do client = double("FontBase::Client") expect(client).to receive(:get_files).with(@font_base_id).and_return({"ttf" => "ttf-data"}) expect(subject).to receive(:client).and_return(client) subject.fetch_files(@font_base_id, @directory) expect(File.read(File.join(@directory, "original.ttf"))).to eq "ttf-data" end it "successfully handles a PostScript and TrueType file" do client = double("FontBase::Client") expect(client).to receive(:get_files).with(@font_base_id).and_return({"ttf" => "ttf-data", "cff" => "cff-data"}) expect(subject).to receive(:client).and_return(client) subject.fetch_files(@font_base_id, @directory) expect(File.read(File.join(@directory, "original.ttf"))).to eq "ttf-data" expect(File.read(File.join(@directory, "original.otf"))).to eq "cff-data" end end describe "processing files" do before(:each) do FileUtils.mkdir_p(@directory) end after(:each) do FileUtils.rm_r(@directory) end it "successfully handles a PostScript file" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "postscript.otf"), File.join(@directory, "original.otf")) parsed_data = JSON.parse(@request_data) subject.process_files(@font_base_id, parsed_data['charset'], parsed_data['formats'], @directory) iterator = FontProcessor::ProcessedFontIterator.new(@font_base_id, @charset_id, @fpv, @directory, true) expect(iterator.valid?).to be true expect(File.exist?(File.join(@directory, "charset-#{@charset_id}-otf.otf"))).to be true end it "successfully processes a TrueType file" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "truetype.otf"), File.join(@directory, "original.ttf")) parsed_data = JSON.parse(@request_data) subject.process_files(@font_base_id, parsed_data['charset'], parsed_data['formats'], @directory) iterator = FontProcessor::ProcessedFontIterator.new(@font_base_id, @charset_id, @fpv, @directory, true) expect(iterator.valid?).to be true expect(File.exist?(File.join(@directory, "charset-#{@charset_id}-otf.ttf"))).to be true end it "successfully processes a PostScript file if given both" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "postscript.otf"), File.join(@directory, "original.otf")) FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "truetype.otf"), File.join(@directory, "original.ttf")) parsed_data = JSON.parse(@request_data) subject.process_files(@font_base_id, parsed_data['charset'], parsed_data['formats'], @directory) iterator = FontProcessor::ProcessedFontIterator.new(@font_base_id, @charset_id, @fpv, @directory, true) expect(iterator.valid?).to be true expect(File.exist?(File.join(@directory, "charset-#{@charset_id}-otf.otf"))).to be true end it "successfully handles a PostScript file" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "postscript.otf"), File.join(@directory, "original.otf")) parsed_data = JSON.parse(@request_data) parsed_data['formats']['convert'] = false subject.process_files(@font_base_id, parsed_data['charset'], parsed_data['formats'], @directory) iterator = FontProcessor::ProcessedFontIterator.new(@font_base_id, @charset_id, @fpv, @directory, false) expect(iterator.valid?).to be true expect(File.exist?(File.join(@directory, "charset-#{@charset_id}-otf.otf"))).to be true end it "successfully subsets a Truetype file" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "truetype.otf"), File.join(@directory, "original.ttf")) charset_data = { "charset_id" => "3", "features" => "NONE", "unicode" => "32..32,65..90,97..122" } format_data = { "process_original" => true, "convert" => true, "derivatives" => [ "dyna_base", "woff", "woff2", "inst", "svg", "swf" ] } subject.process_files(@font_base_id, charset_data, format_data, @directory, false) iterator = FontProcessor::ProcessedFontIterator.new(@font_base_id, charset_data['charset_id'], @fpv, @directory, true) expect(iterator.valid?).to be true file = File.join(@directory, "charset-3-otf.ttf") # process_files calls into generate_char_set, then into processor.generate_internal_char_set # This method now automatically adds nb-space and soft-hyphen, so we need to expect # U+A0 (160) expect(FontProcessor::FontFile.new(file).unicode_ranges).to eq [32, 65..90, 97..122, 160] end it "throws MissingFilesException when neither are found" do FileUtils.cp(File.join(File.dirname(__FILE__), "..", "..", "fixtures", "postscript.otf"), @directory) expect { subject.select_preferred_original(@directory)}.to raise_error(FontProcessor::MissingFilesException) end end describe "uploading metadata" do it "successfully updates the metadata in fontbase" do fake_font_file = double("FontFile", :unicode => "unicode data", :metrics => "metrics data", :names => "names data", :glyph_count => "1", :optical_size => "180", :features => "kern,size") expected_metadata = { :glyphs => "unicode data", :metrics => "metrics data", :names => "names data", :glyph_count => "1", :optical_size => "180", :features => "kern,size" } client = double("FontBase::Client") expect(client).to receive(:set_font_processing_version_attributes).with(@font_base_id, @fpv, expected_metadata).and_return({"ttf" => "ttf-data"}) expect(subject).to receive(:client).and_return(client) subject.upload_metadata(@fpv, @font_base_id, fake_font_file) end it "successfully updates the metadata in fontbase with no optical size" do fake_font_file = double("FontFile", :unicode => "unicode data", :metrics => "metrics data", :names => "names data", :glyph_count => "1", :optical_size => nil, :features => "kern") expected_metadata = { :glyphs => "unicode data", :metrics => "metrics data", :names => "names data", :glyph_count => "1", :optical_size => nil, :features => "kern" } client = double("FontBase::Client") expect(client).to receive(:set_font_processing_version_attributes).with(@font_base_id, @fpv, expected_metadata).and_return({"ttf" => "ttf-data"}) expect(subject).to receive(:client).and_return(client) subject.upload_metadata(@fpv, @font_base_id, fake_font_file) end end describe "uploading files" do it "attempts to upload all valid files" do iterator = [["file1", "key"]] expect(FontProcessor::ProcessedFontIterator).to receive(:new).and_return(iterator) expect(File).to receive(:binread).with("file1").and_return("") expect_any_instance_of(Aws::S3::Client).to receive(:put_object).with(key: "key", body: "", bucket: FontProcessor::Config.s3_processed_bucket) parsed_data = JSON.parse(@request_data) subject.upload_files(parsed_data['fpv'], parsed_data['font_base_id'], parsed_data['charset']['charset_id'], parsed_data['formats'], @directory) end end end