1use notebook::discover::{Error, Local, read_external_asset};
2use std::{fs, io};
3
4#[test]
5fn nested_external_payloads_are_exact_and_empty_is_distinct_from_missing() {
6 let root = tempfile::tempdir().unwrap();
7 let directory = root.path().join("Group 🦀/Section é_onefiles");
8 fs::create_dir_all(&directory).unwrap();
9 let filename = "2a83ae62-6754-4383-8e2b-4033ff3cfba1.onebin";
10 let mut source = Local::open(root.path()).unwrap();
11 let section = "Group 🦀/Section é.ONE";
12 assert!(
13 matches!(read_external_asset(&mut source, section, filename, 0),
14 Err(Error::Io { error, .. }) if error.kind() == io::ErrorKind::NotFound)
15 );
16 fs::write(directory.join(filename), []).unwrap();
17 assert!(
18 read_external_asset(&mut source, section, filename, 0)
19 .unwrap()
20 .is_empty()
21 );
22 let bytes: Vec<_> = (0..65537).map(|index| (index % 251) as u8).collect();
23 fs::write(directory.join(filename), &bytes).unwrap();
24 assert!(
25 matches!(read_external_asset(&mut source, section, filename, bytes.len()-1),
26 Err(Error::Io { error, .. }) if error.kind() == io::ErrorKind::FileTooLarge)
27 );
28 assert_eq!(
29 read_external_asset(&mut source, section, filename, bytes.len()).unwrap(),
30 bytes
31 );
32 assert_eq!(fs::read(directory.join(filename)).unwrap(), bytes);
33}
34
35#[test]
36fn invalid_asset_locations_fail_before_accessing_the_source() {
37 struct Unused;
38 impl notebook::discover::Source for Unused {
39 fn entries(&mut self, _: &str, _: usize) -> io::Result<Vec<notebook::discover::Entry>> {
40 panic!("Unexpected enumeration")
41 }
42 fn read(&mut self, _: &str, _: usize) -> io::Result<Vec<u8>> {
43 panic!("Unexpected read")
44 }
45 }
46 let filename = "2a83ae62-6754-4383-8e2b-4033ff3cfba1.onebin";
47 for section in [
48 "",
49 ".one",
50 "Group/.one",
51 "/Section.one",
52 "../Section.one",
53 "Group/../Section.one",
54 "Group\\Section.one",
55 "Section.onetoc2",
56 ] {
57 assert!(matches!(
58 read_external_asset(&mut Unused, section, filename, 100),
59 Err(Error::Entry { .. })
60 ));
61 }
62 for name in [
63 "",
64 "attachment.png",
65 "../2a83ae62-6754-4383-8e2b-4033ff3cfba1.onebin",
66 "2a83ae62-6754-4383-8e2b-4033ff3cfba1.onebin:other",
67 "<invfdo>",
68 ] {
69 assert!(matches!(
70 read_external_asset(&mut Unused, "Section.one", name, 100),
71 Err(Error::Entry { .. })
72 ));
73 }
74}
75
76#[test]
77fn reserved_payload_directories_are_not_notebook_groups() {
78 let root = tempfile::tempdir().unwrap();
79 fs::write(
80 root.path().join("Section.ONE"),
81 onestore::create_section("Section.one", "Fixture", "Author").unwrap(),
82 )
83 .unwrap();
84 for name in ["section_onefiles", "orphan_onefiles", "ordinary"] {
85 fs::create_dir(root.path().join(name)).unwrap();
86 }
87 fs::write(
88 root.path().join("section_onefiles/unfinished.onebin"),
89 b"payload",
90 )
91 .unwrap();
92 let catalog = notebook::discover::discover(
93 &mut Local::open(root.path()).unwrap(),
94 notebook::discover::Limits {
95 entries: 4,
96 bytes_per_file: 1 << 20,
97 depth: 1,
98 },
99 )
100 .unwrap();
101 assert_eq!(catalog.sections.len(), 1);
102 assert_eq!(
103 catalog
104 .groups
105 .iter()
106 .map(|group| group.path.as_str())
107 .collect::<Vec<_>>(),
108 ["ordinary"]
109 );
110}
111
112#[test]
113#[cfg(feature = "smb")]
114#[ignore = "requires a disposable Samba mirror of corpus/native-external-assets/notebook at ONESTORE_SMB_NOTEBOOK"]
115fn live_external_payloads() {
116 use onestore::{
117 FileDataReference, RevisionIndex, Store,
118 document::{Document, Kind},
119 };
120 let root = std::path::Path::new("../../corpus/native-external-assets/notebook");
121 let bytes = fs::read(root.join("synthetic.one")).unwrap();
122 let store = Store::parse(&bytes).unwrap();
123 let index = RevisionIndex::parse(&store).unwrap();
124 let document = Document::parse(&index).unwrap();
125 let client = notebook::smb::Client::connect(
126 &std::env::var("ONESTORE_SMB_LAB").unwrap(),
127 "agent",
128 notebook::smb::Credentials::default(),
129 std::time::Duration::from_secs(5),
130 )
131 .unwrap();
132 let mut remote =
133 notebook::discover::Smb::new(&client, &std::env::var("ONESTORE_SMB_NOTEBOOK").unwrap())
134 .unwrap();
135 let mut local = Local::open(root).unwrap();
136 let mut seen = std::collections::BTreeSet::new();
137 for node in document
138 .spaces
139 .values()
140 .flat_map(|space| space.revisions.values())
141 .flat_map(|revision| revision.nodes.values())
142 {
143 if let Kind::File {
144 reference: FileDataReference::External(filename),
145 ..
146 } = &node.kind
147 && seen.insert(filename)
148 {
149 let expected = fs::read(root.join("synthetic_onefiles").join(filename)).unwrap();
150 assert_eq!(
151 read_external_asset(&mut local, "synthetic.one", filename, expected.len()).unwrap(),
152 expected
153 );
154 assert_eq!(
155 read_external_asset(&mut remote, "synthetic.one", filename, expected.len())
156 .unwrap(),
157 expected
158 );
159 }
160 }
161 assert_eq!(seen.len(), 3);
162}