1use std::sync::{Arc, OnceLock};
23
24use arrow_array::RecordBatch;
25use expect_test::Expect;
26use itertools::Itertools;
27#[cfg(test)]
28use roaring::RoaringTreemap;
29
30use crate::TableIdent;
31#[cfg(test)]
32use crate::encryption::EncryptionManager;
33use crate::encryption::SensitiveBytes;
34use crate::encryption::kms::{KeyManagementClient, MemoryKeyManagementClient};
35use crate::io::FileIO;
36use crate::runtime::Runtime;
37use crate::spec::TableMetadata;
38use crate::table::Table;
39
40pub fn test_runtime() -> Runtime {
48 static TOKIO_RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
49 let tokio_rt = TOKIO_RT.get_or_init(|| {
50 tokio::runtime::Builder::new_multi_thread()
51 .enable_all()
52 .build()
53 .expect("failed to build test tokio runtime")
54 });
55 Runtime::new(tokio_rt)
56}
57
58pub fn check_record_batches(
67 record_batches: Vec<RecordBatch>,
68 expected_schema: Expect,
69 expected_data: Expect,
70 ignore_check_columns: &[&str],
71 sort_column: Option<&str>,
72) {
73 assert!(!record_batches.is_empty(), "Empty record batches");
74
75 let first_batch = record_batches.first().unwrap();
77 let record_batch =
78 arrow_select::concat::concat_batches(&first_batch.schema(), &record_batches).unwrap();
79
80 let mut columns = record_batch.columns().to_vec();
81 if let Some(sort_column) = sort_column {
82 let column = record_batch.column_by_name(sort_column).unwrap();
83 let indices = arrow_ord::sort::sort_to_indices(column, None, None).unwrap();
84 columns = columns
85 .iter()
86 .map(|column| arrow_select::take::take(column.as_ref(), &indices, None).unwrap())
87 .collect_vec();
88 }
89
90 expected_schema.assert_eq(&format!(
91 "{}",
92 record_batch.schema().fields().iter().format(",\n")
93 ));
94 expected_data.assert_eq(&format!(
95 "{}",
96 record_batch
97 .schema()
98 .fields()
99 .iter()
100 .zip_eq(columns)
101 .map(|(field, column)| {
102 if ignore_check_columns.contains(&field.name().as_str()) {
103 format!("{}: (skipped)", field.name())
104 } else {
105 format!("{}: {:?}", field.name(), column)
106 }
107 })
108 .format(",\n")
109 ));
110}
111
112#[cfg(test)]
114pub(crate) fn make_encryption_manager(table_key_id: &str) -> Arc<EncryptionManager> {
115 let kms = MemoryKeyManagementClient::new();
116 kms.add_master_key(table_key_id).unwrap();
117 Arc::new(
118 EncryptionManager::builder()
119 .kms_client(Arc::new(kms) as Arc<dyn KeyManagementClient>)
120 .table_key_id(table_key_id)
121 .build(),
122 )
123}
124
125#[cfg(test)]
128pub(crate) fn encode_dv_blob(positions: impl IntoIterator<Item = u64>) -> Vec<u8> {
129 let mut bitmap = RoaringTreemap::new();
130 for pos in positions {
131 bitmap.insert(pos);
132 }
133 let mut vector = Vec::new();
134 bitmap.serialize_into(&mut vector).unwrap();
135 crate::delete_vector::frame_dv_blob(&vector)
136}
137
138pub async fn make_encrypted_table() -> Table {
146 let metadata_json = std::fs::read_to_string(format!(
147 "{}/testdata/table_metadata/TableMetadataV3ValidEncryption.json",
148 env!("CARGO_MANIFEST_DIR"),
149 ))
150 .unwrap();
151 let metadata: TableMetadata = serde_json::from_str(&metadata_json).unwrap();
152
153 let kms: Arc<dyn KeyManagementClient> = {
154 let k = MemoryKeyManagementClient::new();
155 k.add_master_key_bytes(
156 "master-1",
157 SensitiveBytes::new([
158 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
159 0x0e, 0x0f,
160 ]),
161 )
162 .unwrap();
163 Arc::new(k)
164 };
165
166 let file_io = FileIO::new_with_memory();
167
168 let manifest_list_bytes = std::fs::read(format!(
170 "{}/testdata/manifests_lists/manifest-list-v3-encrypted.avro",
171 env!("CARGO_MANIFEST_DIR"),
172 ))
173 .unwrap();
174 file_io
175 .new_output(metadata.current_snapshot().unwrap().manifest_list())
176 .unwrap()
177 .write(manifest_list_bytes.into())
178 .await
179 .unwrap();
180
181 Table::builder()
182 .metadata(metadata)
183 .metadata_location("memory:///table/metadata/v1.json")
184 .identifier(TableIdent::from_strs(["ns1", "test1"]).unwrap())
185 .file_io(file_io)
186 .kms_client(kms)
187 .runtime(test_runtime())
188 .build()
189 .unwrap()
190}