
This PR modifies the extension installation and update process to respect version constraints (schema version and Wasm API version) to ensure only compatible versions of extensions are able to be installed. To achieve this there is a new `GET /extensions/updates` endpoint that will return extension versions based on the provided constraints. Release Notes: - N/A --------- Co-authored-by: Max <max@zed.dev>
386 lines
13 KiB
Rust
386 lines
13 KiB
Rust
use super::Database;
|
|
use crate::db::ExtensionVersionConstraints;
|
|
use crate::{
|
|
db::{queries::extensions::convert_time_to_chrono, ExtensionMetadata, NewExtensionVersion},
|
|
test_both_dbs,
|
|
};
|
|
use std::sync::Arc;
|
|
|
|
test_both_dbs!(
|
|
test_extensions,
|
|
test_extensions_postgres,
|
|
test_extensions_sqlite
|
|
);
|
|
|
|
async fn test_extensions(db: &Arc<Database>) {
|
|
let versions = db.get_known_extension_versions().await.unwrap();
|
|
assert!(versions.is_empty());
|
|
|
|
let extensions = db.get_extensions(None, 1, 5).await.unwrap();
|
|
assert!(extensions.is_empty());
|
|
|
|
let t0 = time::OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
|
|
let t0 = time::PrimitiveDateTime::new(t0.date(), t0.time());
|
|
|
|
let t0_chrono = convert_time_to_chrono(t0);
|
|
|
|
db.insert_extension_versions(
|
|
&[
|
|
(
|
|
"ext1",
|
|
vec![
|
|
NewExtensionVersion {
|
|
name: "Extension 1".into(),
|
|
version: semver::Version::parse("0.0.1").unwrap(),
|
|
description: "an extension".into(),
|
|
authors: vec!["max".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
},
|
|
NewExtensionVersion {
|
|
name: "Extension One".into(),
|
|
version: semver::Version::parse("0.0.2").unwrap(),
|
|
description: "a good extension".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"ext2",
|
|
vec![NewExtensionVersion {
|
|
name: "Extension Two".into(),
|
|
version: semver::Version::parse("0.2.0").unwrap(),
|
|
description: "a great extension".into(),
|
|
authors: vec!["marshall".into()],
|
|
repository: "ext2/repo".into(),
|
|
schema_version: 0,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
}],
|
|
),
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let versions = db.get_known_extension_versions().await.unwrap();
|
|
assert_eq!(
|
|
versions,
|
|
[
|
|
("ext1".into(), vec!["0.0.1".into(), "0.0.2".into()]),
|
|
("ext2".into(), vec!["0.2.0".into()])
|
|
]
|
|
.into_iter()
|
|
.collect()
|
|
);
|
|
|
|
// The latest version of each extension is returned.
|
|
let extensions = db.get_extensions(None, 1, 5).await.unwrap();
|
|
assert_eq!(
|
|
extensions,
|
|
&[
|
|
ExtensionMetadata {
|
|
id: "ext1".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension One".into(),
|
|
version: "0.0.2".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
description: Some("a good extension".into()),
|
|
repository: "ext1/repo".into(),
|
|
schema_version: Some(1),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 0,
|
|
},
|
|
ExtensionMetadata {
|
|
id: "ext2".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension Two".into(),
|
|
version: "0.2.0".into(),
|
|
authors: vec!["marshall".into()],
|
|
description: Some("a great extension".into()),
|
|
repository: "ext2/repo".into(),
|
|
schema_version: Some(0),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 0
|
|
},
|
|
]
|
|
);
|
|
|
|
// Extensions with too new of a schema version are excluded.
|
|
let extensions = db.get_extensions(None, 0, 5).await.unwrap();
|
|
assert_eq!(
|
|
extensions,
|
|
&[ExtensionMetadata {
|
|
id: "ext2".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension Two".into(),
|
|
version: "0.2.0".into(),
|
|
authors: vec!["marshall".into()],
|
|
description: Some("a great extension".into()),
|
|
repository: "ext2/repo".into(),
|
|
schema_version: Some(0),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 0
|
|
},]
|
|
);
|
|
|
|
// Record extensions being downloaded.
|
|
for _ in 0..7 {
|
|
assert!(db.record_extension_download("ext2", "0.0.2").await.unwrap());
|
|
}
|
|
|
|
for _ in 0..3 {
|
|
assert!(db.record_extension_download("ext1", "0.0.1").await.unwrap());
|
|
}
|
|
|
|
for _ in 0..2 {
|
|
assert!(db.record_extension_download("ext1", "0.0.2").await.unwrap());
|
|
}
|
|
|
|
// Record download returns false if the extension does not exist.
|
|
assert!(!db
|
|
.record_extension_download("no-such-extension", "0.0.2")
|
|
.await
|
|
.unwrap());
|
|
|
|
// Extensions are returned in descending order of total downloads.
|
|
let extensions = db.get_extensions(None, 1, 5).await.unwrap();
|
|
assert_eq!(
|
|
extensions,
|
|
&[
|
|
ExtensionMetadata {
|
|
id: "ext2".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension Two".into(),
|
|
version: "0.2.0".into(),
|
|
authors: vec!["marshall".into()],
|
|
description: Some("a great extension".into()),
|
|
repository: "ext2/repo".into(),
|
|
schema_version: Some(0),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 7
|
|
},
|
|
ExtensionMetadata {
|
|
id: "ext1".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension One".into(),
|
|
version: "0.0.2".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
description: Some("a good extension".into()),
|
|
repository: "ext1/repo".into(),
|
|
schema_version: Some(1),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 5,
|
|
},
|
|
]
|
|
);
|
|
|
|
// Add more extensions, including a new version of `ext1`, and backfilling
|
|
// an older version of `ext2`.
|
|
db.insert_extension_versions(
|
|
&[
|
|
(
|
|
"ext1",
|
|
vec![NewExtensionVersion {
|
|
name: "Extension One".into(),
|
|
version: semver::Version::parse("0.0.3").unwrap(),
|
|
description: "a real good extension".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
}],
|
|
),
|
|
(
|
|
"ext2",
|
|
vec![NewExtensionVersion {
|
|
name: "Extension Two".into(),
|
|
version: semver::Version::parse("0.1.0").unwrap(),
|
|
description: "an old extension".into(),
|
|
authors: vec!["marshall".into()],
|
|
repository: "ext2/repo".into(),
|
|
schema_version: 0,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
}],
|
|
),
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let versions = db.get_known_extension_versions().await.unwrap();
|
|
assert_eq!(
|
|
versions,
|
|
[
|
|
(
|
|
"ext1".into(),
|
|
vec!["0.0.1".into(), "0.0.2".into(), "0.0.3".into()]
|
|
),
|
|
("ext2".into(), vec!["0.1.0".into(), "0.2.0".into()])
|
|
]
|
|
.into_iter()
|
|
.collect()
|
|
);
|
|
|
|
let extensions = db.get_extensions(None, 1, 5).await.unwrap();
|
|
assert_eq!(
|
|
extensions,
|
|
&[
|
|
ExtensionMetadata {
|
|
id: "ext2".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension Two".into(),
|
|
version: "0.2.0".into(),
|
|
authors: vec!["marshall".into()],
|
|
description: Some("a great extension".into()),
|
|
repository: "ext2/repo".into(),
|
|
schema_version: Some(0),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 7
|
|
},
|
|
ExtensionMetadata {
|
|
id: "ext1".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension One".into(),
|
|
version: "0.0.3".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
description: Some("a real good extension".into()),
|
|
repository: "ext1/repo".into(),
|
|
schema_version: Some(1),
|
|
wasm_api_version: None,
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 5,
|
|
},
|
|
]
|
|
);
|
|
}
|
|
|
|
test_both_dbs!(
|
|
test_extensions_by_id,
|
|
test_extensions_by_id_postgres,
|
|
test_extensions_by_id_sqlite
|
|
);
|
|
|
|
async fn test_extensions_by_id(db: &Arc<Database>) {
|
|
let versions = db.get_known_extension_versions().await.unwrap();
|
|
assert!(versions.is_empty());
|
|
|
|
let extensions = db.get_extensions(None, 1, 5).await.unwrap();
|
|
assert!(extensions.is_empty());
|
|
|
|
let t0 = time::OffsetDateTime::from_unix_timestamp_nanos(0).unwrap();
|
|
let t0 = time::PrimitiveDateTime::new(t0.date(), t0.time());
|
|
|
|
let t0_chrono = convert_time_to_chrono(t0);
|
|
|
|
db.insert_extension_versions(
|
|
&[
|
|
(
|
|
"ext1",
|
|
vec![
|
|
NewExtensionVersion {
|
|
name: "Extension 1".into(),
|
|
version: semver::Version::parse("0.0.1").unwrap(),
|
|
description: "an extension".into(),
|
|
authors: vec!["max".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: Some("0.0.4".into()),
|
|
published_at: t0,
|
|
},
|
|
NewExtensionVersion {
|
|
name: "Extension 1".into(),
|
|
version: semver::Version::parse("0.0.2").unwrap(),
|
|
description: "a good extension".into(),
|
|
authors: vec!["max".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: Some("0.0.4".into()),
|
|
published_at: t0,
|
|
},
|
|
NewExtensionVersion {
|
|
name: "Extension 1".into(),
|
|
version: semver::Version::parse("0.0.3").unwrap(),
|
|
description: "a real good extension".into(),
|
|
authors: vec!["max".into(), "marshall".into()],
|
|
repository: "ext1/repo".into(),
|
|
schema_version: 1,
|
|
wasm_api_version: Some("0.0.5".into()),
|
|
published_at: t0,
|
|
},
|
|
],
|
|
),
|
|
(
|
|
"ext2",
|
|
vec![NewExtensionVersion {
|
|
name: "Extension 2".into(),
|
|
version: semver::Version::parse("0.2.0").unwrap(),
|
|
description: "a great extension".into(),
|
|
authors: vec!["marshall".into()],
|
|
repository: "ext2/repo".into(),
|
|
schema_version: 0,
|
|
wasm_api_version: None,
|
|
published_at: t0,
|
|
}],
|
|
),
|
|
]
|
|
.into_iter()
|
|
.collect(),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let extensions = db
|
|
.get_extensions_by_ids(
|
|
&["ext1"],
|
|
Some(&ExtensionVersionConstraints {
|
|
schema_versions: 1..=1,
|
|
wasm_api_versions: "0.0.1".parse().unwrap()..="0.0.4".parse().unwrap(),
|
|
}),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
extensions,
|
|
&[ExtensionMetadata {
|
|
id: "ext1".into(),
|
|
manifest: rpc::ExtensionApiManifest {
|
|
name: "Extension 1".into(),
|
|
version: "0.0.2".into(),
|
|
authors: vec!["max".into()],
|
|
description: Some("a good extension".into()),
|
|
repository: "ext1/repo".into(),
|
|
schema_version: Some(1),
|
|
wasm_api_version: Some("0.0.4".into()),
|
|
},
|
|
published_at: t0_chrono,
|
|
download_count: 0,
|
|
}]
|
|
);
|
|
}
|