1use std::collections::BTreeSet;
2use std::sync::Arc;
3
4use rpc::ExtensionProvides;
5
6use super::Database;
7use crate::db::ExtensionVersionConstraints;
8use crate::{
9 db::{queries::extensions::convert_time_to_chrono, ExtensionMetadata, NewExtensionVersion},
10 test_both_dbs,
11};
12
13test_both_dbs!(
14 test_extensions,
15 test_extensions_postgres,
16 test_extensions_sqlite
17);
18
19async fn test_extensions(db: &Arc<Database>) {
20 let versions = db.get_known_extension_versions().await.unwrap();
21 assert!(versions.is_empty());
22
23 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
24 assert!(extensions.is_empty());
25
26 let t0 = time::OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
27 let t0 = time::PrimitiveDateTime::new(t0.date(), t0.time());
28
29 let t0_chrono = convert_time_to_chrono(t0);
30
31 db.insert_extension_versions(
32 &[
33 (
34 "ext1",
35 vec![
36 NewExtensionVersion {
37 name: "Extension 1".into(),
38 version: semver::Version::parse("0.0.1").unwrap(),
39 description: "an extension".into(),
40 authors: vec!["max".into()],
41 repository: "ext1/repo".into(),
42 schema_version: 1,
43 wasm_api_version: None,
44 provides: BTreeSet::default(),
45 published_at: t0,
46 },
47 NewExtensionVersion {
48 name: "Extension One".into(),
49 version: semver::Version::parse("0.0.2").unwrap(),
50 description: "a good extension".into(),
51 authors: vec!["max".into(), "marshall".into()],
52 repository: "ext1/repo".into(),
53 schema_version: 1,
54 wasm_api_version: None,
55 provides: BTreeSet::default(),
56 published_at: t0,
57 },
58 ],
59 ),
60 (
61 "ext2",
62 vec![NewExtensionVersion {
63 name: "Extension Two".into(),
64 version: semver::Version::parse("0.2.0").unwrap(),
65 description: "a great extension".into(),
66 authors: vec!["marshall".into()],
67 repository: "ext2/repo".into(),
68 schema_version: 0,
69 wasm_api_version: None,
70 provides: BTreeSet::default(),
71 published_at: t0,
72 }],
73 ),
74 ]
75 .into_iter()
76 .collect(),
77 )
78 .await
79 .unwrap();
80
81 let versions = db.get_known_extension_versions().await.unwrap();
82 assert_eq!(
83 versions,
84 [
85 ("ext1".into(), vec!["0.0.1".into(), "0.0.2".into()]),
86 ("ext2".into(), vec!["0.2.0".into()])
87 ]
88 .into_iter()
89 .collect()
90 );
91
92 // The latest version of each extension is returned.
93 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
94 assert_eq!(
95 extensions,
96 &[
97 ExtensionMetadata {
98 id: "ext1".into(),
99 manifest: rpc::ExtensionApiManifest {
100 name: "Extension One".into(),
101 version: "0.0.2".into(),
102 authors: vec!["max".into(), "marshall".into()],
103 description: Some("a good extension".into()),
104 repository: "ext1/repo".into(),
105 schema_version: Some(1),
106 wasm_api_version: None,
107 provides: BTreeSet::default(),
108 },
109 published_at: t0_chrono,
110 download_count: 0,
111 },
112 ExtensionMetadata {
113 id: "ext2".into(),
114 manifest: rpc::ExtensionApiManifest {
115 name: "Extension Two".into(),
116 version: "0.2.0".into(),
117 authors: vec!["marshall".into()],
118 description: Some("a great extension".into()),
119 repository: "ext2/repo".into(),
120 schema_version: Some(0),
121 wasm_api_version: None,
122 provides: BTreeSet::default(),
123 },
124 published_at: t0_chrono,
125 download_count: 0
126 },
127 ]
128 );
129
130 // Extensions with too new of a schema version are excluded.
131 let extensions = db.get_extensions(None, None, 0, 5).await.unwrap();
132 assert_eq!(
133 extensions,
134 &[ExtensionMetadata {
135 id: "ext2".into(),
136 manifest: rpc::ExtensionApiManifest {
137 name: "Extension Two".into(),
138 version: "0.2.0".into(),
139 authors: vec!["marshall".into()],
140 description: Some("a great extension".into()),
141 repository: "ext2/repo".into(),
142 schema_version: Some(0),
143 wasm_api_version: None,
144 provides: BTreeSet::default(),
145 },
146 published_at: t0_chrono,
147 download_count: 0
148 },]
149 );
150
151 // Record extensions being downloaded.
152 for _ in 0..7 {
153 assert!(db.record_extension_download("ext2", "0.0.2").await.unwrap());
154 }
155
156 for _ in 0..3 {
157 assert!(db.record_extension_download("ext1", "0.0.1").await.unwrap());
158 }
159
160 for _ in 0..2 {
161 assert!(db.record_extension_download("ext1", "0.0.2").await.unwrap());
162 }
163
164 // Record download returns false if the extension does not exist.
165 assert!(!db
166 .record_extension_download("no-such-extension", "0.0.2")
167 .await
168 .unwrap());
169
170 // Extensions are returned in descending order of total downloads.
171 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
172 assert_eq!(
173 extensions,
174 &[
175 ExtensionMetadata {
176 id: "ext2".into(),
177 manifest: rpc::ExtensionApiManifest {
178 name: "Extension Two".into(),
179 version: "0.2.0".into(),
180 authors: vec!["marshall".into()],
181 description: Some("a great extension".into()),
182 repository: "ext2/repo".into(),
183 schema_version: Some(0),
184 wasm_api_version: None,
185 provides: BTreeSet::default(),
186 },
187 published_at: t0_chrono,
188 download_count: 7
189 },
190 ExtensionMetadata {
191 id: "ext1".into(),
192 manifest: rpc::ExtensionApiManifest {
193 name: "Extension One".into(),
194 version: "0.0.2".into(),
195 authors: vec!["max".into(), "marshall".into()],
196 description: Some("a good extension".into()),
197 repository: "ext1/repo".into(),
198 schema_version: Some(1),
199 wasm_api_version: None,
200 provides: BTreeSet::default(),
201 },
202 published_at: t0_chrono,
203 download_count: 5,
204 },
205 ]
206 );
207
208 // Add more extensions, including a new version of `ext1`, and backfilling
209 // an older version of `ext2`.
210 db.insert_extension_versions(
211 &[
212 (
213 "ext1",
214 vec![NewExtensionVersion {
215 name: "Extension One".into(),
216 version: semver::Version::parse("0.0.3").unwrap(),
217 description: "a real good extension".into(),
218 authors: vec!["max".into(), "marshall".into()],
219 repository: "ext1/repo".into(),
220 schema_version: 1,
221 wasm_api_version: None,
222 provides: BTreeSet::default(),
223 published_at: t0,
224 }],
225 ),
226 (
227 "ext2",
228 vec![NewExtensionVersion {
229 name: "Extension Two".into(),
230 version: semver::Version::parse("0.1.0").unwrap(),
231 description: "an old extension".into(),
232 authors: vec!["marshall".into()],
233 repository: "ext2/repo".into(),
234 schema_version: 0,
235 wasm_api_version: None,
236 provides: BTreeSet::default(),
237 published_at: t0,
238 }],
239 ),
240 ]
241 .into_iter()
242 .collect(),
243 )
244 .await
245 .unwrap();
246
247 let versions = db.get_known_extension_versions().await.unwrap();
248 assert_eq!(
249 versions,
250 [
251 (
252 "ext1".into(),
253 vec!["0.0.1".into(), "0.0.2".into(), "0.0.3".into()]
254 ),
255 ("ext2".into(), vec!["0.1.0".into(), "0.2.0".into()])
256 ]
257 .into_iter()
258 .collect()
259 );
260
261 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
262 assert_eq!(
263 extensions,
264 &[
265 ExtensionMetadata {
266 id: "ext2".into(),
267 manifest: rpc::ExtensionApiManifest {
268 name: "Extension Two".into(),
269 version: "0.2.0".into(),
270 authors: vec!["marshall".into()],
271 description: Some("a great extension".into()),
272 repository: "ext2/repo".into(),
273 schema_version: Some(0),
274 wasm_api_version: None,
275 provides: BTreeSet::default(),
276 },
277 published_at: t0_chrono,
278 download_count: 7
279 },
280 ExtensionMetadata {
281 id: "ext1".into(),
282 manifest: rpc::ExtensionApiManifest {
283 name: "Extension One".into(),
284 version: "0.0.3".into(),
285 authors: vec!["max".into(), "marshall".into()],
286 description: Some("a real good extension".into()),
287 repository: "ext1/repo".into(),
288 schema_version: Some(1),
289 wasm_api_version: None,
290 provides: BTreeSet::default(),
291 },
292 published_at: t0_chrono,
293 download_count: 5,
294 },
295 ]
296 );
297}
298
299test_both_dbs!(
300 test_extensions_by_id,
301 test_extensions_by_id_postgres,
302 test_extensions_by_id_sqlite
303);
304
305async fn test_extensions_by_id(db: &Arc<Database>) {
306 let versions = db.get_known_extension_versions().await.unwrap();
307 assert!(versions.is_empty());
308
309 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
310 assert!(extensions.is_empty());
311
312 let t0 = time::OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
313 let t0 = time::PrimitiveDateTime::new(t0.date(), t0.time());
314
315 let t0_chrono = convert_time_to_chrono(t0);
316
317 db.insert_extension_versions(
318 &[
319 (
320 "ext1",
321 vec![
322 NewExtensionVersion {
323 name: "Extension 1".into(),
324 version: semver::Version::parse("0.0.1").unwrap(),
325 description: "an extension".into(),
326 authors: vec!["max".into()],
327 repository: "ext1/repo".into(),
328 schema_version: 1,
329 wasm_api_version: Some("0.0.4".into()),
330 provides: BTreeSet::from_iter([
331 ExtensionProvides::Grammars,
332 ExtensionProvides::Languages,
333 ]),
334 published_at: t0,
335 },
336 NewExtensionVersion {
337 name: "Extension 1".into(),
338 version: semver::Version::parse("0.0.2").unwrap(),
339 description: "a good extension".into(),
340 authors: vec!["max".into()],
341 repository: "ext1/repo".into(),
342 schema_version: 1,
343 wasm_api_version: Some("0.0.4".into()),
344 provides: BTreeSet::from_iter([
345 ExtensionProvides::Grammars,
346 ExtensionProvides::Languages,
347 ExtensionProvides::LanguageServers,
348 ]),
349 published_at: t0,
350 },
351 NewExtensionVersion {
352 name: "Extension 1".into(),
353 version: semver::Version::parse("0.0.3").unwrap(),
354 description: "a real good extension".into(),
355 authors: vec!["max".into(), "marshall".into()],
356 repository: "ext1/repo".into(),
357 schema_version: 1,
358 wasm_api_version: Some("0.0.5".into()),
359 provides: BTreeSet::from_iter([
360 ExtensionProvides::Grammars,
361 ExtensionProvides::Languages,
362 ExtensionProvides::LanguageServers,
363 ]),
364 published_at: t0,
365 },
366 ],
367 ),
368 (
369 "ext2",
370 vec![NewExtensionVersion {
371 name: "Extension 2".into(),
372 version: semver::Version::parse("0.2.0").unwrap(),
373 description: "a great extension".into(),
374 authors: vec!["marshall".into()],
375 repository: "ext2/repo".into(),
376 schema_version: 0,
377 wasm_api_version: None,
378 provides: BTreeSet::default(),
379 published_at: t0,
380 }],
381 ),
382 ]
383 .into_iter()
384 .collect(),
385 )
386 .await
387 .unwrap();
388
389 let extensions = db
390 .get_extensions_by_ids(
391 &["ext1"],
392 Some(&ExtensionVersionConstraints {
393 schema_versions: 1..=1,
394 wasm_api_versions: "0.0.1".parse().unwrap()..="0.0.4".parse().unwrap(),
395 }),
396 )
397 .await
398 .unwrap();
399
400 assert_eq!(
401 extensions,
402 &[ExtensionMetadata {
403 id: "ext1".into(),
404 manifest: rpc::ExtensionApiManifest {
405 name: "Extension 1".into(),
406 version: "0.0.2".into(),
407 authors: vec!["max".into()],
408 description: Some("a good extension".into()),
409 repository: "ext1/repo".into(),
410 schema_version: Some(1),
411 wasm_api_version: Some("0.0.4".into()),
412 provides: BTreeSet::from_iter([
413 ExtensionProvides::Grammars,
414 ExtensionProvides::Languages,
415 ExtensionProvides::LanguageServers,
416 ]),
417 },
418 published_at: t0_chrono,
419 download_count: 0,
420 }]
421 );
422}