ruchy 4.2.0

A systems scripting language that transpiles to idiomatic Rust with extreme quality engineering
Documentation
// RED Phase Test for Feature #89: String.as_bytes() method
//
// This addresses 2 failing book examples (Ch4.10, Ch17.8) that use string.as_bytes()
//
// Error: "Unknown zero-argument string method: as_bytes"
//
// Expected behavior: Return array of UTF-8 byte values (0-255) for the string
//
// EXTREME TDD Methodology:
// 1. RED: Create comprehensive failing tests (this file)
// 2. GREEN: Implement as_bytes() in string method handler
// 3. REFACTOR: Add property tests for UTF-8 correctness

#![allow(missing_docs)]

use assert_cmd::Command;
use std::fs;
use tempfile::TempDir;

fn ruchy_cmd() -> Command {
    assert_cmd::cargo::cargo_bin_cmd!("ruchy")
}

/// RED: Test basic ASCII string conversion to bytes
///
/// This is the most common use case from the book examples.
#[test]
fn test_feature_089_as_bytes_basic_ascii() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_as_bytes.ruchy");

    let code = r#"
fun main() {
    let text = "Hello";
    let bytes = text.as_bytes();
    println(bytes);
}
"#;

    fs::write(&test_file, code).unwrap();

    // RED: Currently fails with "Unknown zero-argument string method: as_bytes"
    // GREEN: Should output array of byte values [72, 101, 108, 108, 111]
    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(
        stdout.contains("[72, 101, 108, 108, 111]"),
        "Expected byte array for 'Hello', got: {stdout}"
    );
}

/// RED: Test empty string
#[test]
fn test_feature_089_as_bytes_empty_string() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_empty.ruchy");

    let code = r#"
fun main() {
    let text = "";
    let bytes = text.as_bytes();
    println(bytes.len());
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(
        stdout.contains('0'),
        "Expected length 0 for empty string, got: {stdout}"
    );
}

/// RED: Test UTF-8 multi-byte characters
///
/// "Hello 世界" should encode correctly (世=228,184,150, 界=231,149,140)
#[test]
fn test_feature_089_as_bytes_utf8() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_utf8.ruchy");

    let code = r#"
fun main() {
    let text = "A";
    let bytes = text.as_bytes();
    // 'A' is ASCII 65
    println(bytes[0]);
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(
        stdout.contains("65"),
        "Expected byte value 65 for 'A', got: {stdout}"
    );
}

/// RED: Test indexing into returned byte array
///
/// Common pattern: bytes[0] to get first byte
#[test]
fn test_feature_089_as_bytes_indexing() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_index.ruchy");

    let code = r#"
fun main() {
    let text = "ABC";
    let bytes = text.as_bytes();
    println(bytes[0]);
    println(bytes[1]);
    println(bytes[2]);
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(stdout.contains("65"), "Expected 65 (A), got: {stdout}");
    assert!(stdout.contains("66"), "Expected 66 (B), got: {stdout}");
    assert!(stdout.contains("67"), "Expected 67 (C), got: {stdout}");
}

/// RED: Test special characters
///
/// Newline (\n = 10), tab (\t = 9), etc.
#[test]
fn test_feature_089_as_bytes_special_chars() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_special.ruchy");

    let code = r#"
fun main() {
    let text = "\n";
    let bytes = text.as_bytes();
    println(bytes[0]);
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(
        stdout.contains("10"),
        "Expected byte value 10 for newline, got: {stdout}"
    );
}

/// RED: Test iteration over byte array
///
/// Common pattern: for byte in bytes { ... }
#[test]
fn test_feature_089_as_bytes_iteration() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_iter.ruchy");

    let code = r#"
fun main() {
    let text = "Hi";
    let bytes = text.as_bytes();
    for byte in bytes {
        println(byte);
    }
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(stdout.contains("72"), "Expected 72 (H), got: {stdout}");
    assert!(stdout.contains("105"), "Expected 105 (i), got: {stdout}");
}

/// RED: Test method chaining
///
/// Pattern: `text.as_bytes().len()`
#[test]
fn test_feature_089_as_bytes_method_chaining() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_chain.ruchy");

    let code = r#"
fun main() {
    let text = "Test";
    let length = text.as_bytes().len();
    println(length);
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(
        stdout.contains('4'),
        "Expected length 4 for 'Test', got: {stdout}"
    );
}

/// GREEN: Verify string methods still work
///
/// Sanity check: `as_bytes()` doesn't break other string methods
#[test]
fn test_feature_089_other_string_methods_still_work() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test_sanity.ruchy");

    let code = r#"
fun main() {
    let text = "Hello";
    println(text.len());
    println(text.to_uppercase());
}
"#;

    fs::write(&test_file, code).unwrap();

    let output = ruchy_cmd().arg("run").arg(&test_file).assert().success();

    let stdout = String::from_utf8_lossy(&output.get_output().stdout);
    assert!(stdout.contains('5'), "Expected length 5, got: {stdout}");
    assert!(stdout.contains("HELLO"), "Expected HELLO, got: {stdout}");
}