///|
test "plan ignores non GET" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9"),
      100L,
      false,
      true,
      true,
    ).unwrap()
    is Ignore,
  )
}

///|
test "plan ignores unsupported ranges" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9"),
      100L,
      true,
      false,
      true,
    ).unwrap()
    is Ignore,
  )
}

///|
test "plan honors caller decision not to apply" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9"),
      100L,
      true,
      true,
      false,
    ).unwrap()
    is Ignore,
  )
}

///|
test "plan ignores unknown units" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("items=0-9"),
      100L,
      true,
      true,
      true,
    ).unwrap()
    is Ignore,
  )
}

///|
test "plan creates single response" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9"),
      100L,
      true,
      true,
      true,
    ).unwrap()
    is Single(_),
  )
}

///|
test "plan creates multiple response" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9,90-"),
      100L,
      true,
      true,
      true,
    ).unwrap()
    is Multiple(_),
  )
}

///|
test "plan drops unsatisfiable members" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=0-9,999-"),
      100L,
      true,
      true,
      true,
    ).unwrap()
    is Single(_),
  )
}

///|
test "plan creates unsatisfiable response" {
  assert_true(
    plan_byte_range_response(
      unwrap_request("bytes=999-"),
      100L,
      true,
      true,
      true,
    ).unwrap()
    is Unsatisfiable(100L),
  )
}

///|
test "Ignore recommends 200" {
  assert_int_eq(Ignore.recommended_status(), 200)
}

///|
test "Single recommends 206" {
  assert_int_eq(Single(concrete_range(0L, 0L)).recommended_status(), 206)
}

///|
test "Multiple recommends 206" {
  assert_int_eq(Multiple([concrete_range(0L, 0L)]).recommended_status(), 206)
}

///|
test "Unsatisfiable recommends 416" {
  assert_int_eq(Unsatisfiable(10L).recommended_status(), 416)
}

///|
test "multipart planner produces per part metadata" {
  let plan = plan_multipart_ranges(
    [concrete_range(0L, 9L), concrete_range(90L, 99L)],
    100L,
    "BOUNDARY",
  ).unwrap()
  assert_int_eq(plan.parts().length(), 2)
  assert_i64_eq(plan.parts()[0].content_length(), 10L)
}

///|
test "multipart content type uses caller boundary" {
  assert_str_eq(
    multipart_content_type("abc"),
    "multipart/byteranges; boundary=abc",
  )
}

///|
test "multipart planner rejects empty boundary" {
  assert_true(plan_multipart_ranges([], 10L, "") is Err(_))
}

///|
test "multipart planner rejects out of range part" {
  assert_true(
    plan_multipart_ranges([concrete_range(0L, 10L)], 10L, "b") is Err(_),
  )
}

///|
test "conditional plan matches a strong tag and serves the range" {
  let plan = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    Some(EntityTag("\"xyzzy\"")),
    "\"xyzzy\"",
    0L,
    true,
    true,
    true,
  ).unwrap()
  assert_true(plan is Single(_))
}

///|
test "conditional plan ignores the range for a weak tag" {
  let plan = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    Some(EntityTag("W/\"xyzzy\"")),
    "\"xyzzy\"",
    0L,
    true,
    true,
    true,
  ).unwrap()
  assert_true(plan is Ignore)
}

///|
test "conditional plan ignores the range for a stale tag" {
  let plan = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    Some(EntityTag("\"old\"")),
    "\"new\"",
    0L,
    true,
    true,
    true,
  ).unwrap()
  assert_true(plan is Ignore)
}

///|
test "conditional plan matches an exact HTTP-date" {
  let plan = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    Some(HttpDate(784_111_777L)),
    "anything",
    784_111_777L,
    true,
    true,
    true,
  ).unwrap()
  assert_true(plan is Single(_))
}

///|
test "conditional plan ignores the range for a stale date" {
  let plan = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    Some(HttpDate(784_111_777L)),
    "anything",
    784_111_778L,
    true,
    true,
    true,
  ).unwrap()
  assert_true(plan is Ignore)
}

///|
test "conditional plan without a validator matches the unconditional plan" {
  let conditional = plan_conditional_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    None,
    "unused",
    0L,
    true,
    true,
    true,
  ).unwrap()
  let plain = plan_byte_range_response(
    unwrap_request("bytes=0-9"),
    100L,
    true,
    true,
    true,
  ).unwrap()
  assert_eq(conditional, plain)
}

///|
test "multipart body length estimate counts the full frame" {
  // One part 0-9 of 100, boundary BOUNDARY, per-part type text/plain:
  // delimiter 12 + Content-Type 26 + Content-Range 30 + blank 2 + data 10
  // + trailing CRLF 2 = 82; closing delimiter "--BOUNDARY--\r\n" = 14.
  let plan = plan_multipart_ranges([concrete_range(0L, 9L)], 100L, "BOUNDARY").unwrap()
  assert_i64_eq(plan.estimated_body_length("text/plain"), 96L)
}

///|
test "multipart body length estimate sums every part" {
  // Part two uses "bytes 90-99/100" (15 chars), so its frame is 84 bytes.
  let plan = plan_multipart_ranges(
    [concrete_range(0L, 9L), concrete_range(90L, 99L)],
    100L,
    "BOUNDARY",
  ).unwrap()
  assert_i64_eq(plan.estimated_body_length("text/plain"), 180L)
}

///|
test "multipart body length estimate handles zero parts" {
  let plan = plan_multipart_ranges([], 100L, "BOUNDARY").unwrap()
  assert_i64_eq(plan.estimated_body_length("text/plain"), 14L)
}

///|
test "multipart body length estimate tracks boundary and type sizes" {
  let plan = plan_multipart_ranges([concrete_range(0L, 9L)], 100L, "b").unwrap()
  // delimiter 5 + Content-Type 16 + Content-Range 30 + blank 2 + data 10
  // + trailing CRLF 2 = 65; closing "--b--\r\n" = 7.
  assert_i64_eq(plan.estimated_body_length(""), 72L)
}