// test_limits.mbt — Tests for the resource limit presets and their
// enforcement (limits.mbt).

///|
/// Repeats `s` `n` times.
fn repeat_str(s : String, n : Int) -> String {
  let sb = StringBuilder()
  for _i = 0; _i < n; _i = _i + 1 {
    sb.write_string(s)
  }
  sb.to_string()
}

///|
/// A `Limits` with a tiny input bound, everything else default.
fn tiny_input() -> Limits {
  {
    max_input_bytes: 8,
    max_links: 1024,
    max_params_per_link: 256,
    max_relations_per_link: 64,
    max_target_bytes: 8192,
    max_parameter_name_bytes: 256,
    max_parameter_value_bytes: 8192,
    max_quoted_string_bytes: 16384,
    max_linkset_links: 8192,
    max_json_bytes: 4 << 20,
  }
}

///|
/// A `Limits` with a tiny JSON byte bound and a generous input bound, so the
/// `max_json_bytes` check (not the `max_input_bytes` check) is what fires.
fn tiny_json_limits() -> Limits {
  {
    max_input_bytes: 1 << 20,
    max_links: 1024,
    max_params_per_link: 256,
    max_relations_per_link: 64,
    max_target_bytes: 8192,
    max_parameter_name_bytes: 256,
    max_parameter_value_bytes: 8192,
    max_quoted_string_bytes: 16384,
    max_linkset_links: 8192,
    max_json_bytes: 64,
  }
}

///|
test "the three presets expose their documented bounds and every parser bound is enforced" {
  // Preset values.
  let d = Limits::default()
  let s = Limits::strict()
  let p = Limits::permissive()
  assert_true(d.max_input_bytes() == 1 << 20)
  assert_true(d.max_links() == 1024)
  assert_true(d.max_relations_per_link() == 64)
  assert_true(s.max_links() == 64)
  assert_true(s.max_params_per_link() == 32)
  assert_true(s.max_target_bytes() == 512)
  assert_true(s.max_quoted_string_bytes() == 2048)
  assert_true(p.max_links() == 100000)
  assert_true(p.max_json_bytes() == 256 << 20)

  // max_input_bytes (Input stage).
  expect_err_kind(
    parse_link_header("; rel=\"next\"", tiny_input()),
    LimitExceeded,
  )
  match parse_link_header("; rel=\"next\"", tiny_input()) {
    Ok(_) => fail("expected limit error")
    Err(e) => assert_true(e.stage() == Input)
  }

  // max_links (strict: 64).
  let many = repeat_str("; rel=\"next\",", 70)
  expect_err_kind(parse_link_header(many, s), LimitExceeded)

  // max_target_bytes (strict: 512).
  let long_target = repeat_str("a", 600)
  expect_err_kind(
    parse_link_header("<\{long_target}>; rel=\"next\"", s),
    LimitExceeded,
  )

  // max_params_per_link (strict: 32).
  let extra_params = repeat_str("; x-i=v", 40)
  expect_err_kind(
    parse_link_header("; rel=\"next\"\{extra_params}", s),
    LimitExceeded,
  )

  // max_relations_per_link (strict: 16).
  let many_rels = repeat_str("next ", 20)
  expect_err_kind(
    parse_link_header("; rel=\"\{many_rels}\"", s),
    LimitExceeded,
  )

  // max_parameter_name_bytes (strict: 64).
  let long_name = repeat_str("a", 70)
  expect_err_kind(
    parse_link_header("; rel=\"next\"; x-\{long_name}=v", s),
    LimitExceeded,
  )

  // max_parameter_value_bytes (strict: 1024).
  let long_value = repeat_str("v", 2000)
  expect_err_kind(
    parse_link_header("; rel=\"next\"; x=\{long_value}", s),
    LimitExceeded,
  )

  // max_quoted_string_bytes (strict: 2048).
  let long_title = repeat_str("x", 3000)
  expect_err_kind(
    parse_link_header("; rel=\"next\"; title=\"\{long_title}\"", s),
    LimitExceeded,
  )

  // max_json_bytes on the JSON parser.
  let long_href = repeat_str("a", 200)
  let big_json = "{\"linkset\":[{\"next\":[{\"href\":\"/\{long_href}\"}]}]}"
  expect_err_kind(
    parse_linkset_json(big_json, tiny_json_limits()),
    LimitExceeded,
  )

  // Enforced limits are reported with the Limit stage.
  match parse_link_header(many, s) {
    Ok(_) => fail("expected limit error")
    Err(e) => assert_true(e.stage() == Limit)
  }

  // At or under the bound parses cleanly.
  let ok = unwrap_ok(
    parse_link_header("; rel=\"next\", ; rel=\"prev\"", s),
  )
  assert_true(ok.length() == 2)
}