diff --git a/python-scripts/metadatavalidator/README.rst b/python-scripts/metadatavalidator/README.rst
index 709207fdd..9ea4eec46 100644
--- a/python-scripts/metadatavalidator/README.rst
+++ b/python-scripts/metadatavalidator/README.rst
@@ -79,3 +79,7 @@ recognized:
* :var:`require_meta_architecture`: Requires a ```` tag or not.
* :var:`valid_meta_architecture`: Lists the valid architecture names for ``/``.
+
+ * :var:`require_meta_category`: Requires a ```` tag or not.
+
+ * :var:`valid_meta_category`: Lists the valid category names for ``/``.
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/metadatavalidator.ini b/python-scripts/metadatavalidator/metadatavalidator.ini
index a502e414c..540e22b3a 100644
--- a/python-scripts/metadatavalidator/metadatavalidator.ini
+++ b/python-scripts/metadatavalidator/metadatavalidator.ini
@@ -27,4 +27,8 @@ require_meta_platform = off
#
require_meta_architecture = off
-valid_meta_architecture = Arm, AMD64/Intel\u00a064, POWER, IBM LinuxONE
\ No newline at end of file
+valid_meta_architecture = Arm, AMD64/Intel\u00a064, POWER, IBM LinuxONE
+
+#
+require_meta_category = off
+valid_meta_category = 3rd Party, Cloud, Containerization, Developer Tools, High Availability, Tuning & Performance, SAP, Security, Storage, Systems Management, Virtualization
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/src/metadatavalidator/checks/__init__.py b/python-scripts/metadatavalidator/src/metadatavalidator/checks/__init__.py
index 6b8133c88..9505d357b 100644
--- a/python-scripts/metadatavalidator/src/metadatavalidator/checks/__init__.py
+++ b/python-scripts/metadatavalidator/src/metadatavalidator/checks/__init__.py
@@ -17,6 +17,7 @@
check_meta_techpartner,
check_meta_platform,
check_meta_architecture,
+ check_meta_category,
)
# Keep the order. The next item is dependent on the previous item.
@@ -36,5 +37,6 @@
"check_meta_techpartner",
"check_meta_platform",
"check_meta_architecture",
+ "check_meta_category",
]
diff --git a/python-scripts/metadatavalidator/src/metadatavalidator/checks/check_meta.py b/python-scripts/metadatavalidator/src/metadatavalidator/checks/check_meta.py
index 9a33a2fb7..6b13ff9f7 100644
--- a/python-scripts/metadatavalidator/src/metadatavalidator/checks/check_meta.py
+++ b/python-scripts/metadatavalidator/src/metadatavalidator/checks/check_meta.py
@@ -166,4 +166,48 @@ def check_meta_architecture(tree: etree._ElementTree,
f"Unknown architecture(s) {wrong_items}. "
f"Allowed are {valid_archs}."
)
- print(">>>", wrong_items, valid_archs, archs)
+
+
+def check_meta_category(tree: etree._ElementTree,
+ config: dict[t.Any, t.Any]):
+ """Checks for a element"""
+ root = tree.getroot()
+ meta = root.find("./d:info/d:meta[@name='category']",
+ namespaces=NAMESPACES)
+ required = config.get("metadata", {}).get("require_meta_category", False)
+ if meta is None:
+ if required:
+ raise InvalidValueError(
+ f"Couldn't find required meta[@name='category'] element "
+ f"in {root.tag}."
+ )
+ return
+
+ valid_cats = [
+ x.strip() for x in config.get("metadata", {}
+ ).get("valid_meta_category", [])
+ if x
+ ]
+
+ # Do we have children?
+ cats = [tag.text.strip() for tag in meta.iterchildren()]
+ if not cats:
+ raise InvalidValueError(
+ f"Couldn't find any child elements in meta[@name='category'] "
+ f"(line {meta.sourceline})."
+ )
+
+ # Are they unique?
+ if len(cats) != len(set(cats)):
+ raise InvalidValueError(
+ f"Duplicate categories found in meta[@name='category'] "
+ f"(line {meta.sourceline})."
+ )
+
+ # Do we have items that don't conform to our predefined list?
+ wrong_items = set(cats) - set(valid_cats)
+ if wrong_items:
+ raise InvalidValueError(
+ f"Unknown category(ies) {wrong_items}. "
+ f"Allowed are {valid_cats}."
+ )
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/src/metadatavalidator/config.py b/python-scripts/metadatavalidator/src/metadatavalidator/config.py
index fd0fa247e..9dee23582 100644
--- a/python-scripts/metadatavalidator/src/metadatavalidator/config.py
+++ b/python-scripts/metadatavalidator/src/metadatavalidator/config.py
@@ -107,6 +107,30 @@ def validate_and_convert_config(config: configparser.ConfigParser) -> dict[t.Any
)
theconfig.setdefault("metadata", {})["require_meta_series"] = require_meta_series
+ # architectures
+ require_meta_architecture = truefalse(
+ theconfig.get("metadata", {}).get("require_meta_architecture", False)
+ )
+ theconfig.setdefault("metadata", {})["require_meta_architecture"] = require_meta_architecture
+ try:
+ architectures = split.split(theconfig.get("metadata", {}).get("valid_meta_architecture", []))
+ theconfig.setdefault("metadata", {})["valid_meta_architecture"] = architectures
+ except TypeError:
+ raise MissingKeyError("metadata.valid_meta_architecture")
+
+
+ # categories
+ require_meta_category = truefalse(
+ theconfig.get("metadata", {}).get("require_meta_category", False)
+ )
+ theconfig.setdefault("metadata", {})["require_meta_category"] = require_meta_category
+ try:
+ categories = split.split(theconfig.get("metadata", {}).get("valid_meta_category", []))
+ theconfig.setdefault("metadata", {})["valid_meta_category"] = categories
+ except TypeError:
+ raise MissingKeyError("metadata.valid_meta_category")
+
+
# Store the configfiles
theconfig["configfiles"] = getattr(config, "configfiles")
return theconfig
diff --git a/python-scripts/metadatavalidator/tests/integration/badcase1/article.xml b/python-scripts/metadatavalidator/tests/integration/badcase1/article.xml
new file mode 100644
index 000000000..d766a4375
--- /dev/null
+++ b/python-scripts/metadatavalidator/tests/integration/badcase1/article.xml
@@ -0,0 +1,7 @@
+
+
+ Test
+ The very long, long, long long SEO title
+
+
+
diff --git a/python-scripts/metadatavalidator/tests/integration/badcase1/config-test.ini b/python-scripts/metadatavalidator/tests/integration/badcase1/config-test.ini
new file mode 100644
index 000000000..636b78b88
--- /dev/null
+++ b/python-scripts/metadatavalidator/tests/integration/badcase1/config-test.ini
@@ -0,0 +1,23 @@
+[validator]
+file_extension = .xml
+check_root_elements = book article topic
+valid_languages = en-us
+
+[metadata]
+revhistory = 0
+require_xmlid_on_revision = 0
+
+#
+require_meta_title = on
+meta_title_length = 55
+
+#
+require_meta_description = off
+meta_description_length = 150
+
+require_meta_architecture = off
+valid_meta_architecture = Arm, AMD64/Intel\u00a064, POWER, IBM LinuxONE
+
+#
+require_meta_category = off
+valid_meta_category = 3rd Party, Cloud, Containerization, Developer Tools, High Availability, Tuning & Performance, SAP, Security, Storage, Systems Management, Virtualization
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/tests/integration/badcase1/test_badcase1.py b/python-scripts/metadatavalidator/tests/integration/badcase1/test_badcase1.py
new file mode 100644
index 000000000..a769f5446
--- /dev/null
+++ b/python-scripts/metadatavalidator/tests/integration/badcase1/test_badcase1.py
@@ -0,0 +1,23 @@
+import os.path
+import json
+import pytest
+
+from metadatavalidator.cli import main
+
+
+BASEDIR = os.path.dirname(os.path.realpath(__file__))
+RELATIVE_PATH = os.path.relpath(BASEDIR, os.getcwd())
+
+
+def test_case1_integration(capsys):
+ cli = ["--config", f"{BASEDIR}/config-test.ini",
+ "--format", "json", # needed to avoid formatting issues
+ f"{RELATIVE_PATH}/article.xml"]
+
+ result = main(cli)
+ captured = capsys.readouterr()
+ assert result == 0
+ result = json.loads(captured.out)
+ assert result[0]['errors'] == []
+ assert result[0]['xmlfile'] == f"{RELATIVE_PATH}/article.xml"
+
diff --git a/python-scripts/metadatavalidator/tests/integration/goodcase1/config-test.ini b/python-scripts/metadatavalidator/tests/integration/goodcase1/config-test.ini
index 08afdc93d..636b78b88 100644
--- a/python-scripts/metadatavalidator/tests/integration/goodcase1/config-test.ini
+++ b/python-scripts/metadatavalidator/tests/integration/goodcase1/config-test.ini
@@ -14,3 +14,10 @@ meta_title_length = 55
#
require_meta_description = off
meta_description_length = 150
+
+require_meta_architecture = off
+valid_meta_architecture = Arm, AMD64/Intel\u00a064, POWER, IBM LinuxONE
+
+#
+require_meta_category = off
+valid_meta_category = 3rd Party, Cloud, Containerization, Developer Tools, High Availability, Tuning & Performance, SAP, Security, Storage, Systems Management, Virtualization
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/tests/unit/checks/test_check_meta.py b/python-scripts/metadatavalidator/tests/unit/checks/test_check_meta.py
index 01e9734a2..c3e51ecce 100644
--- a/python-scripts/metadatavalidator/tests/unit/checks/test_check_meta.py
+++ b/python-scripts/metadatavalidator/tests/unit/checks/test_check_meta.py
@@ -8,6 +8,7 @@
check_meta_techpartner,
check_meta_platform,
check_meta_architecture,
+ check_meta_category,
)
from metadatavalidator.exceptions import InvalidValueError
@@ -406,3 +407,19 @@ def test_check_unknown_child_meta_architecture(xmlparser):
with pytest.raises(InvalidValueError,
match=r".*Unknown architecture.*"):
check_meta_architecture(tree, config)
+
+
+def test_meta_category(xmlparser):
+ xmlcontent = """
+
+ Test
+
+ Systems Management
+
+
+
+"""
+ tree = etree.ElementTree(etree.fromstring(xmlcontent, parser=xmlparser))
+ config = dict(metadata=dict(require_meta_category=True,
+ valid_meta_category=["Systems Management"]))
+ assert check_meta_category(tree, config) is None
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/tests/unit/data/metadatavalidator.ini b/python-scripts/metadatavalidator/tests/unit/data/metadatavalidator.ini
index 9dcf1e744..e9cc4c1b8 100644
--- a/python-scripts/metadatavalidator/tests/unit/data/metadatavalidator.ini
+++ b/python-scripts/metadatavalidator/tests/unit/data/metadatavalidator.ini
@@ -13,6 +13,20 @@ meta_title_length = 55
#
require_meta_description = off
meta_description_length = 150
-#
+
+#
require_meta_series = off
-valid_meta_series = Products & Solutions, Best Practices, Technical References
\ No newline at end of file
+valid_meta_series = Products & Solutions, Best Practices, Technical References
+
+#
+require_meta_techpartner = off
+
+#
+require_meta_platform = off
+
+#
+require_meta_architecture = off
+valid_meta_architecture = Arm, AMD64/Intel\u00a064, POWER, IBM LinuxONE
+
+require_meta_category = off
+valid_meta_category = 3rd Party, Cloud, Containerization, Developer Tools, High Availability, Tuning & Performance, SAP, Security, Storage, Systems Management, Virtualization
\ No newline at end of file
diff --git a/python-scripts/metadatavalidator/tests/unit/test_script_config.py b/python-scripts/metadatavalidator/tests/unit/test_script_config.py
index 189204547..b9dbdd7f1 100644
--- a/python-scripts/metadatavalidator/tests/unit/test_script_config.py
+++ b/python-scripts/metadatavalidator/tests/unit/test_script_config.py
@@ -3,8 +3,16 @@
import pytest
-from metadatavalidator.config import readconfig, validate_and_convert_config, truefalse
-from metadatavalidator.exceptions import MissingKeyError, MissingSectionError, NoConfigFilesFoundError
+from metadatavalidator.config import (
+ readconfig,
+ validate_and_convert_config,
+ truefalse,
+ )
+from metadatavalidator.exceptions import (
+ MissingKeyError,
+ MissingSectionError,
+ NoConfigFilesFoundError,
+ )
def create_config():
config = ConfigParser()
@@ -19,6 +27,8 @@ def create_config():
config.set("metadata", "meta_title_length", "50")
config.set("metadata", "meta_description_length", "150")
#
+ config.set("metadata", "valid_meta_architecture", "A, B, C")
+ config.set("metadata", "valid_meta_category", "D, E, F")
setattr(config, "configfiles", None)
return config
@@ -49,7 +59,8 @@ def test_valid_validate_and_convert_config():
assert result.get("validator") == {
"check_root_elements": ["book", "article"],
"file_extension": ".xml",
- "valid_languages": ["en-us", "de-de",]
+ "valid_languages": ["en-us", "de-de",],
+ # ""
}