diff --git a/Cargo.lock b/Cargo.lock
index 0cf8e029..6b7c54a7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8568,6 +8568,7 @@ dependencies = [
"osv",
"packageurl",
"regex",
+ "roxmltree",
"sea-orm",
"sea-query",
"semver",
diff --git a/modules/fundamental/Cargo.toml b/modules/fundamental/Cargo.toml
index d88cebb6..79b5bfb1 100644
--- a/modules/fundamental/Cargo.toml
+++ b/modules/fundamental/Cargo.toml
@@ -53,6 +53,7 @@ log = { workspace = true }
osv = { workspace = true }
packageurl = { workspace = true }
regex = { workspace = true }
+roxmltree = { workspace = true }
semver = { workspace = true }
serde_json = { workspace = true }
serde_yml = { workspace = true }
diff --git a/modules/fundamental/tests/fundamental.rs b/modules/fundamental/tests/fundamental.rs
index 4dba2d4c..4f61d09b 100644
--- a/modules/fundamental/tests/fundamental.rs
+++ b/modules/fundamental/tests/fundamental.rs
@@ -1,2 +1,3 @@
mod advisory;
mod sbom;
+mod weakness;
diff --git a/modules/fundamental/tests/weakness/mod.rs b/modules/fundamental/tests/weakness/mod.rs
new file mode 100644
index 00000000..1119e6ec
--- /dev/null
+++ b/modules/fundamental/tests/weakness/mod.rs
@@ -0,0 +1,99 @@
+#![allow(clippy::expect_used)]
+
+use roxmltree::Document;
+use std::io::Read;
+use test_context::test_context;
+use test_log::test;
+use trustify_common::{hashing::HashingRead, model::Paginated};
+use trustify_entity::labels::Labels;
+use trustify_module_fundamental::weakness::service::WeaknessService;
+use trustify_module_ingestor::{graph::Graph, service::weakness::CweCatalogLoader};
+use trustify_test_context::{document_read, TrustifyContext};
+use zip::ZipArchive;
+
+#[test_context(TrustifyContext)]
+#[test(tokio::test)]
+async fn simple(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
+ const TOTAL_ITEMS_FOUND: u64 = 964;
+
+ let graph = Graph::new(ctx.db.clone());
+ let loader = CweCatalogLoader::new(&graph);
+ let service = WeaknessService::new(ctx.db.clone());
+
+ // extract document from zip file
+
+ let zip = document_read("cwec_latest.xml.zip").await?;
+
+ let mut archive = ZipArchive::new(zip)?;
+
+ let entry = archive.by_index(0)?;
+
+ let mut hashing = HashingRead::new(entry);
+ let mut xml = String::new();
+ hashing.read_to_string(&mut xml)?;
+ let digests = hashing.finish()?;
+
+ // load
+
+ let doc = Document::parse(&xml)?;
+ loader.load(Labels::default(), &doc, &digests).await?;
+
+ // fetch data
+
+ let all = service
+ .list_weaknesses(
+ Default::default(),
+ Paginated {
+ offset: 0,
+ limit: 10,
+ },
+ )
+ .await?;
+
+ assert_eq!(TOTAL_ITEMS_FOUND, all.total);
+
+ let w = service
+ .get_weakness("CWE-1004")
+ .await?
+ .expect("must be found");
+
+ assert_eq!(w.head.description.as_deref(), Some("The product uses a cookie to store sensitive information, but the cookie is not marked with the HttpOnly flag."));
+
+ // now update (poor man's XML update)
+
+ let xml = xml.replace("The product uses a cookie to store sensitive information, but the cookie is not marked with the HttpOnly flag.", "Foo Bar Update");
+
+ // load again
+
+ let doc = Document::parse(&xml)?;
+ loader.load(Labels::default(), &doc, &digests).await?;
+
+ // fetch data again
+
+ let all = service
+ .list_weaknesses(
+ Default::default(),
+ Paginated {
+ offset: 0,
+ limit: 10,
+ },
+ )
+ .await?;
+
+ // must be the same number of items
+
+ assert_eq!(964, all.total);
+
+ let w = service
+ .get_weakness("CWE-1004")
+ .await?
+ .expect("must be found");
+
+ // but a different description
+
+ assert_eq!(w.head.description.as_deref(), Some("Foo Bar Update"));
+
+ // done
+
+ Ok(())
+}