// tests/suite/test_string.el — comprehensive tests for runtime/string.el
//
// Covers every public function in string.el.  Import this file after all
// runtime modules and runtime/test.el have been concatenated in.

// ── str_eq ────────────────────────────────────────────────────────────────────

fn test_str_eq_basic(_: String) -> String {
    assert_true(str_eq("hello", "hello"), "identical strings are equal")
    assert_false(str_eq("hello", "world"), "different strings are not equal")
    assert_true(str_eq("", ""), "empty strings are equal")
    assert_false(str_eq("a", ""), "non-empty vs empty is not equal")
    assert_false(str_eq("", "a"), "empty vs non-empty is not equal")
    assert_false(str_eq("Hello", "hello"), "comparison is case-sensitive")
    return ""
}

fn test_str_eq_symbols(_: String) -> String {
    assert_true(str_eq("!@#$", "!@#$"), "symbols are equal")
    assert_true(str_eq("  ", "  "), "spaces match")
    assert_false(str_eq(" a", "a "), "leading vs trailing space differ")
    assert_false(str_eq("abc", "ab"), "prefix not equal to full string")
    assert_false(str_eq("ab", "abc"), "shorter not equal to longer")
    return ""
}

// ── str_len ───────────────────────────────────────────────────────────────────

fn test_str_len_basic(_: String) -> String {
    assert_int_eq(str_len(""), 0, "empty string has length 0")
    assert_int_eq(str_len("a"), 1, "single char has length 1")
    assert_int_eq(str_len("hello"), 5, "hello has length 5")
    assert_int_eq(str_len("hello world"), 11, "space counted in length")
    assert_int_eq(str_len("12345"), 5, "digits counted correctly")
    assert_int_eq(str_len("!@#$%"), 5, "punctuation counted correctly")
    return ""
}

fn test_str_len_longer(_: String) -> String {
    let s: String = str_repeat("ab", 50)
    assert_int_eq(str_len(s), 100, "repeated string has correct length")
    assert_int_eq(str_len("abcdefghij"), 10, "10-char string")
    return ""
}

// ── str_concat / + operator ───────────────────────────────────────────────────

fn test_str_concat_basic(_: String) -> String {
    assert_eq(str_concat("hello", " world"), "hello world", "basic concat")
    assert_eq(str_concat("", "world"), "world", "empty prefix")
    assert_eq(str_concat("hello", ""), "hello", "empty suffix")
    assert_eq(str_concat("", ""), "", "both empty")
    assert_eq("foo" + "bar", "foobar", "operator + concat")
    return ""
}

fn test_str_concat_chaining(_: String) -> String {
    let a: String = "hello"
    let b: String = " "
    let c: String = "world"
    assert_eq(a + b + c, "hello world", "chained concat")
    assert_eq(str_concat(str_concat("a", "b"), "c"), "abc", "nested concat")
    assert_int_eq(str_len(str_concat("abc", "def")), 6, "length after concat")
    return ""
}

// ── str_slice ─────────────────────────────────────────────────────────────────

fn test_str_slice_basic(_: String) -> String {
    assert_eq(str_slice("hello world", 0, 5), "hello", "slice from start")
    assert_eq(str_slice("hello world", 6, 11), "world", "slice from middle")
    assert_eq(str_slice("hello world", 0, 0), "", "zero-length slice")
    assert_eq(str_slice("hello", 0, 100), "hello", "end clamped to length")
    assert_eq(str_slice("hello", 3, 3), "", "start == end is empty")
    assert_eq(str_slice("hello world", 2, 7), "llo w", "interior slice")
    return ""
}

fn test_str_slice_edge(_: String) -> String {
    assert_eq(str_slice("", 0, 0), "", "empty string slice")
    assert_eq(str_slice("abc", 1, 2), "b", "single char slice")
    assert_eq(str_slice("abc", 0, 3), "abc", "full string slice")
    assert_eq(str_slice("abc", 2, 1), "", "inverted range is empty")
    assert_eq(str_slice("hello", 5, 5), "", "slice at end is empty")
    return ""
}

// ── str_starts_with ───────────────────────────────────────────────────────────

fn test_str_starts_with_basic(_: String) -> String {
    assert_true(str_starts_with("hello world", "hello"), "prefix present")
    assert_false(str_starts_with("hello world", "world"), "not a prefix")
    assert_true(str_starts_with("hello", "hello"), "string is its own prefix")
    assert_true(str_starts_with("hello", ""), "empty prefix always true")
    assert_false(str_starts_with("", "a"), "empty string has no non-empty prefix")
    assert_false(str_starts_with("hi", "hello"), "prefix longer than string")
    return ""
}

fn test_str_starts_with_edge(_: String) -> String {
    assert_true(str_starts_with("abc", "a"), "single-char prefix")
    assert_false(str_starts_with("abc", "b"), "wrong single-char prefix")
    assert_true(str_starts_with("", ""), "empty starts with empty")
    assert_true(str_starts_with("hello world", "hello world"), "full string is prefix")
    assert_false(str_starts_with("hello", "HELLO"), "case-sensitive prefix")
    return ""
}

// ── str_ends_with ─────────────────────────────────────────────────────────────

fn test_str_ends_with_basic(_: String) -> String {
    assert_true(str_ends_with("hello world", "world"), "suffix present")
    assert_false(str_ends_with("hello world", "hello"), "not a suffix")
    assert_true(str_ends_with("hello", "hello"), "string is its own suffix")
    assert_true(str_ends_with("hello", ""), "empty suffix always true")
    assert_false(str_ends_with("", "a"), "empty string has no non-empty suffix")
    assert_false(str_ends_with("hi", "world"), "suffix longer than string")
    return ""
}

fn test_str_ends_with_edge(_: String) -> String {
    assert_true(str_ends_with("abc", "c"), "single-char suffix")
    assert_false(str_ends_with("abc", "b"), "wrong single-char suffix")
    assert_true(str_ends_with("", ""), "empty ends with empty")
    assert_false(str_ends_with("hello", "HELLO"), "case-sensitive suffix")
    assert_true(str_ends_with("file.txt", ".txt"), "common file extension case")
    return ""
}

// ── str_contains ─────────────────────────────────────────────────────────────

fn test_str_contains_basic(_: String) -> String {
    assert_true(str_contains("hello world", "world"), "contains at end")
    assert_true(str_contains("hello world", "hello"), "contains at start")
    assert_true(str_contains("hello world", "lo wo"), "contains in middle")
    assert_false(str_contains("hello world", "xyz"), "not contained")
    assert_true(str_contains("hello", ""), "empty sub always contained")
    assert_false(str_contains("", "a"), "empty string contains nothing")
    return ""
}

fn test_str_contains_edge(_: String) -> String {
    assert_true(str_contains("hello", "hello"), "string contains itself")
    assert_false(str_contains("hello", "helloo"), "longer sub not contained")
    assert_true(str_contains("aaa", "a"), "single char in repeated chars")
    assert_true(str_contains("aaa", "aa"), "two-char sub in repeated chars")
    assert_false(str_contains("abc", "ABC"), "case-sensitive")
    return ""
}

// ── str_index_of ─────────────────────────────────────────────────────────────

fn test_str_index_of_basic(_: String) -> String {
    assert_int_eq(str_index_of("hello world", "world"), 6, "index of suffix")
    assert_int_eq(str_index_of("hello world", "hello"), 0, "index of prefix")
    assert_int_eq(str_index_of("hello world", "o"), 4, "index of first occurrence")
    assert_int_eq(str_index_of("hello world", "xyz"), -1, "not found returns -1")
    assert_int_eq(str_index_of("hello", ""), 0, "empty sub returns 0")
    assert_int_eq(str_index_of("", "a"), -1, "search in empty string")
    return ""
}

fn test_str_index_of_duplicates(_: String) -> String {
    assert_int_eq(str_index_of("aababc", "ab"), 1, "finds first of two occurrences")
    assert_int_eq(str_index_of("abab", "ab"), 0, "finds first at position 0")
    assert_int_eq(str_index_of("xabab", "ab"), 1, "finds first after leading char")
    assert_int_eq(str_index_of("hello hello", "hello"), 0, "finds first hello")
    assert_int_eq(str_index_of("hello", "hello"), 0, "exact match at 0")
    return ""
}

// ── str_last_index_of ─────────────────────────────────────────────────────────

fn test_str_last_index_of_basic(_: String) -> String {
    assert_int_eq(str_last_index_of("hello hello", "hello"), 6, "last occurrence index")
    assert_int_eq(str_last_index_of("hello", "hello"), 0, "only one occurrence")
    assert_int_eq(str_last_index_of("hello", "xyz"), -1, "not found returns -1")
    assert_int_eq(str_last_index_of("aababc", "ab"), 3, "last ab in aababc")
    assert_int_eq(str_last_index_of("abcabc", "abc"), 3, "last abc")
    return ""
}

// ── str_replace ───────────────────────────────────────────────────────────────

fn test_str_replace_basic(_: String) -> String {
    assert_eq(str_replace("hello world", "world", "there"), "hello there", "basic replace")
    assert_eq(str_replace("aaa", "a", "b"), "bbb", "replaces all occurrences")
    assert_eq(str_replace("hello", "xyz", "abc"), "hello", "no match is identity")
    assert_eq(str_replace("", "a", "b"), "", "empty string unchanged")
    assert_eq(str_replace("hello", "", "x"), "hello", "empty from is identity")
    return ""
}

fn test_str_replace_multiple(_: String) -> String {
    assert_eq(str_replace("hello hello", "hello", "bye"), "bye bye", "replace multiple")
    assert_eq(str_replace("aXbXc", "X", "-"), "a-b-c", "single-char delimiter")
    assert_eq(str_replace("abcabc", "abc", "X"), "XX", "multi-char replaced twice")
    assert_eq(str_replace("aabbcc", "bb", ""), "aacc", "replace with empty")
    assert_eq(str_replace("hello", "hello", "world"), "world", "replace whole string")
    return ""
}

// ── str_to_upper / str_to_lower ──────────────────────────────────────────────

fn test_str_to_upper_basic(_: String) -> String {
    assert_eq(str_to_upper("hello"), "HELLO", "lowercase to uppercase")
    assert_eq(str_to_upper("HELLO"), "HELLO", "already uppercase unchanged")
    assert_eq(str_to_upper("Hello World"), "HELLO WORLD", "mixed case")
    assert_eq(str_to_upper(""), "", "empty string unchanged")
    assert_eq(str_to_upper("hello123"), "HELLO123", "digits unchanged")
    assert_eq(str_to_upper("abc-def"), "ABC-DEF", "punctuation unchanged")
    return ""
}

fn test_str_to_lower_basic(_: String) -> String {
    assert_eq(str_to_lower("HELLO"), "hello", "uppercase to lowercase")
    assert_eq(str_to_lower("hello"), "hello", "already lowercase unchanged")
    assert_eq(str_to_lower("Hello World"), "hello world", "mixed case")
    assert_eq(str_to_lower(""), "", "empty string unchanged")
    assert_eq(str_to_lower("HELLO123"), "hello123", "digits unchanged")
    assert_eq(str_to_lower("ABC-DEF"), "abc-def", "punctuation unchanged")
    return ""
}

fn test_str_upper_lower_roundtrip(_: String) -> String {
    let s: String = "Hello, World!"
    assert_eq(str_to_lower(str_to_upper(s)), "hello, world!", "upper then lower")
    assert_eq(str_to_upper(str_to_lower(s)), "HELLO, WORLD!", "lower then upper")
    assert_eq(str_to_upper(str_to_lower("ABC")), "ABC", "ABC roundtrip")
    return ""
}

// ── str_trim / str_lstrip / str_rstrip ───────────────────────────────────────

fn test_str_trim_basic(_: String) -> String {
    assert_eq(str_trim("  hello  "), "hello", "trims spaces both sides")
    assert_eq(str_trim("hello"), "hello", "no whitespace unchanged")
    assert_eq(str_trim("   "), "", "all-space string becomes empty")
    assert_eq(str_trim(""), "", "empty string unchanged")
    assert_eq(str_trim("\t hello \n"), "hello", "trims tabs and newlines")
    assert_eq(str_trim("  hello world  "), "hello world", "internal spaces preserved")
    return ""
}

fn test_str_lstrip_rstrip(_: String) -> String {
    assert_eq(str_lstrip("  hello  "), "hello  ", "lstrip removes leading spaces")
    assert_eq(str_rstrip("  hello  "), "  hello", "rstrip removes trailing spaces")
    assert_eq(str_lstrip("hello"), "hello", "lstrip no-op on clean string")
    assert_eq(str_rstrip("hello"), "hello", "rstrip no-op on clean string")
    assert_eq(str_lstrip(""), "", "lstrip empty string")
    assert_eq(str_rstrip(""), "", "rstrip empty string")
    return ""
}

// ── str_split ─────────────────────────────────────────────────────────────────

fn test_str_split_basic(_: String) -> String {
    let parts: [String] = str_split("a,b,c", ",")
    assert_int_eq(el_list_len(parts), 3, "split yields 3 parts")
    assert_eq(el_list_get(parts, 0), "a", "first part is a")
    assert_eq(el_list_get(parts, 1), "b", "second part is b")
    assert_eq(el_list_get(parts, 2), "c", "third part is c")
    return ""
}

fn test_str_split_edge(_: String) -> String {
    let single: [String] = str_split("hello", ",")
    assert_int_eq(el_list_len(single), 1, "no sep found yields 1 part")
    assert_eq(el_list_get(single, 0), "hello", "single part is the full string")

    let trailing: [String] = str_split("a,b,", ",")
    assert_int_eq(el_list_len(trailing), 3, "trailing sep yields empty last element")
    assert_eq(el_list_get(trailing, 2), "", "last element is empty")

    let multi: [String] = str_split("one::two::three", "::")
    assert_int_eq(el_list_len(multi), 3, "multi-char separator works")
    assert_eq(el_list_get(multi, 1), "two", "middle element correct")
    return ""
}

// ── str_join ─────────────────────────────────────────────────────────────────

fn test_str_join_basic(_: String) -> String {
    let parts: [String] = el_list_empty()
    let parts = el_list_append(parts, "a")
    let parts = el_list_append(parts, "b")
    let parts = el_list_append(parts, "c")
    assert_eq(str_join(parts, ","), "a,b,c", "basic join with comma")
    assert_eq(str_join(parts, ""), "abc", "join with empty separator")
    assert_eq(str_join(parts, " | "), "a | b | c", "join with multi-char sep")
    return ""
}

fn test_str_join_edge(_: String) -> String {
    let empty_list: [String] = el_list_empty()
    assert_eq(str_join(empty_list, ","), "", "joining empty list yields empty")

    let one: [String] = el_list_empty()
    let one = el_list_append(one, "solo")
    assert_eq(str_join(one, ","), "solo", "joining single element yields that element")
    assert_eq(str_join(one, "---"), "solo", "single element: no sep inserted")

    let two: [String] = el_list_empty()
    let two = el_list_append(two, "alpha")
    let two = el_list_append(two, "beta")
    assert_eq(str_join(two, ", "), "alpha, beta", "two elements join correctly")
    return ""
}

// ── int_to_str / str_to_int ──────────────────────────────────────────────────

fn test_int_to_str_basic(_: String) -> String {
    assert_eq(int_to_str(0), "0", "zero")
    assert_eq(int_to_str(42), "42", "positive integer")
    assert_eq(int_to_str(-1), "-1", "negative integer")
    assert_eq(int_to_str(1000000), "1000000", "large integer")
    assert_eq(int_to_str(-999), "-999", "negative large")
    assert_int_eq(str_len(int_to_str(12345)), 5, "correct digit count")
    return ""
}

fn test_str_to_int_basic(_: String) -> String {
    assert_int_eq(str_to_int("0"), 0, "zero")
    assert_int_eq(str_to_int("42"), 42, "positive integer")
    assert_int_eq(str_to_int("-1"), -1, "negative integer")
    assert_int_eq(str_to_int("1000000"), 1000000, "large integer")
    assert_int_eq(str_to_int("-999"), -999, "negative large")
    assert_int_eq(str_to_int(int_to_str(12345)), 12345, "roundtrip")
    return ""
}

// ── float_to_str / str_to_float ──────────────────────────────────────────────

fn test_float_to_str_basic(_: String) -> String {
    assert_eq(float_to_str(0.0), "0", "zero float to str")
    assert_eq(float_to_str(1.5), "1.5", "basic float")
    assert_eq(float_to_str(-3.14), "-3.14", "negative float")
    assert_true(str_len(float_to_str(1.0)) > 0, "float_to_str produces output")
    assert_contains(float_to_str(100.0), "100", "100.0 contains 100")
    return ""
}

fn test_str_to_float_basic(_: String) -> String {
    let f: Float = str_to_float("1.5")
    let s: String = float_to_str(f)
    assert_eq(s, "1.5", "roundtrip 1.5")

    let f2: Float = str_to_float("0.0")
    let s2: String = float_to_str(f2)
    assert_eq(s2, "0", "roundtrip 0.0")

    assert_true(str_to_float("3.14") > 3.13, "3.14 > 3.13")
    assert_true(str_to_float("3.14") < 3.15, "3.14 < 3.15")
    assert_true(str_to_float("-1.0") < 0.0, "negative float is negative")
    return ""
}

// ── str_repeat / str_reverse ─────────────────────────────────────────────────

fn test_str_repeat_basic(_: String) -> String {
    assert_eq(str_repeat("ab", 3), "ababab", "repeat 3 times")
    assert_eq(str_repeat("x", 1), "x", "repeat once")
    assert_eq(str_repeat("x", 0), "", "repeat zero times yields empty")
    assert_eq(str_repeat("", 5), "", "repeating empty string yields empty")
    assert_eq(str_repeat("-", 4), "----", "single char repeat")
    assert_int_eq(str_len(str_repeat("abc", 10)), 30, "length is n * len")
    return ""
}

fn test_str_reverse_basic(_: String) -> String {
    assert_eq(str_reverse("hello"), "olleh", "basic reverse")
    assert_eq(str_reverse("a"), "a", "single char is its own reverse")
    assert_eq(str_reverse(""), "", "empty string reverses to empty")
    assert_eq(str_reverse("abcd"), "dcba", "even-length reverse")
    assert_eq(str_reverse("racecar"), "racecar", "palindrome unchanged")
    assert_eq(str_reverse("abc"), "cba", "three-char reverse")
    return ""
}

// ── str_count ─────────────────────────────────────────────────────────────────

fn test_str_count_basic(_: String) -> String {
    assert_int_eq(str_count("hello world hello", "hello"), 2, "two occurrences")
    assert_int_eq(str_count("aaa", "a"), 3, "adjacent single chars")
    assert_int_eq(str_count("aaa", "aa"), 1, "non-overlapping: one match")
    assert_int_eq(str_count("hello", "xyz"), 0, "no match")
    assert_int_eq(str_count("", "a"), 0, "empty string has no occurrences")
    assert_int_eq(str_count("hello", ""), 0, "empty sub returns 0")
    return ""
}

// ── str_strip_prefix / str_strip_suffix ──────────────────────────────────────

fn test_str_strip_prefix_basic(_: String) -> String {
    assert_eq(str_strip_prefix("foobar", "foo"), "bar", "strips matching prefix")
    assert_eq(str_strip_prefix("foobar", "baz"), "foobar", "no-match is identity")
    assert_eq(str_strip_prefix("hello", ""), "hello", "empty prefix is identity")
    assert_eq(str_strip_prefix("hello", "hello"), "", "full match yields empty")
    assert_eq(str_strip_prefix("hello", "HELLO"), "hello", "case-sensitive no-match")
    return ""
}

fn test_str_strip_suffix_basic(_: String) -> String {
    assert_eq(str_strip_suffix("hello.md", ".md"), "hello", "strips matching suffix")
    assert_eq(str_strip_suffix("hello.md", ".txt"), "hello.md", "no-match is identity")
    assert_eq(str_strip_suffix("hello", ""), "hello", "empty suffix is identity")
    assert_eq(str_strip_suffix("hello", "hello"), "", "full match yields empty")
    assert_eq(str_strip_suffix("hello", "HELLO"), "hello", "case-sensitive no-match")
    return ""
}

// ── str_find_chars ────────────────────────────────────────────────────────────

fn test_str_find_chars_basic(_: String) -> String {
    assert_int_eq(str_find_chars("hello world", " "), 5, "finds space at index 5")
    assert_int_eq(str_find_chars("hello", "xyz"), -1, "not found returns -1")
    assert_int_eq(str_find_chars("hello", ""), -1, "empty charset returns -1")
    assert_int_eq(str_find_chars("hello", "aeiou"), 1, "finds first vowel at index 1")
    assert_int_eq(str_find_chars("", "a"), -1, "search in empty string returns -1")
    assert_int_eq(str_find_chars("abc", "c"), 2, "finds last char of string")
    return ""
}

// ── str_char_at / str_char_code ───────────────────────────────────────────────

fn test_str_char_at_basic(_: String) -> String {
    assert_eq(str_char_at("hello", 0), "h", "first char")
    assert_eq(str_char_at("hello", 4), "o", "last char")
    assert_eq(str_char_at("hello", 2), "l", "middle char")
    assert_eq(str_char_at("hello", -1), "", "negative index yields empty")
    assert_eq(str_char_at("hello", 5), "", "out-of-bounds yields empty")
    assert_eq(str_char_at("", 0), "", "empty string yields empty")
    return ""
}

fn test_str_char_code_basic(_: String) -> String {
    assert_int_eq(str_char_code("A", 0), 65, "A has code 65")
    assert_int_eq(str_char_code("a", 0), 97, "a has code 97")
    assert_int_eq(str_char_code("0", 0), 48, "0 has code 48")
    assert_int_eq(str_char_code("hello", 0), 104, "h has code 104")
    assert_int_eq(str_char_code("", 0), 0, "empty string yields 0")
    assert_int_eq(str_char_code("a", 5), 0, "out-of-bounds yields 0")
    return ""
}

// ── str_pad_left / str_pad_right ─────────────────────────────────────────────

fn test_str_pad_left_basic(_: String) -> String {
    assert_eq(str_pad_left("hi", 5, " "), "   hi", "pads to width 5")
    assert_eq(str_pad_left("hi", 2, " "), "hi", "already at width is unchanged")
    assert_eq(str_pad_left("hi", 1, " "), "hi", "wider than width is unchanged")
    assert_eq(str_pad_left("5", 3, "0"), "005", "zero-pad a number")
    assert_int_eq(str_len(str_pad_left("ab", 7, "x")), 7, "padded to exact width")
    return ""
}

fn test_str_pad_right_basic(_: String) -> String {
    assert_eq(str_pad_right("hi", 5, " "), "hi   ", "pads to width 5")
    assert_eq(str_pad_right("hi", 2, " "), "hi", "already at width is unchanged")
    assert_eq(str_pad_right("hi", 1, " "), "hi", "wider than width is unchanged")
    assert_int_eq(str_len(str_pad_right("ab", 7, "-")), 7, "padded to exact width")
    assert_starts_with(str_pad_right("ab", 7, "-"), "ab", "original string at start")
    return ""
}

// ── Character classification ──────────────────────────────────────────────────

fn test_is_letter_basic(_: String) -> String {
    assert_true(is_letter("a"), "a is a letter")
    assert_true(is_letter("Z"), "Z is a letter")
    assert_true(is_letter("abc"), "abc all letters")
    assert_false(is_letter("1"), "1 is not a letter")
    assert_false(is_letter(""), "empty string is not a letter")
    assert_false(is_letter("a1"), "a1 has non-letter")
    return ""
}

fn test_is_digit_basic(_: String) -> String {
    assert_true(is_digit("0"), "0 is a digit")
    assert_true(is_digit("9"), "9 is a digit")
    assert_true(is_digit("123"), "123 all digits")
    assert_false(is_digit("a"), "a is not a digit")
    assert_false(is_digit(""), "empty is not a digit")
    assert_false(is_digit("1a"), "1a has non-digit")
    return ""
}

fn test_is_whitespace_basic(_: String) -> String {
    assert_true(is_whitespace(" "), "space is whitespace")
    assert_true(is_whitespace("   "), "multiple spaces")
    assert_false(is_whitespace("a"), "a is not whitespace")
    assert_false(is_whitespace(""), "empty string is not whitespace")
    assert_false(is_whitespace(" a"), "mixed is not all whitespace")
    return ""
}

// ── str_count_lines / str_count_words ────────────────────────────────────────

fn test_str_count_lines_basic(_: String) -> String {
    assert_int_eq(str_count_lines(""), 0, "empty string has 0 lines")
    assert_int_eq(str_count_lines("hello"), 1, "single line no newline")
    assert_int_eq(str_count_lines("a\nb"), 2, "two lines with newline")
    assert_int_eq(str_count_lines("a\nb\nc"), 3, "three lines")
    assert_int_eq(str_count_lines("a\n"), 1, "trailing newline not counted as extra line")
    return ""
}

fn test_str_count_words_basic(_: String) -> String {
    assert_int_eq(str_count_words(""), 0, "empty string has 0 words")
    assert_int_eq(str_count_words("hello"), 1, "single word")
    assert_int_eq(str_count_words("hello world"), 2, "two words")
    assert_int_eq(str_count_words("  hello  world  "), 2, "extra spaces do not add words")
    assert_int_eq(str_count_words("one two three four"), 4, "four words")
    return ""
}

// ── url_encode / url_decode ──────────────────────────────────────────────────

fn test_url_encode_basic(_: String) -> String {
    assert_eq(url_encode("hello"), "hello", "plain string unchanged")
    assert_contains(url_encode("hello world"), "%20", "space encoded as %20")
    assert_true(str_len(url_encode("hello world")) > str_len("hello world"), "encoded is longer")
    let encoded: String = url_encode("hello world")
    let decoded: String = url_decode(encoded)
    assert_eq(decoded, "hello world", "decode reverses encode")
    return ""
}

fn test_url_roundtrip(_: String) -> String {
    let s: String = "name=hello world&value=42"
    let encoded: String = url_encode(s)
    let decoded: String = url_decode(encoded)
    assert_eq(decoded, s, "url encode/decode roundtrip")
    assert_true(!str_eq(encoded, s), "encoded differs from original")
    assert_true(str_len(encoded) >= str_len(s), "encoded is at least as long")
    return ""
}

// ── bool_to_str ───────────────────────────────────────────────────────────────

fn test_bool_to_str_basic(_: String) -> String {
    assert_eq(bool_to_str(true), "true", "true becomes 'true'")
    assert_eq(bool_to_str(false), "false", "false becomes 'false'")
    assert_true(str_eq(bool_to_str(1 == 1), "true"), "expression true")
    assert_true(str_eq(bool_to_str(1 == 2), "false"), "expression false")
    assert_int_eq(str_len(bool_to_str(true)), 4, "true has length 4")
    return ""
}

// ── str_to_bytes / bytes_to_str ───────────────────────────────────────────────

fn test_str_to_bytes_basic(_: String) -> String {
    let b: String = str_to_bytes("hi")
    assert_true(str_contains(b, "104"), "h has code 104")
    assert_true(str_contains(b, "105"), "i has code 105")
    assert_eq(str_to_bytes(""), "[]", "empty string yields empty array")
    assert_true(str_starts_with(b, "["), "bytes is JSON array")
    assert_true(str_ends_with(b, "]"), "bytes is JSON array")
    return ""
}

fn test_bytes_roundtrip(_: String) -> String {
    let s: String = "hello"
    let b: String = str_to_bytes(s)
    let s2: String = bytes_to_str(b)
    assert_eq(s2, s, "bytes roundtrip: hello")

    let s3: String = "ABC"
    let b3: String = str_to_bytes(s3)
    let s4: String = bytes_to_str(b3)
    assert_eq(s4, s3, "bytes roundtrip: ABC")

    assert_eq(bytes_to_str("[]"), "", "empty bytes yields empty string")
    return ""
}
