// Shared assertion and unwrap helpers for the named tests in this package.
//
// Kept deliberately small: each helper fails loudly with a readable message
// instead of silently passing, so a broken assertion never hides in noise.

///|
fn assert_true(value : Bool) -> Unit raise {
  if !value {
    fail("expected true")
  }
}

///|
fn assert_false(value : Bool) -> Unit raise {
  if value {
    fail("expected false")
  }
}

///|
fn assert_int_eq(actual : Int, expected : Int) -> Unit raise {
  if actual != expected {
    fail("expected \{expected}, got \{actual}")
  }
}

///|

///|
fn assert_str_eq(actual : String, expected : String) -> Unit raise {
  if actual != expected {
    fail("expected '\{expected}', got '\{actual}'")
  }
}

///|
fn assert_opt_str_eq(actual : String?, expected : String?) -> Unit raise {
  match (actual, expected) {
    (Some(a), Some(e)) => assert_str_eq(a, e)
    (None, None) => ()
    _ => fail("optional strings differ")
  }
}

///|
/// Parse successfully or fail the test with the parse error.
fn unwrap_parse(input : String) -> SecurityTxt raise {
  match parse_security_txt(input) {
    Ok(document) => document
    Err(err) => fail("parse failed unexpectedly: \{err.to_string()}")
  }
}

///|
/// Parse raw bytes successfully or fail the test with the parse error.
fn unwrap_parse_bytes(input : Bytes, limits : Limits) -> SecurityTxt raise {
  match parse_security_txt_bytes(input, limits) {
    Ok(document) => document
    Err(err) => fail("parse failed unexpectedly: \{err.to_string()}")
  }
}

///|
/// Require a parse error; fail the test when parsing succeeds.
fn expect_parse_error(input : String) -> SecurityTxtError raise {
  match parse_security_txt(input) {
    Ok(document) =>
      fail("expected a parse error, got \{document.field_count()} fields")
    Err(err) => err
  }
}

///|
/// Require a parse error for raw bytes; fail when parsing succeeds.
fn expect_parse_bytes_error(
  input : Bytes,
  limits : Limits,
) -> SecurityTxtError raise {
  match parse_security_txt_bytes(input, limits) {
    Ok(document) =>
      fail("expected a parse error, got \{document.field_count()} fields")
    Err(err) => err
  }
}

///|
/// Assert an error has exactly the expected kind.
fn assert_err_kind(
  err : SecurityTxtError,
  kind : SecurityTxtErrorKind,
) -> Unit raise {
  if err.kind() != kind {
    fail(
      "expected \{kind.to_string()}, got \{err.kind().to_string()}: \{err.message()}",
    )
  }
}

///|
/// A small, fully RFC-valid document used across the test suite.
fn valid_document() -> String {
  "# A valid example security.txt\nContact: mailto:security@example.com\nExpires: 2027-01-01T00:00:00.000Z\nPreferred-Languages: en, de\n"
}

///|
/// Unwrap a DateTime result or fail.
fn unwrap_datetime(
  result : Result[DateTime, SecurityTxtError],
) -> DateTime raise {
  match result {
    Ok(dt) => dt
    Err(err) => fail("datetime construction failed: \{err.to_string()}")
  }
}

///|
/// True when the findings contain the given kind.
fn has_finding(findings : Array[AuditFinding], kind : AuditFindingKind) -> Bool {
  for finding in findings {
    if finding.kind() == kind {
      return true
    }
  }
  false
}

///| True when the result holds a value.

///| True when the result holds an error.

///|
/// Build raw bytes from integer values (mod 256), for invalid-UTF-8 inputs.
fn bytes_of(values : Array[Int]) -> Bytes {
  let out : Array[Byte] = []
  for v in values {
    out.push((v & 0xFF).to_byte())
  }
  Bytes::from_array(out)
}