1
use chrono::Utc;
2
use finance::split::Split;
3
use finance::transaction::Transaction;
4
use scripting::executor::ScriptExecutor;
5
use scripting::format::{ContextType, EntityType, Operation};
6
use scripting::parser::EntityData;
7
use scripting::serializer::MemorySerializer;
8
use uuid::Uuid;
9

            
10
const TEST_WASM: &[u8] = include_bytes!("../../web/static/wasm/groceries_markup.wasm");
11

            
12
#[test]
13
1
fn test_groceries_script_tags_splits() {
14
1
    let executor = ScriptExecutor::new();
15

            
16
1
    let tx = Transaction {
17
1
        id: Uuid::new_v4(),
18
1
        post_date: Utc::now(),
19
1
        enter_date: Utc::now(),
20
1
    };
21

            
22
1
    let account1_id = Uuid::new_v4();
23
1
    let account2_id = Uuid::new_v4();
24
1
    let commodity_id = Uuid::new_v4();
25

            
26
1
    let split1 = Split {
27
1
        id: Uuid::new_v4(),
28
1
        tx_id: tx.id,
29
1
        account_id: account1_id,
30
1
        commodity_id,
31
1
        value_num: -5000,
32
1
        value_denom: 100,
33
1
        reconcile_state: None,
34
1
        reconcile_date: None,
35
1
        lot_id: None,
36
1
    };
37

            
38
1
    let split2 = Split {
39
1
        id: Uuid::new_v4(),
40
1
        tx_id: tx.id,
41
1
        account_id: account2_id,
42
1
        commodity_id,
43
1
        value_num: 5000,
44
1
        value_denom: 100,
45
1
        reconcile_state: None,
46
1
        reconcile_date: None,
47
1
        lot_id: None,
48
1
    };
49

            
50
1
    let mut serializer = MemorySerializer::new();
51
1
    serializer.set_context(ContextType::EntityCreate, EntityType::Transaction);
52

            
53
    // Transaction with 2 splits and 1 tag
54
1
    let tx_idx = serializer.add_transaction_from(&tx, true, 2, 1, false);
55
1
    serializer.set_primary(tx_idx);
56

            
57
1
    let split1_idx = serializer.add_split_from(&split1, tx_idx as i32);
58
1
    let split2_idx = serializer.add_split_from(&split2, tx_idx as i32);
59

            
60
    // Add "note" = "groceries" tag to transaction
61
1
    serializer.add_tag(
62
1
        Uuid::new_v4().into_bytes(),
63
1
        tx_idx as i32,
64
        false,
65
        false,
66
1
        "note",
67
1
        "groceries",
68
    );
69

            
70
1
    let input = serializer.finalize(4096);
71

            
72
1
    println!("Input size: {} bytes", input.len());
73

            
74
1
    let entities = executor
75
1
        .execute(TEST_WASM, &input, Some(4096))
76
1
        .expect("Execution failed");
77

            
78
1
    println!("Output entities: {entities:?}");
79
1
    assert_eq!(
80
1
        entities.len(),
81
        2,
82
        "Expected 2 output entities (category tags for each split)"
83
    );
84

            
85
2
    for (i, entity) in entities.iter().enumerate() {
86
2
        assert_eq!(entity.entity_type, EntityType::Tag);
87
2
        assert_eq!(entity.operation, Operation::Create);
88

            
89
2
        if let EntityData::Tag { name, value } = &entity.data {
90
2
            assert_eq!(name, "category", "Tag {i} name mismatch");
91
2
            assert_eq!(value, "groceries", "Tag {i} value mismatch");
92
        } else {
93
            panic!("Expected Tag entity data, got {:?}", entity.data);
94
        }
95
    }
96

            
97
1
    let parent_indices: Vec<i32> = entities.iter().map(|e| e.parent_idx).collect();
98
1
    assert!(
99
1
        parent_indices.contains(&(split1_idx as i32)),
100
        "Missing tag for split1"
101
    );
102
1
    assert!(
103
1
        parent_indices.contains(&(split2_idx as i32)),
104
        "Missing tag for split2"
105
    );
106
1
}
107

            
108
#[test]
109
1
fn test_groceries_script_skips_non_groceries() {
110
1
    let executor = ScriptExecutor::new();
111

            
112
1
    let tx = Transaction {
113
1
        id: Uuid::new_v4(),
114
1
        post_date: Utc::now(),
115
1
        enter_date: Utc::now(),
116
1
    };
117

            
118
1
    let mut serializer = MemorySerializer::new();
119
1
    serializer.set_context(ContextType::EntityCreate, EntityType::Transaction);
120

            
121
    // Transaction with tag "note" = "other" (not groceries)
122
1
    let tx_idx = serializer.add_transaction_from(&tx, true, 0, 1, false);
123
1
    serializer.set_primary(tx_idx);
124
1
    serializer.add_tag(
125
1
        Uuid::new_v4().into_bytes(),
126
1
        tx_idx as i32,
127
        false,
128
        false,
129
1
        "note",
130
1
        "other",
131
    );
132

            
133
1
    let input = serializer.finalize(4096);
134

            
135
1
    let entities = executor
136
1
        .execute(TEST_WASM, &input, Some(4096))
137
1
        .expect("Execution failed");
138
1
    assert!(
139
1
        entities.is_empty(),
140
        "Expected 0 output entities for non-groceries transaction"
141
    );
142
1
}
143

            
144
#[test]
145
1
fn test_groceries_script_skips_non_transaction() {
146
1
    let executor = ScriptExecutor::new();
147

            
148
1
    let mut serializer = MemorySerializer::new();
149
1
    serializer.set_context(ContextType::EntityCreate, EntityType::Account);
150

            
151
1
    let account_id = [1u8; 16];
152
1
    let parent_account_id = [0u8; 16];
153
1
    let account_idx = serializer.add_account(
154
1
        account_id,
155
        -1,
156
        true,
157
        false,
158
1
        parent_account_id,
159
1
        "Test Account",
160
1
        "Assets:Test Account",
161
        0,
162
    );
163
1
    serializer.set_primary(account_idx);
164

            
165
1
    let input = serializer.finalize(4096);
166

            
167
1
    let entities = executor
168
1
        .execute(TEST_WASM, &input, Some(4096))
169
1
        .expect("Execution failed");
170
1
    assert!(
171
1
        entities.is_empty(),
172
        "Expected 0 output entities for Account"
173
    );
174
1
}
175

            
176
#[test]
177
1
fn test_groceries_script_skips_no_note_tag() {
178
1
    let executor = ScriptExecutor::new();
179

            
180
1
    let tx = Transaction {
181
1
        id: Uuid::new_v4(),
182
1
        post_date: Utc::now(),
183
1
        enter_date: Utc::now(),
184
1
    };
185

            
186
1
    let mut serializer = MemorySerializer::new();
187
1
    serializer.set_context(ContextType::EntityCreate, EntityType::Transaction);
188

            
189
    // Transaction without any tags
190
1
    let tx_idx = serializer.add_transaction_from(&tx, true, 0, 0, false);
191
1
    serializer.set_primary(tx_idx);
192

            
193
1
    let input = serializer.finalize(4096);
194

            
195
1
    let entities = executor
196
1
        .execute(TEST_WASM, &input, Some(4096))
197
1
        .expect("Execution failed");
198
1
    assert!(
199
1
        entities.is_empty(),
200
        "Expected 0 output entities for transaction without note tag"
201
    );
202
1
}