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::{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}