NotionRubyMapping のアップデート(18) : hkob の雑記録 (176)

はじめに

hkob の雑記録の第176回目は、昨日作成したテストデータを使って、10MB を超えるファイルに対する FileUploadObject のコンストラクタのテストおよび実装を作成します。今日は途中までになってしまいました。

with a small file のテスト修正

画像の場合には、15,369バイトの ErSxuLeq.png-medium.png というファイルを spec/fixtures に置いていました。今回、10MB を超えるファイルを用意するのですが、そのようなファイルをリポジトリに起きたくありません。また、テストのたびに Tempfile でファイルを分割するもの無駄なので、ファイルに対するメソッドを全て stub 化すことにしてみます。

まず、with a small file の場合も、ファイルを使わずにテストを通過するようにしてみます。File については、 exist?size 、その後の FilePart の new でファイルを実際に開くので、これ自体は mock オブジェクトを作成するようにしました。

describe "initialize" do
      context "with a small file" do
        let(:fname) { "spec/fixtures/ErSxuLeq.png-medium.png" }
        let(:id) { TestConnection::FILE_UPLOAD_IMAGE_ID }
        let(:response) { tc.read_json "upload_file_image" }

        before do
          allow(tc.nc.multipart_client).to receive(:send)
            .and_return(instance_double(Faraday::Response, {body: response}))
          allow(File).to receive(:exist?).with(fname).and_return(true)
          allow(File).to receive(:size).with(fname).and_return(15_369)
          allow(Faraday::Multipart::FilePart).to receive(:new).and_return(instance_double(Faraday::Multipart::FilePart))
        end

        subject { described_class.new(fname: fname) }

        it { expect(subject.id).to eq id }
      end
    end

このように修正することで、ファイルを用意することなくテストが通過しました。

FileUploadObject
  initialize
    with a small file
      is expected to eq "20cd8e4e98ab81aa973b00b23083c115"

Finished in 0.01175 seconds (files took 0.47518 seconds to load)
1 example, 0 failures

with a large file テストの追加

これができれば、large file についても大きなファイルがなくてもテストができるはずです。まずはファイルを分割する split_file メソッドのテストを行います。26 バイトのファイルを作成し、20バイトと6バイトの二つのファイルに分割されることを確認します。

   describe "split_to_small_files" do
      before do
        @org_file = Tempfile.new("org_file")
        @org_file.write "0" * 26
        @org_file.rewind
        @temp_files = described_class.split_to_small_files(@org_file.path, 20)
      end

      after do
        @temp_files.each { |f| f.close }
        @org_file.close
      end

      it "splits the file into smaller parts" do
        expect(@temp_files.size).to eq 2
        expect(File.size(@temp_files[0].path)).to eq 20
        expect(File.size(@temp_files[1].path)).to eq 6
      end
    end

実装は昨日テストした時に作ったコードそのままです。これがテスト通ったので、split_to_small_files は、今後でテストでは stub にしてしまいます。

  def self.split_to_small_files(org_file, max_size = MAX_SIZE)
    raise StandardError, "File does not exist: #{org_file}" unless File.exist?(org_file)

    temp_files = []
    File.open(org_file, "rb") do |file|
      until file.eof?
        chunk = file.read(max_size)
        temp_file = Tempfile.new("part_")
        temp_file.binmode
        temp_file.write(chunk)
        temp_file.rewind
        temp_files << temp_file
      end
    end
    temp_files
  end

おわりに

このままテストを続けるつもりでしたが、サンプル shell script で示されている -F による multipart の part_number=1 の部分を Ruby からどのように送付すればいいかの確認ができませんでした。明日、実際に Faraday から Notion API を叩いてみて検証してみます。

curl --request POST \
  --url 'https://api.notion.com/v1/file_uploads/a3f9d3e2-1abc-42de-b904-badc0ffee000/send' \
  --header 'Authorization: Bearer ntn_****' \
  --header 'Notion-Version: 2022-06-28' \
  --header 'Content-Type: multipart/form-data' \
  -F "file=@image_to_upload_split_aa.png"
  -F "part_number=1"

hkob.notion.site