cobolx 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/devcontainer.json +26 -0
- package/.dockerignore +4 -0
- package/.github/workflows/ci.yml +157 -0
- package/Cargo.lock +2245 -0
- package/Cargo.toml +39 -0
- package/bin/check-update.js +44 -0
- package/bin/cobolx.js +81 -0
- package/docker-compose.yml +33 -0
- package/dockerfile +18 -0
- package/dockerfile.test +39 -0
- package/package.json +27 -0
- package/scripts/install.js +145 -0
- package/src/agent/client.rs +1345 -0
- package/src/agent.rs +1 -0
- package/src/cobol/copybook.rs +71 -0
- package/src/cobol/data_parser.rs +290 -0
- package/src/cobol/indexer.rs +256 -0
- package/src/cobol/layout.rs +278 -0
- package/src/cobol/lexer.rs +135 -0
- package/src/cobol/model.rs +196 -0
- package/src/cobol/scanner.rs +72 -0
- package/src/cobol/source_parser.rs +91 -0
- package/src/cobol.rs +8 -0
- package/src/config/config.rs +64 -0
- package/src/config.rs +3 -0
- package/src/lib.rs +6 -0
- package/src/main.rs +20 -0
- package/src/memory/files.rs +155 -0
- package/src/memory/store.rs +406 -0
- package/src/memory.rs +5 -0
- package/src/ui/draw.rs +519 -0
- package/src/ui/tui.rs +812 -0
- package/src/ui.rs +2 -0
- package/tests/indexer_tests.rs +192 -0
- package/tests/memory_store_tests.rs +21 -0
- package/tests/project_files_tests.rs +72 -0
- package/tests/sandbox_tests.rs +178 -0
package/src/ui.rs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
use std::fs::{self, File};
|
|
2
|
+
use std::io::Write;
|
|
3
|
+
use tempfile::tempdir;
|
|
4
|
+
|
|
5
|
+
#[test]
|
|
6
|
+
fn init_indexer_persists_program_copybook_and_call_edges() {
|
|
7
|
+
let dir = tempdir().unwrap();
|
|
8
|
+
let copy_dir = dir.path().join("copy");
|
|
9
|
+
fs::create_dir_all(©_dir).unwrap();
|
|
10
|
+
|
|
11
|
+
File::create(dir.path().join("MAIN.cbl"))
|
|
12
|
+
.unwrap()
|
|
13
|
+
.write_all(
|
|
14
|
+
br#"
|
|
15
|
+
IDENTIFICATION DIVISION.
|
|
16
|
+
PROGRAM-ID. MAIN.
|
|
17
|
+
DATA DIVISION.
|
|
18
|
+
WORKING-STORAGE SECTION.
|
|
19
|
+
01 WS-NEXT-PGM PIC X(8).
|
|
20
|
+
COPY CUSTOMER.
|
|
21
|
+
PROCEDURE DIVISION.
|
|
22
|
+
CALL "SUB001"
|
|
23
|
+
USING WS-NEXT-PGM.
|
|
24
|
+
CALL WS-NEXT-PGM.
|
|
25
|
+
STOP RUN.
|
|
26
|
+
"#,
|
|
27
|
+
)
|
|
28
|
+
.unwrap();
|
|
29
|
+
|
|
30
|
+
File::create(dir.path().join("SUB001.cbl"))
|
|
31
|
+
.unwrap()
|
|
32
|
+
.write_all(
|
|
33
|
+
br#"
|
|
34
|
+
IDENTIFICATION DIVISION.
|
|
35
|
+
PROGRAM-ID. SUB001.
|
|
36
|
+
PROCEDURE DIVISION.
|
|
37
|
+
EXIT PROGRAM.
|
|
38
|
+
"#,
|
|
39
|
+
)
|
|
40
|
+
.unwrap();
|
|
41
|
+
|
|
42
|
+
File::create(dir.path().join("CUSTOMER.cpy"))
|
|
43
|
+
.unwrap()
|
|
44
|
+
.write_all(
|
|
45
|
+
br#"
|
|
46
|
+
01 CUSTOMER-REC.
|
|
47
|
+
05 CUST-ID PIC X(10).
|
|
48
|
+
05 CUST-BALANCE PIC S9(7)V99
|
|
49
|
+
COMP-3.
|
|
50
|
+
05 CUST-ALIAS REDEFINES CUST-ID PIC X(10).
|
|
51
|
+
05 CUST-ADDR OCCURS
|
|
52
|
+
3 TIMES
|
|
53
|
+
PIC X(20).
|
|
54
|
+
"#,
|
|
55
|
+
)
|
|
56
|
+
.unwrap();
|
|
57
|
+
|
|
58
|
+
let mut store = rdo::memory::MemoryStore::open_or_create(dir.path()).unwrap();
|
|
59
|
+
let report = rdo::cobol::indexer::index_sandbox(dir.path(), &mut store).unwrap();
|
|
60
|
+
|
|
61
|
+
assert_eq!(report.source_count, 2);
|
|
62
|
+
assert_eq!(report.copybook_count, 1);
|
|
63
|
+
assert_eq!(report.programs.len(), 2);
|
|
64
|
+
assert_eq!(report.copybook_uses, 1);
|
|
65
|
+
assert_eq!(report.resolved_copybooks, 1);
|
|
66
|
+
assert_eq!(report.static_calls, 1);
|
|
67
|
+
assert_eq!(report.dynamic_calls, 1);
|
|
68
|
+
assert_eq!(report.data_items, 6);
|
|
69
|
+
|
|
70
|
+
let conn = store.connection();
|
|
71
|
+
let programs: i64 = conn
|
|
72
|
+
.query_row("SELECT COUNT(*) FROM programs", [], |row| row.get(0))
|
|
73
|
+
.unwrap();
|
|
74
|
+
let copies: i64 = conn
|
|
75
|
+
.query_row(
|
|
76
|
+
"SELECT COUNT(*) FROM copybook_uses WHERE resolve_status = 'resolved'",
|
|
77
|
+
[],
|
|
78
|
+
|row| row.get(0),
|
|
79
|
+
)
|
|
80
|
+
.unwrap();
|
|
81
|
+
let static_calls: i64 = conn
|
|
82
|
+
.query_row(
|
|
83
|
+
"SELECT COUNT(*) FROM call_edges WHERE kind = 'static'",
|
|
84
|
+
[],
|
|
85
|
+
|row| row.get(0),
|
|
86
|
+
)
|
|
87
|
+
.unwrap();
|
|
88
|
+
let dynamic_calls: i64 = conn
|
|
89
|
+
.query_row(
|
|
90
|
+
"SELECT COUNT(*) FROM call_edges WHERE kind = 'dynamic'",
|
|
91
|
+
[],
|
|
92
|
+
|row| row.get(0),
|
|
93
|
+
)
|
|
94
|
+
.unwrap();
|
|
95
|
+
let data_items: i64 = conn
|
|
96
|
+
.query_row("SELECT COUNT(*) FROM data_items", [], |row| row.get(0))
|
|
97
|
+
.unwrap();
|
|
98
|
+
let cust_id_pic: String = conn
|
|
99
|
+
.query_row(
|
|
100
|
+
"SELECT pic FROM data_items WHERE name = 'CUST-ID'",
|
|
101
|
+
[],
|
|
102
|
+
|row| row.get(0),
|
|
103
|
+
)
|
|
104
|
+
.unwrap();
|
|
105
|
+
let comp3_usage: String = conn
|
|
106
|
+
.query_row(
|
|
107
|
+
"SELECT usage_clause FROM data_items WHERE name = 'CUST-BALANCE'",
|
|
108
|
+
[],
|
|
109
|
+
|row| row.get(0),
|
|
110
|
+
)
|
|
111
|
+
.unwrap();
|
|
112
|
+
let comp3_pic: String = conn
|
|
113
|
+
.query_row(
|
|
114
|
+
"SELECT pic FROM data_items WHERE name = 'CUST-BALANCE'",
|
|
115
|
+
[],
|
|
116
|
+
|row| row.get(0),
|
|
117
|
+
)
|
|
118
|
+
.unwrap();
|
|
119
|
+
let redefines: String = conn
|
|
120
|
+
.query_row(
|
|
121
|
+
"SELECT redefines FROM data_items WHERE name = 'CUST-ALIAS'",
|
|
122
|
+
[],
|
|
123
|
+
|row| row.get(0),
|
|
124
|
+
)
|
|
125
|
+
.unwrap();
|
|
126
|
+
let occurs: i64 = conn
|
|
127
|
+
.query_row(
|
|
128
|
+
"SELECT occurs FROM data_items WHERE name = 'CUST-ADDR'",
|
|
129
|
+
[],
|
|
130
|
+
|row| row.get(0),
|
|
131
|
+
)
|
|
132
|
+
.unwrap();
|
|
133
|
+
let ws_layout: (i64, i64, String) = conn
|
|
134
|
+
.query_row(
|
|
135
|
+
"SELECT byte_offset, byte_size, storage_kind FROM data_items WHERE name = 'WS-NEXT-PGM'",
|
|
136
|
+
[],
|
|
137
|
+
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
|
138
|
+
)
|
|
139
|
+
.unwrap();
|
|
140
|
+
let customer_group: (i64, i64, String) = conn
|
|
141
|
+
.query_row(
|
|
142
|
+
"SELECT byte_offset, byte_size, storage_kind FROM data_items WHERE name = 'CUSTOMER-REC'",
|
|
143
|
+
[],
|
|
144
|
+
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
|
145
|
+
)
|
|
146
|
+
.unwrap();
|
|
147
|
+
let cust_id_layout: (i64, i64) = conn
|
|
148
|
+
.query_row(
|
|
149
|
+
"SELECT byte_offset, byte_size FROM data_items WHERE name = 'CUST-ID'",
|
|
150
|
+
[],
|
|
151
|
+
|row| Ok((row.get(0)?, row.get(1)?)),
|
|
152
|
+
)
|
|
153
|
+
.unwrap();
|
|
154
|
+
let comp3_layout: (i64, i64, String) = conn
|
|
155
|
+
.query_row(
|
|
156
|
+
"SELECT byte_offset, byte_size, storage_kind FROM data_items WHERE name = 'CUST-BALANCE'",
|
|
157
|
+
[],
|
|
158
|
+
|row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
|
|
159
|
+
)
|
|
160
|
+
.unwrap();
|
|
161
|
+
let alias_layout: (i64, i64) = conn
|
|
162
|
+
.query_row(
|
|
163
|
+
"SELECT byte_offset, byte_size FROM data_items WHERE name = 'CUST-ALIAS'",
|
|
164
|
+
[],
|
|
165
|
+
|row| Ok((row.get(0)?, row.get(1)?)),
|
|
166
|
+
)
|
|
167
|
+
.unwrap();
|
|
168
|
+
let occurs_layout: (i64, i64) = conn
|
|
169
|
+
.query_row(
|
|
170
|
+
"SELECT byte_offset, byte_size FROM data_items WHERE name = 'CUST-ADDR'",
|
|
171
|
+
[],
|
|
172
|
+
|row| Ok((row.get(0)?, row.get(1)?)),
|
|
173
|
+
)
|
|
174
|
+
.unwrap();
|
|
175
|
+
|
|
176
|
+
assert_eq!(programs, 2);
|
|
177
|
+
assert_eq!(copies, 1);
|
|
178
|
+
assert_eq!(static_calls, 1);
|
|
179
|
+
assert_eq!(dynamic_calls, 1);
|
|
180
|
+
assert_eq!(data_items, 6);
|
|
181
|
+
assert_eq!(cust_id_pic, "X(10)");
|
|
182
|
+
assert_eq!(comp3_pic, "S9(7)V99");
|
|
183
|
+
assert_eq!(comp3_usage, "COMP-3");
|
|
184
|
+
assert_eq!(redefines, "CUST-ID");
|
|
185
|
+
assert_eq!(occurs, 3);
|
|
186
|
+
assert_eq!(ws_layout, (0, 8, "display".to_string()));
|
|
187
|
+
assert_eq!(customer_group, (8, 75, "group".to_string()));
|
|
188
|
+
assert_eq!(cust_id_layout, (8, 10));
|
|
189
|
+
assert_eq!(comp3_layout, (18, 5, "packed-decimal".to_string()));
|
|
190
|
+
assert_eq!(alias_layout, (8, 10));
|
|
191
|
+
assert_eq!(occurs_layout, (23, 60));
|
|
192
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
use tempfile::tempdir;
|
|
2
|
+
|
|
3
|
+
#[test]
|
|
4
|
+
fn first_open_creates_memory_database_and_schema() {
|
|
5
|
+
let dir = tempdir().unwrap();
|
|
6
|
+
|
|
7
|
+
let store = rdo::memory::MemoryStore::open_or_create(dir.path()).unwrap();
|
|
8
|
+
|
|
9
|
+
assert!(store.db_path().exists());
|
|
10
|
+
|
|
11
|
+
let count: i64 = store
|
|
12
|
+
.connection()
|
|
13
|
+
.query_row(
|
|
14
|
+
"SELECT COUNT(*) FROM sqlite_master WHERE type = 'table' AND name IN ('files', 'programs', 'runs', 'skills')",
|
|
15
|
+
[],
|
|
16
|
+
|row| row.get(0),
|
|
17
|
+
)
|
|
18
|
+
.unwrap();
|
|
19
|
+
|
|
20
|
+
assert_eq!(count, 4);
|
|
21
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
use rdo::memory::MemoryStore;
|
|
2
|
+
use tempfile::tempdir;
|
|
3
|
+
|
|
4
|
+
#[test]
|
|
5
|
+
fn markdown_files_are_created_appended_and_read_under_docs_dir() {
|
|
6
|
+
let dir = tempdir().unwrap();
|
|
7
|
+
let store = MemoryStore::open_or_create(dir.path()).unwrap();
|
|
8
|
+
|
|
9
|
+
let path = store
|
|
10
|
+
.write_markdown("analysis/init.md", "# Init\n")
|
|
11
|
+
.unwrap();
|
|
12
|
+
store
|
|
13
|
+
.append_markdown("analysis/init.md", "\nCOBOL inventory ready.\n")
|
|
14
|
+
.unwrap();
|
|
15
|
+
|
|
16
|
+
assert!(path.starts_with(store.docs_dir()));
|
|
17
|
+
assert!(path.starts_with(dir.path().join("docs")));
|
|
18
|
+
assert_eq!(
|
|
19
|
+
store.read_markdown("analysis/init.md").unwrap(),
|
|
20
|
+
"# Init\n\nCOBOL inventory ready.\n"
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[test]
|
|
25
|
+
fn skill_files_are_created_and_read_under_skills_dir() {
|
|
26
|
+
let dir = tempdir().unwrap();
|
|
27
|
+
let store = MemoryStore::open_or_create(dir.path()).unwrap();
|
|
28
|
+
|
|
29
|
+
let path = store
|
|
30
|
+
.write_skill_file("cobol-migration/SKILL.md", "# COBOL Migration\n")
|
|
31
|
+
.unwrap();
|
|
32
|
+
|
|
33
|
+
assert!(path.starts_with(store.skills_dir()));
|
|
34
|
+
assert_eq!(
|
|
35
|
+
store.read_skill_file("cobol-migration/SKILL.md").unwrap(),
|
|
36
|
+
"# COBOL Migration\n"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#[test]
|
|
41
|
+
fn windows_style_relative_paths_are_normalized() {
|
|
42
|
+
let dir = tempdir().unwrap();
|
|
43
|
+
let store = MemoryStore::open_or_create(dir.path()).unwrap();
|
|
44
|
+
|
|
45
|
+
let path = store
|
|
46
|
+
.write_markdown("analysis\\windows.md", "# Windows\n")
|
|
47
|
+
.unwrap();
|
|
48
|
+
|
|
49
|
+
assert!(path.starts_with(dir.path().join("docs")));
|
|
50
|
+
assert_eq!(
|
|
51
|
+
store.read_markdown("analysis/windows.md").unwrap(),
|
|
52
|
+
"# Windows\n"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[test]
|
|
57
|
+
fn project_file_writer_rejects_unsafe_or_wrong_paths() {
|
|
58
|
+
let dir = tempdir().unwrap();
|
|
59
|
+
let store = MemoryStore::open_or_create(dir.path()).unwrap();
|
|
60
|
+
|
|
61
|
+
assert!(store.write_markdown("../escape.md", "bad").is_err());
|
|
62
|
+
assert!(store.write_markdown("..\\escape.md", "bad").is_err());
|
|
63
|
+
assert!(store.write_markdown("C:\\tmp\\escape.md", "bad").is_err());
|
|
64
|
+
assert!(store.write_markdown("\\\\srv\\share\\x.md", "bad").is_err());
|
|
65
|
+
assert!(store.write_markdown("CON.md", "bad").is_err());
|
|
66
|
+
assert!(
|
|
67
|
+
store
|
|
68
|
+
.write_markdown("notes/not-markdown.txt", "bad")
|
|
69
|
+
.is_err()
|
|
70
|
+
);
|
|
71
|
+
assert!(store.write_skill_file("../SKILL.md", "bad").is_err());
|
|
72
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
use std::fs::{self, File};
|
|
2
|
+
use std::io::Write;
|
|
3
|
+
use tempfile::tempdir;
|
|
4
|
+
|
|
5
|
+
#[test]
|
|
6
|
+
fn test_scan_finds_cobol_files_flat() {
|
|
7
|
+
let dir = tempdir().unwrap();
|
|
8
|
+
|
|
9
|
+
// Create mock COBOL files with various supported extensions
|
|
10
|
+
File::create(dir.path().join("main.cbl"))
|
|
11
|
+
.unwrap()
|
|
12
|
+
.write_all(b"IDENTIFICATION DIVISION.")
|
|
13
|
+
.unwrap();
|
|
14
|
+
File::create(dir.path().join("utility.cpy"))
|
|
15
|
+
.unwrap()
|
|
16
|
+
.write_all(b"01 WS-VAR PIC X.")
|
|
17
|
+
.unwrap();
|
|
18
|
+
File::create(dir.path().join("test.cob"))
|
|
19
|
+
.unwrap()
|
|
20
|
+
.write_all(b"PROCEDURE DIVISION.")
|
|
21
|
+
.unwrap();
|
|
22
|
+
File::create(dir.path().join("other.coo"))
|
|
23
|
+
.unwrap()
|
|
24
|
+
.write_all(b"DATA DIVISION.")
|
|
25
|
+
.unwrap();
|
|
26
|
+
|
|
27
|
+
// Create non-COBOL files that should be ignored
|
|
28
|
+
File::create(dir.path().join("README.md")).unwrap();
|
|
29
|
+
File::create(dir.path().join("Cargo.toml")).unwrap();
|
|
30
|
+
|
|
31
|
+
let result = rdo::cobol::scanner::scan_sandbox(dir.path()).unwrap();
|
|
32
|
+
|
|
33
|
+
assert_eq!(result.len(), 4);
|
|
34
|
+
|
|
35
|
+
let sources: Vec<_> = result
|
|
36
|
+
.iter()
|
|
37
|
+
.filter(|f| f.file_type == rdo::cobol::scanner::CobolFileType::Source)
|
|
38
|
+
.collect();
|
|
39
|
+
let copybooks: Vec<_> = result
|
|
40
|
+
.iter()
|
|
41
|
+
.filter(|f| f.file_type == rdo::cobol::scanner::CobolFileType::Copybook)
|
|
42
|
+
.collect();
|
|
43
|
+
|
|
44
|
+
assert_eq!(
|
|
45
|
+
sources.len(),
|
|
46
|
+
3,
|
|
47
|
+
"Should find 3 source files (.cbl, .cob, .coo)"
|
|
48
|
+
);
|
|
49
|
+
assert_eq!(copybooks.len(), 1, "Should find 1 copybook file (.cpy)");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[test]
|
|
53
|
+
fn test_scan_recursive_finds_nested_files() {
|
|
54
|
+
let dir = tempdir().unwrap();
|
|
55
|
+
|
|
56
|
+
// Create nested directory structure
|
|
57
|
+
let sub1 = dir.path().join("module_a");
|
|
58
|
+
let sub2 = dir.path().join("module_b");
|
|
59
|
+
let sub2_nested = sub2.join("submodule");
|
|
60
|
+
fs::create_dir_all(&sub1).unwrap();
|
|
61
|
+
fs::create_dir_all(&sub2_nested).unwrap();
|
|
62
|
+
|
|
63
|
+
File::create(dir.path().join("main.cbl"))
|
|
64
|
+
.unwrap()
|
|
65
|
+
.write_all(b"ROOT")
|
|
66
|
+
.unwrap();
|
|
67
|
+
File::create(sub1.join("helper.cbl"))
|
|
68
|
+
.unwrap()
|
|
69
|
+
.write_all(b"SUB1")
|
|
70
|
+
.unwrap();
|
|
71
|
+
File::create(sub2.join("process.cob"))
|
|
72
|
+
.unwrap()
|
|
73
|
+
.write_all(b"SUB2")
|
|
74
|
+
.unwrap();
|
|
75
|
+
File::create(sub2_nested.join("deep.cpy"))
|
|
76
|
+
.unwrap()
|
|
77
|
+
.write_all(b"DEEP")
|
|
78
|
+
.unwrap();
|
|
79
|
+
|
|
80
|
+
let result = rdo::cobol::scanner::scan_sandbox(dir.path()).unwrap();
|
|
81
|
+
|
|
82
|
+
assert_eq!(
|
|
83
|
+
result.len(),
|
|
84
|
+
4,
|
|
85
|
+
"Should find all 4 COBOL files across nested dirs"
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Verify paths are sorted
|
|
89
|
+
for i in 1..result.len() {
|
|
90
|
+
assert!(
|
|
91
|
+
result[i - 1].path <= result[i].path,
|
|
92
|
+
"Results should be sorted by path"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#[test]
|
|
98
|
+
fn test_scan_excludes_hidden_and_build_dirs() {
|
|
99
|
+
let dir = tempdir().unwrap();
|
|
100
|
+
|
|
101
|
+
// Create directories that should be excluded
|
|
102
|
+
let git_dir = dir.path().join(".git");
|
|
103
|
+
let target_dir = dir.path().join("target");
|
|
104
|
+
let node_modules = dir.path().join("node_modules");
|
|
105
|
+
let vendor_dir = dir.path().join("vendor");
|
|
106
|
+
let build_dir = dir.path().join("build");
|
|
107
|
+
let hidden_dir = dir.path().join(".hidden");
|
|
108
|
+
let valid_dir = dir.path().join("src");
|
|
109
|
+
|
|
110
|
+
for d in &[
|
|
111
|
+
&git_dir,
|
|
112
|
+
&target_dir,
|
|
113
|
+
&node_modules,
|
|
114
|
+
&vendor_dir,
|
|
115
|
+
&build_dir,
|
|
116
|
+
&hidden_dir,
|
|
117
|
+
&valid_dir,
|
|
118
|
+
] {
|
|
119
|
+
fs::create_dir_all(d).unwrap();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Put COBOL files in excluded dirs (should NOT be found)
|
|
123
|
+
File::create(git_dir.join("hooks.cbl")).unwrap();
|
|
124
|
+
File::create(target_dir.join("out.cbl")).unwrap();
|
|
125
|
+
File::create(node_modules.join("dep.cbl")).unwrap();
|
|
126
|
+
File::create(vendor_dir.join("lib.cbl")).unwrap();
|
|
127
|
+
File::create(build_dir.join("gen.cbl")).unwrap();
|
|
128
|
+
File::create(hidden_dir.join("secret.cbl")).unwrap();
|
|
129
|
+
|
|
130
|
+
// Put COBOL files in valid dirs (SHOULD be found)
|
|
131
|
+
File::create(dir.path().join("root.cbl"))
|
|
132
|
+
.unwrap()
|
|
133
|
+
.write_all(b"ROOT")
|
|
134
|
+
.unwrap();
|
|
135
|
+
File::create(valid_dir.join("app.cob"))
|
|
136
|
+
.unwrap()
|
|
137
|
+
.write_all(b"APP")
|
|
138
|
+
.unwrap();
|
|
139
|
+
|
|
140
|
+
let result = rdo::cobol::scanner::scan_sandbox(dir.path()).unwrap();
|
|
141
|
+
|
|
142
|
+
assert_eq!(
|
|
143
|
+
result.len(),
|
|
144
|
+
2,
|
|
145
|
+
"Should only find files outside excluded directories"
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
let names: Vec<String> = result
|
|
149
|
+
.iter()
|
|
150
|
+
.map(|f| f.path.file_name().unwrap().to_string_lossy().into_owned())
|
|
151
|
+
.collect();
|
|
152
|
+
assert!(names.contains(&"root.cbl".to_string()));
|
|
153
|
+
assert!(names.contains(&"app.cob".to_string()));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#[test]
|
|
157
|
+
fn test_scan_empty_directory() {
|
|
158
|
+
let dir = tempdir().unwrap();
|
|
159
|
+
|
|
160
|
+
let result = rdo::cobol::scanner::scan_sandbox(dir.path()).unwrap();
|
|
161
|
+
assert!(result.is_empty(), "Empty dir should return no results");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#[test]
|
|
165
|
+
fn test_scan_tracks_file_size() {
|
|
166
|
+
let dir = tempdir().unwrap();
|
|
167
|
+
|
|
168
|
+
let content = b"IDENTIFICATION DIVISION.\nPROGRAM-ID. HELLO.\n";
|
|
169
|
+
File::create(dir.path().join("sized.cbl"))
|
|
170
|
+
.unwrap()
|
|
171
|
+
.write_all(content)
|
|
172
|
+
.unwrap();
|
|
173
|
+
|
|
174
|
+
let result = rdo::cobol::scanner::scan_sandbox(dir.path()).unwrap();
|
|
175
|
+
|
|
176
|
+
assert_eq!(result.len(), 1);
|
|
177
|
+
assert_eq!(result[0].size_bytes, content.len() as u64);
|
|
178
|
+
}
|