defmodule AttachmentRangesTest do
  use CouchTestCase

  @moduletag :attachments

  @moduledoc """
  Test CouchDB attachment range requests
  This is a port of the attachment_ranges.js suite
  """

  @tag :with_db
  test "manages attachment range requests successfully", context do
    db_name = context[:db_name]

    bin_att_doc = %{
      _id: "bin_doc",
      _attachments: %{
        "foo.txt": %{
          content_type: "application/octet-stream",
          data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
        }
      }
    }

    create_doc(db_name, bin_att_doc)
    # Fetching the whole entity is a 206
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=0-28"]
      )

    assert(resp.status_code == 206)
    assert resp.body == "This is a base64 encoded text"
    assert resp.headers["Content-Range"] == "bytes 0-28/29"
    assert resp.headers["Content-Length"] == "29"

    # Fetch the whole entity without an end offset is a 200
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=0-"]
      )

    assert(resp.status_code == 200)
    assert resp.body == "This is a base64 encoded text"
    assert resp.headers["Content-Range"] == nil
    assert resp.headers["Content-Length"] == "29"

    # Even if you ask multiple times.
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=0-,0-,0-"]
      )

    assert(resp.status_code == 200)

    # Badly formed range header is a 200
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes:0-"]
      )

    assert(resp.status_code == 200)

    # Fetch the end of an entity without an end offset is a 206
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=2-"]
      )

    assert(resp.status_code == 206)
    assert resp.body == "is is a base64 encoded text"
    assert resp.headers["Content-Range"] == "bytes 2-28/29"
    assert resp.headers["Content-Length"] == "27"

    # Fetch first part of entity is a 206
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=0-3"]
      )

    assert(resp.status_code == 206)
    assert resp.body == "This"
    assert resp.headers["Content-Range"] == "bytes 0-3/29"
    assert resp.headers["Content-Length"] == "4"

    # Fetch middle of entity is also a 206
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=10-15"]
      )

    assert(resp.status_code == 206)
    assert resp.body == "base64"
    assert resp.headers["Content-Range"] == "bytes 10-15/29"
    assert resp.headers["Content-Length"] == "6"

    # Fetch end of entity is also a 206
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=-3"]
      )

    assert(resp.status_code == 206)
    assert resp.body == "ext"
    assert resp.headers["Content-Range"] == "bytes 26-28/29"
    assert resp.headers["Content-Length"] == "3"

    # backward range is 416
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=5-3"]
      )

    assert(resp.status_code == 416)

    # range completely outside of entity is 416
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=300-310"]
      )

    assert(resp.status_code == 416)

    # We ignore a Range header with too many ranges
    resp =
      Couch.get(
        "/#{db_name}/bin_doc/foo.txt",
        headers: [Range: "bytes=0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1,0-1"]
      )

    assert(resp.status_code == 200)
  end
end
