// test_property.mbt — Deterministic property tests.
//
// Every generator is the fixed-seed PRNG from test_helpers.mbt: no clock, no
// `random()`. The same seeds always produce the same corpus on every platform
// and every run. Each iteration checks a round-trip or normalization
// invariant against a freshly generated input; the whole file exercises more
// than 1200 deterministic cases.

///|
test "property: header round-trip for 400 random links" {
  let rng = rng_new(11)
  let dflt = Limits::default()
  for _i = 0; _i < 400; _i = _i + 1 {
    let link = rng.web_link()
    let header = serialize_link(link)
    let reparsed = unwrap_ok(parse_link_header(header, dflt))
    assert_true(reparsed.length() == 1)
    // Serialization is canonical: parse then re-serialize reproduces the
    // exact same header text.
    assert_str_eq(serialize_link(reparsed[0]), header)
  }
}

///|
test "property: linkset text and JSON round-trips for 300 random sets" {
  let rng = rng_new(22)
  let dflt = Limits::default()
  for _i = 0; _i < 300; _i = _i + 1 {
    let links = Array::new()
    let n = rng.range(1, 5)
    for _j = 0; _j < n; _j = _j + 1 {
      links.push(rng.web_link())
    }
    let set = LinkSet::from_links(links)

    // Text round-trip: parse(serialize(set)) re-serializes identically, and
    // the text form doubles as a valid Link header field value.
    let text = serialize_linkset_text(set)
    let text_parsed = unwrap_ok(parse_linkset_text(text, dflt))
    assert_str_eq(serialize_linkset_text(text_parsed), text)
    let as_header = unwrap_ok(parse_link_header(text, dflt))
    assert_str_eq(serialize_link_header(as_header), text)

    // JSON normalization is a fixed point: one parse/serialize cycle is
    // stable, because the JSON format normalizes a multi-relation link to one
    // link per relation type on first serialization.
    let j1 = serialize_linkset_json(set)
    let json_parsed = unwrap_ok(parse_linkset_json(j1, dflt))
    assert_str_eq(serialize_linkset_json(json_parsed), j1)
  }
}

///|
test "property: URI, language-tag and relation-type invariants for 300 random cases" {
  let rng = rng_new(33)
  for _i = 0; _i < 300; _i = _i + 1 {
    // A generated URI-reference recomposes byte-for-byte.
    let uri = rng.uri()
    let parsed = unwrap_ok(parse_uri_reference(uri))
    assert_str_eq(parsed.to_string(), uri)

    // Resolution is idempotent, for both absolute and relative references.
    let once = unwrap_ok(
      resolve_uri_reference_string("http://a.example/b/c/d;p?q", uri),
    )
    let twice = unwrap_ok(
      resolve_uri_reference_string("http://a.example/b/c/d;p?q", once),
    )
    assert_str_eq(twice, once)

    // A generated language tag is valid.
    assert_true(valid_language_tag(rng.lang_tag()))

    // A generated extension relation URI parses as an extension type.
    let ext = "https://rel.example/" + rng.segment(1, 8)
    let rt = unwrap_ok(parse_relation_type(ext))
    assert_true(rt.is_extension())
  }

  // remove_dot_segments is idempotent on generated paths.
  let rng2 = rng_new(55)
  for _i = 0; _i < 200; _i = _i + 1 {
    let p = "/\{rng2.segment(1, 6)}/\{rng2.segment(1, 6)}/./../\{rng2.segment(1, 6)}"
    let once = remove_dot_segments(p)
    assert_str_eq(remove_dot_segments(once), once)
  }
}