1use std::collections::BTreeSet;
2use std::sync::Arc;
3
4use rpc::ExtensionProvides;
5
6use super::Database;
7use crate::db::ExtensionVersionConstraints;
8use crate::{
9 db::{ExtensionMetadata, NewExtensionVersion, queries::extensions::convert_time_to_chrono},
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!(
166 !db.record_extension_download("no-such-extension", "0.0.2")
167 .await
168 .unwrap()
169 );
170
171 // Extensions are returned in descending order of total downloads.
172 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
173 assert_eq!(
174 extensions,
175 &[
176 ExtensionMetadata {
177 id: "ext2".into(),
178 manifest: rpc::ExtensionApiManifest {
179 name: "Extension Two".into(),
180 version: "0.2.0".into(),
181 authors: vec!["marshall".into()],
182 description: Some("a great extension".into()),
183 repository: "ext2/repo".into(),
184 schema_version: Some(0),
185 wasm_api_version: None,
186 provides: BTreeSet::default(),
187 },
188 published_at: t0_chrono,
189 download_count: 7
190 },
191 ExtensionMetadata {
192 id: "ext1".into(),
193 manifest: rpc::ExtensionApiManifest {
194 name: "Extension One".into(),
195 version: "0.0.2".into(),
196 authors: vec!["max".into(), "marshall".into()],
197 description: Some("a good extension".into()),
198 repository: "ext1/repo".into(),
199 schema_version: Some(1),
200 wasm_api_version: None,
201 provides: BTreeSet::default(),
202 },
203 published_at: t0_chrono,
204 download_count: 5,
205 },
206 ]
207 );
208
209 // Add more extensions, including a new version of `ext1`, and backfilling
210 // an older version of `ext2`.
211 db.insert_extension_versions(
212 &[
213 (
214 "ext1",
215 vec![NewExtensionVersion {
216 name: "Extension One".into(),
217 version: semver::Version::parse("0.0.3").unwrap(),
218 description: "a real good extension".into(),
219 authors: vec!["max".into(), "marshall".into()],
220 repository: "ext1/repo".into(),
221 schema_version: 1,
222 wasm_api_version: None,
223 provides: BTreeSet::default(),
224 published_at: t0,
225 }],
226 ),
227 (
228 "ext2",
229 vec![NewExtensionVersion {
230 name: "Extension Two".into(),
231 version: semver::Version::parse("0.1.0").unwrap(),
232 description: "an old extension".into(),
233 authors: vec!["marshall".into()],
234 repository: "ext2/repo".into(),
235 schema_version: 0,
236 wasm_api_version: None,
237 provides: BTreeSet::default(),
238 published_at: t0,
239 }],
240 ),
241 ]
242 .into_iter()
243 .collect(),
244 )
245 .await
246 .unwrap();
247
248 let versions = db.get_known_extension_versions().await.unwrap();
249 assert_eq!(
250 versions,
251 [
252 (
253 "ext1".into(),
254 vec!["0.0.1".into(), "0.0.2".into(), "0.0.3".into()]
255 ),
256 ("ext2".into(), vec!["0.1.0".into(), "0.2.0".into()])
257 ]
258 .into_iter()
259 .collect()
260 );
261
262 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
263 assert_eq!(
264 extensions,
265 &[
266 ExtensionMetadata {
267 id: "ext2".into(),
268 manifest: rpc::ExtensionApiManifest {
269 name: "Extension Two".into(),
270 version: "0.2.0".into(),
271 authors: vec!["marshall".into()],
272 description: Some("a great extension".into()),
273 repository: "ext2/repo".into(),
274 schema_version: Some(0),
275 wasm_api_version: None,
276 provides: BTreeSet::default(),
277 },
278 published_at: t0_chrono,
279 download_count: 7
280 },
281 ExtensionMetadata {
282 id: "ext1".into(),
283 manifest: rpc::ExtensionApiManifest {
284 name: "Extension One".into(),
285 version: "0.0.3".into(),
286 authors: vec!["max".into(), "marshall".into()],
287 description: Some("a real good extension".into()),
288 repository: "ext1/repo".into(),
289 schema_version: Some(1),
290 wasm_api_version: None,
291 provides: BTreeSet::default(),
292 },
293 published_at: t0_chrono,
294 download_count: 5,
295 },
296 ]
297 );
298}
299
300test_both_dbs!(
301 test_extensions_by_id,
302 test_extensions_by_id_postgres,
303 test_extensions_by_id_sqlite
304);
305
306async fn test_extensions_by_id(db: &Arc<Database>) {
307 let versions = db.get_known_extension_versions().await.unwrap();
308 assert!(versions.is_empty());
309
310 let extensions = db.get_extensions(None, None, 1, 5).await.unwrap();
311 assert!(extensions.is_empty());
312
313 let t0 = time::OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
314 let t0 = time::PrimitiveDateTime::new(t0.date(), t0.time());
315
316 let t0_chrono = convert_time_to_chrono(t0);
317
318 db.insert_extension_versions(
319 &[
320 (
321 "ext1",
322 vec![
323 NewExtensionVersion {
324 name: "Extension 1".into(),
325 version: semver::Version::parse("0.0.1").unwrap(),
326 description: "an extension".into(),
327 authors: vec!["max".into()],
328 repository: "ext1/repo".into(),
329 schema_version: 1,
330 wasm_api_version: Some("0.0.4".into()),
331 provides: BTreeSet::from_iter([
332 ExtensionProvides::Grammars,
333 ExtensionProvides::Languages,
334 ]),
335 published_at: t0,
336 },
337 NewExtensionVersion {
338 name: "Extension 1".into(),
339 version: semver::Version::parse("0.0.2").unwrap(),
340 description: "a good extension".into(),
341 authors: vec!["max".into()],
342 repository: "ext1/repo".into(),
343 schema_version: 1,
344 wasm_api_version: Some("0.0.4".into()),
345 provides: BTreeSet::from_iter([
346 ExtensionProvides::Grammars,
347 ExtensionProvides::Languages,
348 ExtensionProvides::LanguageServers,
349 ]),
350 published_at: t0,
351 },
352 NewExtensionVersion {
353 name: "Extension 1".into(),
354 version: semver::Version::parse("0.0.3").unwrap(),
355 description: "a real good extension".into(),
356 authors: vec!["max".into(), "marshall".into()],
357 repository: "ext1/repo".into(),
358 schema_version: 1,
359 wasm_api_version: Some("0.0.5".into()),
360 provides: BTreeSet::from_iter([
361 ExtensionProvides::Grammars,
362 ExtensionProvides::Languages,
363 ExtensionProvides::LanguageServers,
364 ]),
365 published_at: t0,
366 },
367 ],
368 ),
369 (
370 "ext2",
371 vec![NewExtensionVersion {
372 name: "Extension 2".into(),
373 version: semver::Version::parse("0.2.0").unwrap(),
374 description: "a great extension".into(),
375 authors: vec!["marshall".into()],
376 repository: "ext2/repo".into(),
377 schema_version: 0,
378 wasm_api_version: None,
379 provides: BTreeSet::default(),
380 published_at: t0,
381 }],
382 ),
383 ]
384 .into_iter()
385 .collect(),
386 )
387 .await
388 .unwrap();
389
390 let extensions = db
391 .get_extensions_by_ids(
392 &["ext1"],
393 Some(&ExtensionVersionConstraints {
394 schema_versions: 1..=1,
395 wasm_api_versions: "0.0.1".parse().unwrap()..="0.0.4".parse().unwrap(),
396 }),
397 )
398 .await
399 .unwrap();
400
401 assert_eq!(
402 extensions,
403 &[ExtensionMetadata {
404 id: "ext1".into(),
405 manifest: rpc::ExtensionApiManifest {
406 name: "Extension 1".into(),
407 version: "0.0.2".into(),
408 authors: vec!["max".into()],
409 description: Some("a good extension".into()),
410 repository: "ext1/repo".into(),
411 schema_version: Some(1),
412 wasm_api_version: Some("0.0.4".into()),
413 provides: BTreeSet::from_iter([
414 ExtensionProvides::Grammars,
415 ExtensionProvides::Languages,
416 ExtensionProvides::LanguageServers,
417 ]),
418 },
419 published_at: t0_chrono,
420 download_count: 0,
421 }]
422 );
423}