extension_tests.rs

  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}