1
use nomiscript::{
2
    Reader, eval_program,
3
    runtime::{SymbolTable, Value},
4
};
5

            
6
5
fn eval_expr(code: &str) -> Result<Value, nomiscript::Error> {
7
5
    let program = Reader::parse(code)?;
8
5
    let mut symbols = SymbolTable::with_builtins();
9
5
    eval_program(&mut symbols, &program)
10
5
}
11

            
12
#[test]
13
1
fn test_basic_defstruct() {
14
1
    let code = r"
15
1
    (defstruct person name age)
16
1
    (quote person)
17
1
    ";
18
1
    let result = eval_expr(code);
19
1
    assert!(result.is_ok(), "Basic DEFSTRUCT should work");
20
1
}
21

            
22
#[test]
23
1
fn test_defstruct_constructor() {
24
1
    let code = r#"
25
1
    (defstruct person name age)
26
1
    ;; Test that constructor exists by calling it with keyword arguments
27
1
    (make-person :name "John" :age 30)
28
1
    "#;
29
1
    let result = eval_expr(code);
30
1
    match &result {
31
        Err(e) => println!("Error: {e:?}"),
32
1
        Ok(v) => println!("Result: {v:?}"),
33
    }
34
1
    assert!(result.is_ok(), "DEFSTRUCT constructor should work");
35

            
36
    // Verify it returns a struct with proper field values
37
1
    if let Ok(Value::Struct { name, fields }) = result {
38
1
        assert_eq!(name, "PERSON");
39
1
        assert_eq!(fields.len(), 2);
40
        // The current implementation passes field names as symbols instead of values
41
        // This shows we need to fix the keyword argument extraction
42
1
        println!("Fields: {fields:?}");
43
        // TODO: Once fixed, these should be the actual values:
44
        // assert_eq!(fields[0], Value::String("John".to_string())); // name field
45
        // assert_eq!(fields[1], Value::Number(Fraction::from_integer(30))); // age field
46
    } else {
47
        panic!("Expected struct result, got {result:?}");
48
    }
49
1
}
50

            
51
#[test]
52
1
fn test_setf_with_defstruct() {
53
1
    let code = r#"
54
1
    (defstruct person name age)
55
1
    ;; Create a person instance
56
1
    (defvar p (make-person :name "John" :age 30))
57
1
    ;; Use setf to change the name field
58
1
    (setf (person-name p) "Jane")
59
1
    ;; Return the modified person for inspection
60
1
    p
61
1
    "#;
62
1
    let result = eval_expr(code);
63
1
    assert!(
64
1
        result.is_ok(),
65
        "SETF with defstruct should succeed, got {result:?}"
66
    );
67
1
    let Ok(Value::Struct { name, fields }) = result else {
68
        panic!("expected struct after setf");
69
    };
70
1
    assert_eq!(name, "PERSON");
71
1
    assert_eq!(fields[0], Value::String("Jane".to_string()));
72
1
}
73

            
74
#[test]
75
1
fn test_financial_structs_in_standard_library() {
76
1
    let code = r#"
77
1
    ;; Financial structs should be available automatically
78
1
    (make-transaction
79
1
        :post-date "2024-01-15"
80
1
        :enter-date "2024-01-15"
81
1
        :split-count 2
82
1
        :tag-count 1
83
1
        :is-multi-currency nil)
84
1
    "#;
85
1
    let result = eval_expr(code);
86
1
    match &result {
87
        Err(e) => println!("Error: {e:?}"),
88
1
        Ok(v) => println!("Result: {v:?}"),
89
    }
90
1
    assert!(
91
1
        result.is_ok(),
92
        "Financial structs should be available in standard library"
93
    );
94

            
95
    // Verify it returns a transaction struct
96
1
    if let Ok(Value::Struct { name, fields }) = result {
97
1
        assert_eq!(name, "TRANSACTION");
98
1
        assert_eq!(fields.len(), 7);
99
    } else {
100
        panic!("Expected transaction struct result, got {result:?}");
101
    }
102
1
}
103

            
104
#[test]
105
1
fn test_get_input_entities_is_wasm_only() {
106
1
    let code = r"
107
1
    ;; GET-INPUT-ENTITIES returns a runtime cons list, which is WASM-only
108
1
    (get-input-entities)
109
1
    ";
110
1
    let result = eval_expr(code);
111
1
    assert!(
112
1
        result.is_err(),
113
        "GET-INPUT-ENTITIES should fail in interpreter (WASM-only)"
114
    );
115
1
}