This is an automated email from the ASF dual-hosted git repository.
FreeOnePlus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git
The following commit(s) were added to refs/heads/master by this push:
new bb63ec1 feat: support Doris-lineage distribution version brands (#213)
bb63ec1 is described below
commit bb63ec1821d16b319f96636dd90a8675885bfe87
Author: Yijia Su <[email protected]>
AuthorDate: Thu Aug 13 23:16:35 2026 +0800
feat: support Doris-lineage distribution version brands (#213)
Recognize explicitly configured distribution brand tokens while preserving
the project minimum, feature ranges, mixed-component checks, and evidence
provenance boundaries. Resolve the branch against current master and close the
remaining parser and certification edge cases identified in review.
Co-authored-by: Jmmt-mingrui <[email protected]>
---
docs/reference/configuration.md | 1 +
docs/reference/configuration.zh-CN.md | 1 +
doris_mcp_server/main.py | 3 +
doris_mcp_server/tools/capability_detector.py | 31 ++-
doris_mcp_server/tools/doris_feature_matrix.py | 139 ++++++++++++--
doris_mcp_server/tools/doris_version.py | 82 ++++++--
doris_mcp_server/tools/tools_manager.py | 9 +
doris_mcp_server/utils/config.py | 30 +++
doris_mcp_server/utils/version_brands.py | 62 ++++++
test/protocol/test_multiworker_config.py | 26 +++
test/tools/test_capability_detector.py | 173 ++++++++++++++++-
test/tools/test_doris_feature_matrix.py | 255 ++++++++++++++++++++++++-
test/tools/test_doris_version.py | 106 ++++++++++
13 files changed, 879 insertions(+), 39 deletions(-)
diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md
index ff1be41..38453bd 100644
--- a/docs/reference/configuration.md
+++ b/docs/reference/configuration.md
@@ -123,6 +123,7 @@ Increasing one variable does not remove the absolute safety
cap.
| `CAPABILITY_SNAPSHOT_TTL_SECONDS` | route-private snapshot lifetime |
| `CAPABILITY_PROBE_TIMEOUT_SECONDS` | bounded probe timeout |
| `CAPABILITY_STALE_GRACE_SECONDS` | maximum stale fallback window |
+| `CAPABILITY_VERSION_BRAND_ALIASES` | comma-separated extra
`@@version_comment` brand tokens for Doris-lineage distributions |
| `MCP_TOOL_PROVIDERS` | comma-separated exact custom provider allowlist |
Governance:
diff --git a/docs/reference/configuration.zh-CN.md
b/docs/reference/configuration.zh-CN.md
index dd09ea9..8509836 100644
--- a/docs/reference/configuration.zh-CN.md
+++ b/docs/reference/configuration.zh-CN.md
@@ -118,6 +118,7 @@ HTTP 安全:
| `CAPABILITY_SNAPSHOT_TTL_SECONDS` | 路由私有 Snapshot Lifetime |
| `CAPABILITY_PROBE_TIMEOUT_SECONDS` | 有界 Probe Timeout |
| `CAPABILITY_STALE_GRACE_SECONDS` | 最大 Stale Fallback Window |
+| `CAPABILITY_VERSION_BRAND_ALIASES` | 逗号分隔的额外 `@@version_comment` 品牌标识,用于
Doris 系发行版 |
| `MCP_TOOL_PROVIDERS` | 逗号分隔的精确 Custom Provider Allowlist |
Governance:
diff --git a/doris_mcp_server/main.py b/doris_mcp_server/main.py
index e0611a7..c996d6a 100644
--- a/doris_mcp_server/main.py
+++ b/doris_mcp_server/main.py
@@ -109,6 +109,9 @@ def _multiworker_environment(
"CAPABILITY_STALE_GRACE_SECONDS": str(
config.capability.stale_grace_seconds
),
+ "CAPABILITY_VERSION_BRAND_ALIASES": ",".join(
+ config.capability.version_brand_aliases
+ ),
"GOVERNANCE_MAX_SAMPLE_RATIO": str(
config.governance.max_sample_ratio
),
diff --git a/doris_mcp_server/tools/capability_detector.py
b/doris_mcp_server/tools/capability_detector.py
index 6d115c4..88c35be 100644
--- a/doris_mcp_server/tools/capability_detector.py
+++ b/doris_mcp_server/tools/capability_detector.py
@@ -611,7 +611,7 @@ class DorisCapabilityDetector:
frontend_rows,
fallback=version,
)
- backends = _backend_versions(backend_rows)
+ backends = _backend_versions(backend_rows, default_brand=version.brand)
versions = DorisClusterVersionVector(
master_fe=master_fe,
follower_fes=follower_fes,
@@ -2436,13 +2436,22 @@ def _nonnegative_int(value: Any | None) -> int:
return max(0, parsed)
-def _component_version(value: Any) -> DorisVersion:
+def _component_version(
+ value: Any,
+ *,
+ default_brand: str | None = None,
+) -> DorisVersion:
raw = "" if value is None else str(value).strip()
if not raw:
return parse_doris_version_comment("")
- if "doris" not in raw.lower():
- raw = f"Doris version doris-{raw}"
- return parse_doris_version_comment(raw)
+ parsed = parse_doris_version_comment(raw)
+ if parsed.is_parsed:
+ return parsed
+ # Brandless component builds (for example "4.0.6" or "4.0.6-abc1234")
+ # carry no brand of their own; inherit the cluster brand observed in
+ # @@version_comment so distribution provenance is not lost.
+ brand = default_brand or "doris"
+ return parse_doris_version_comment(f"{brand} version {brand}-{raw}")
def _truthy(value: Any) -> bool:
@@ -2460,7 +2469,10 @@ def _frontend_versions(
for row in rows:
if not _component_is_active(row):
continue
- version = _component_version(_row_value(row, "Version", "FeVersion"))
+ version = _component_version(
+ _row_value(row, "Version", "FeVersion"),
+ default_brand=fallback.brand,
+ )
observed.append(
(
version,
@@ -2485,9 +2497,14 @@ def _frontend_versions(
def _backend_versions(
rows: Sequence[Mapping[str, Any]],
+ *,
+ default_brand: str | None = None,
) -> tuple[DorisVersion, ...]:
return tuple(
- _component_version(_row_value(row, "Version", "BeVersion"))
+ _component_version(
+ _row_value(row, "Version", "BeVersion"),
+ default_brand=default_brand,
+ )
for row in rows
if _component_is_active(row)
)
diff --git a/doris_mcp_server/tools/doris_feature_matrix.py
b/doris_mcp_server/tools/doris_feature_matrix.py
index 585dac2..9e93ab4 100644
--- a/doris_mcp_server/tools/doris_feature_matrix.py
+++ b/doris_mcp_server/tools/doris_feature_matrix.py
@@ -28,6 +28,7 @@ from typing import Annotated, Literal, Self
from pydantic import Field, StringConstraints, model_validator
+from ..utils.version_brands import normalize_version_brand_aliases
from .domain_models import (
CapabilityVariant,
ChildSupportContract,
@@ -399,10 +400,17 @@ class PatchCertificationCase(ContractModel):
class PatchCertificationEvidence(ContractModel):
- """Committed proof required before one three-part Doris patch is
certified."""
+ """Committed proof required before one three-part Doris patch is certified.
+
+ ``brand`` identifies which distribution the evidence certifies. The
+ default ``doris`` brand denotes Apache Doris real-cluster evidence; a
+ derived distribution is only certified by evidence recorded under its
+ own brand token.
+ """
certification_id: Identifier
version: NonEmptyText
+ brand: NonEmptyText = "doris"
master_fe_version_comment: NonEmptyText
follower_fe_version_comments: tuple[NonEmptyText, ...] = ()
backend_version_comments: Annotated[
@@ -443,21 +451,40 @@ class PatchCertificationEvidence(ContractModel):
"certification evidence requires a three-part target version"
)
target_literal = _version_literal(target)
+ if normalize_version_brand_aliases((self.brand,)) != (self.brand,):
+ raise ValueError(
+ "certification evidence brand must be a canonical lowercase "
+ "brand token"
+ )
comments = (
self.master_fe_version_comment,
*self.follower_fe_version_comments,
*self.backend_version_comments,
)
for comment in comments:
- observed = parse_doris_version_comment(comment)
- if (
- not observed.is_parsed
- or observed.core != target_literal
- ):
- raise ValueError(
- "every certified FE and BE must report the Doris core "
- f"version {target_literal}"
- )
+ if self.brand == "doris":
+ observed = parse_doris_version_comment(comment)
+ if (
+ not observed.is_parsed
+ or observed.core != target_literal
+ ):
+ raise ValueError(
+ "every certified FE and BE must report the Doris core "
+ f"version {target_literal}"
+ )
+ else:
+ # Distribution evidence is validated without relying on the
+ # runtime brand registry: the comment must carry the declared
+ # brand token and the certified three-part core version.
+ if not _distribution_comment_matches(
+ comment,
+ brand=self.brand,
+ core=target_literal,
+ ):
+ raise ValueError(
+ f"every certified FE and BE must report the
{self.brand} "
+ f"core version {target_literal}"
+ )
expected_cases = {
("stdio", "hierarchical"),
@@ -525,12 +552,18 @@ class DorisPatchCertificationMatrix(ContractModel):
"certification targets must use three-part versions"
)
_require_unique(targets, "patch certification targets")
- evidence_versions = tuple(record.version for record in self.evidence)
- _require_unique(evidence_versions, "patch certification evidence
versions")
+ evidence_keys = tuple(
+ f"{record.brand}:{record.version}" for record in self.evidence
+ )
+ _require_unique(
+ evidence_keys,
+ "patch certification evidence brand/version pairs",
+ )
_require_unique(
tuple(record.certification_id for record in self.evidence),
"patch certification evidence IDs",
)
+ evidence_versions = tuple(record.version for record in self.evidence)
if not set(evidence_versions).issubset(targets):
raise ValueError(
"patch certification evidence must reference target versions"
@@ -583,11 +616,19 @@ class DorisPatchCertificationMatrix(ContractModel):
literal = observed.core
if literal is None:
raise AssertionError("parsed Doris version must expose a core
version")
+ component_brands = {version.brand for version in components}
+ uniform_brand = (
+ component_brands.pop() if len(component_brands) == 1 else None
+ )
target = literal in self.target_versions
+ # Certification evidence is provenance-aware: Apache Doris evidence
+ # only certifies clusters whose components all carry the doris brand,
+ # and a derived distribution is only certified by evidence recorded
+ # under its own brand token.
matches = tuple(
record
for record in self.evidence
- if record.version == literal
+ if record.version == literal and record.brand == uniform_brand
)
if matches:
return self._report(
@@ -603,6 +644,21 @@ class DorisPatchCertificationMatrix(ContractModel):
),
)
if target:
+ if uniform_brand != "doris":
+ return self._report(
+ observed_fe,
+ observed_be,
+ uniform_version=literal,
+ status=VersionCertificationStatus.TARGET_UNCERTIFIED,
+ reason_code="PATCH_CERTIFICATION_DISTRIBUTION_UNVERIFIED",
+ targeted=True,
+ limitations=(
+ "The observed components do not all carry the Apache "
+ "Doris brand, so Apache Doris patch certification does
"
+ "not transfer; a distribution requires its own "
+ "committed certification evidence.",
+ ),
+ )
return self._report(
observed_fe,
observed_be,
@@ -934,13 +990,59 @@ def _ordered_unique(values: Iterable[object]) ->
tuple[str, ...]:
return tuple(dict.fromkeys(str(value) for value in values))
+def _distribution_comment_matches(
+ comment: str,
+ *,
+ brand: str,
+ core: str,
+) -> bool:
+ """Match one evidence brand and core without the mutable alias registry."""
+ escaped_brand = re.escape(brand)
+ escaped_core = re.escape(core)
+ pattern = re.compile(
+ rf"""
+ (?<![A-Za-z0-9_])
+ {escaped_brand}
+ (?:\s*,?\s*version)?
+ (?:\s+{escaped_brand}-|\s*-\s*|\s+)
+ {escaped_core}
+ (?:-(?:rc\d+|alpha\d*|beta\d*))?
+ (?:-[0-9a-f]{{7,40}})?
+ (?=\s|\(|,|$)
+ """,
+ re.IGNORECASE | re.VERBOSE,
+ )
+ return pattern.search(comment) is not None
+
+
def _certification_status(
version: DorisVersion | None,
matrix: DorisFeatureMatrix,
+ versions: DorisClusterVersionVector | None = None,
) -> VersionCertificationStatus:
if version is None or version.core is None:
return VersionCertificationStatus.UNKNOWN
literal = version.core
+ # Apache Doris certification evidence only applies when every observed
+ # component carries the Apache Doris brand; a registered distribution is
+ # at best target-uncertified until its own evidence is committed.
+ provenance_verified = version.brand_verified
+ if provenance_verified and versions is not None:
+ components = (
+ versions.master_fe,
+ *versions.follower_fes,
+ *versions.backends,
+ )
+ provenance_verified = all(
+ component.is_parsed and component.brand_verified
+ for component in components
+ )
+ if not provenance_verified:
+ return (
+ VersionCertificationStatus.TARGET_UNCERTIFIED
+ if literal in matrix.certification_targets
+ else VersionCertificationStatus.OUTSIDE_TARGET
+ )
if literal in matrix.certified_versions:
return VersionCertificationStatus.CERTIFIED
if literal in matrix.certification_targets:
@@ -961,7 +1063,7 @@ def _evaluation(
matched_variants: tuple[str, ...] = (),
matched_ranges: tuple[str, ...] = (),
) -> FeatureVersionEvaluation:
- certification_status = _certification_status(effective, matrix)
+ certification_status = _certification_status(effective, matrix, versions)
return FeatureVersionEvaluation(
feature_id=feature.feature_id,
requested_variant=variant_name,
@@ -2190,8 +2292,13 @@ DORIS_PATCH_CERTIFICATION_MATRIX =
DorisPatchCertificationMatrix(
evidence=PATCH_CERTIFICATION_EVIDENCE,
)
-CERTIFIED_DORIS_VERSIONS = (
- DORIS_PATCH_CERTIFICATION_MATRIX.certified_versions
+# Feature-level certification claims Apache Doris real-cluster evidence
+# only; distribution-branded evidence never certifies the Apache feature
+# matrix.
+CERTIFIED_DORIS_VERSIONS = tuple(
+ record.version
+ for record in PATCH_CERTIFICATION_EVIDENCE
+ if record.brand == "doris"
)
DORIS_FEATURE_MATRIX = DorisFeatureMatrix(
diff --git a/doris_mcp_server/tools/doris_version.py
b/doris_mcp_server/tools/doris_version.py
index d88d437..fbd192b 100644
--- a/doris_mcp_server/tools/doris_version.py
+++ b/doris_mcp_server/tools/doris_version.py
@@ -20,30 +20,51 @@
from __future__ import annotations
import re
-from collections.abc import Mapping, Sequence
+from collections.abc import Iterable, Mapping, Sequence
from dataclasses import dataclass
from enum import StrEnum
from typing import Any
from ..utils.db import DorisConnection
+from ..utils.version_brands import (
+ DEFAULT_VERSION_BRAND,
+ normalize_version_brand_aliases,
+)
DORIS_VERSION_COMMENT_QUERY = "SELECT @@version_comment;"
_VERSION_COMMENT_COLUMN = "@@version_comment"
_VERSION_COMMENT_MAX_BYTES = 4096
-_VERSION_PATTERN = re.compile(
- r"""
- (?<![A-Za-z0-9_])
- (?:apache\s+)?doris
- (?:\s*,?\s*version)?
- (?:\s+doris-|\s*-\s*|\s+)
- (?P<core>\d+\.\d+\.\d+)
- (?:-(?P<prerelease>rc\d+|alpha\d*|beta\d*))?
- (?:-(?P<commit>[0-9a-f]{7,40}))?
- (?=\s|\(|,|$)
- """,
- re.IGNORECASE | re.VERBOSE,
-)
+_DEFAULT_BRAND = DEFAULT_VERSION_BRAND
+
+# Brand tokens whose version comments follow the Apache Doris three-part
+# core version scheme. Only ``doris`` is recognized by default; derived
+# distributions that report their own brand token can register it through
+# ``configure_version_brands`` so the comment participates in every numeric
+# version check (minimum version, patch ranges, mixed-component
+# evaluation). Comments without a recognized brand still fail closed.
+_known_brands: tuple[str, ...] = (_DEFAULT_BRAND,)
+
+
+def _compile_version_pattern(brands: tuple[str, ...]) -> re.Pattern[str]:
+ brand_alternatives = "|".join((r"apache\s+doris", *brands))
+ prefix_alternatives = "|".join(brands)
+ return re.compile(
+ rf"""
+ (?<![A-Za-z0-9_])
+ (?P<brand>{brand_alternatives})
+ (?:\s*,?\s*version)?
+ (?:\s+(?:{prefix_alternatives})-|\s*-\s*|\s+)
+ (?P<core>\d+\.\d+\.\d+)
+ (?:-(?P<prerelease>rc\d+|alpha\d*|beta\d*))?
+ (?:-(?P<commit>[0-9a-f]{{7,40}}))?
+ (?=\s|\(|,|$)
+ """,
+ re.IGNORECASE | re.VERBOSE,
+ )
+
+
+_VERSION_PATTERN = _compile_version_pattern(_known_brands)
_DEPLOYMENT_PATTERNS = (
("cloud", re.compile(r"\bcloud\s+mode\b", re.IGNORECASE)),
("shared_data", re.compile(r"\bshared[-\s]+data\b", re.IGNORECASE)),
@@ -65,12 +86,18 @@ class DorisVersion:
prerelease: str | None = None
commit: str | None = None
deployment_hint: str | None = None
+ brand: str | None = None
parse_status: DorisVersionParseStatus = DorisVersionParseStatus.UNKNOWN
@property
def is_parsed(self) -> bool:
return self.parse_status is DorisVersionParseStatus.PARSED
+ @property
+ def brand_verified(self) -> bool:
+ """Whether the version comment carries the Apache Doris brand."""
+ return self.brand == "doris"
+
@property
def core(self) -> str | None:
if (
@@ -107,6 +134,27 @@ class DorisVersion:
return (self.major, self.minor, self.patch)
+def known_version_brands() -> tuple[str, ...]:
+ """Return the currently recognized version-comment brand tokens."""
+ return _known_brands
+
+
+def configure_version_brands(aliases: Iterable[str]) -> tuple[str, ...]:
+ """Register extra version-comment brand tokens and rebuild the pattern.
+
+ The ``doris`` brand is always recognized. Aliases replace any
+ previously configured set, so passing an empty iterable restores the
+ default doris-only behavior. Aliases are normalized and validated with
+ the same contract used by configuration validation.
+ """
+ global _known_brands, _VERSION_PATTERN
+
+ extras = normalize_version_brand_aliases(aliases)
+ _known_brands = tuple(dict.fromkeys((_DEFAULT_BRAND, *extras)))
+ _VERSION_PATTERN = _compile_version_pattern(_known_brands)
+ return _known_brands
+
+
def parse_doris_version_comment(comment: str) -> DorisVersion:
deployment_hint = _detect_deployment_hint(comment)
match = _VERSION_PATTERN.search(comment)
@@ -125,10 +173,16 @@ def parse_doris_version_comment(comment: str) ->
DorisVersion:
prerelease=prerelease.lower() if prerelease else None,
commit=commit.lower() if commit else None,
deployment_hint=deployment_hint,
+ brand=_normalize_brand(match.group("brand")),
parse_status=DorisVersionParseStatus.PARSED,
)
+def _normalize_brand(value: str) -> str:
+ normalized = " ".join(value.casefold().split())
+ return "doris" if normalized == "apache doris" else normalized
+
+
def parse_doris_version_rows(
rows: Sequence[Mapping[str, Any]],
) -> DorisVersion:
diff --git a/doris_mcp_server/tools/tools_manager.py
b/doris_mcp_server/tools/tools_manager.py
index 682ba2e..6a5dda1 100644
--- a/doris_mcp_server/tools/tools_manager.py
+++ b/doris_mcp_server/tools/tools_manager.py
@@ -63,6 +63,7 @@ from .domain_manifest import (
DomainManifestService,
)
from .doris_feature_matrix import DORIS_FEATURE_MATRIX
+from .doris_version import configure_version_brands
from .governance_handlers import GovernanceToolHandlersMixin
from .lakehouse_handlers import LakehouseToolHandlersMixin
from .pipeline_handlers import PipelineToolHandlersMixin
@@ -142,6 +143,14 @@ class DorisToolsManager(
if domain_availability_provider is None:
bound_handlers = BoundHandlerAvailabilityProvider(self)
capability_config = getattr(config, "capability", None)
+ # Register any configured @@version_comment brand aliases before
+ # the first probe parses a cluster version.
+ brand_aliases = getattr(
+ capability_config, "version_brand_aliases", ()
+ )
+ configure_version_brands(
+ brand_aliases if isinstance(brand_aliases, list | tuple) else
()
+ )
detector = DorisCapabilityDetector(
connection_manager,
probe_timeout_seconds=getattr(
diff --git a/doris_mcp_server/utils/config.py b/doris_mcp_server/utils/config.py
index f74cd29..7646365 100644
--- a/doris_mcp_server/utils/config.py
+++ b/doris_mcp_server/utils/config.py
@@ -61,6 +61,7 @@ from .secret_policy import (
normalize_token_hash_algorithm,
validate_high_entropy_secret,
)
+from .version_brands import normalize_version_brand_aliases
class AuthConfigError(ValueError):
@@ -891,6 +892,10 @@ class CapabilityConfig:
snapshot_ttl_seconds: int = 300
probe_timeout_seconds: int = 5
stale_grace_seconds: int = 900
+ # Extra @@version_comment brand tokens recognized as Doris-lineage
+ # distributions (for example a vendor distribution brand). Each token
+ # must be a single lowercase alphanumeric word.
+ version_brand_aliases: list[str] = field(default_factory=list)
@dataclass
@@ -1665,6 +1670,12 @@ class DorisConfig:
config.capability.stale_grace_seconds,
)
_mark_source(config, "capability_stale_grace_seconds", "env")
+ if "CAPABILITY_VERSION_BRAND_ALIASES" in os.environ:
+ config.capability.version_brand_aliases = _env_csv(
+ "CAPABILITY_VERSION_BRAND_ALIASES",
+ config.capability.version_brand_aliases,
+ )
+ _mark_source(config, "capability_version_brand_aliases", "env")
if "GOVERNANCE_MAX_SAMPLE_RATIO" in os.environ:
config.governance.max_sample_ratio = float(
os.getenv(
@@ -1971,6 +1982,9 @@ class DorisConfig:
"stale_grace_seconds": (
self.capability.stale_grace_seconds
),
+ "version_brand_aliases": list(
+ self.capability.version_brand_aliases
+ ),
},
"governance": {
"max_sample_ratio": self.governance.max_sample_ratio,
@@ -2277,6 +2291,19 @@ class DorisConfig:
errors.append(
"Capability stale grace must be in the range 0-86400 seconds"
)
+ # JSON configuration can smuggle in non-list values despite the
+ # declared field type, so widen to object before the runtime check.
+ aliases: object = self.capability.version_brand_aliases
+ if not isinstance(aliases, list | tuple):
+ errors.append(
+ "Capability version brand aliases must be a list of strings, "
+ f"got: {type(aliases).__name__}"
+ )
+ else:
+ try:
+ normalize_version_brand_aliases(aliases)
+ except (TypeError, ValueError) as exc:
+ errors.append(str(exc))
if not 0 < self.governance.max_sample_ratio <= 1:
errors.append(
"Governance maximum sample ratio must be in the range (0, 1]"
@@ -2608,6 +2635,9 @@ class DorisConfig:
"stale_grace_seconds": (
self.capability.stale_grace_seconds
),
+ "version_brand_aliases": list(
+ self.capability.version_brand_aliases
+ ),
},
"semantic": {
"enabled": self.semantic.enabled,
diff --git a/doris_mcp_server/utils/version_brands.py
b/doris_mcp_server/utils/version_brands.py
new file mode 100644
index 0000000..8eb6f1c
--- /dev/null
+++ b/doris_mcp_server/utils/version_brands.py
@@ -0,0 +1,62 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""Canonical validation for Doris version-comment brand aliases.
+
+Both configuration validation and the runtime brand registry share this
+normalization contract so an alias accepted by one is accepted by the
+other.
+"""
+
+from __future__ import annotations
+
+import re
+from collections.abc import Iterable
+
+DEFAULT_VERSION_BRAND = "doris"
+BRAND_TOKEN_PATTERN = re.compile(r"^[a-z][a-z0-9]*$")
+RESERVED_BRAND_TOKENS = frozenset({"apache", "version"})
+
+
+def normalize_version_brand_aliases(aliases: Iterable[object]) -> tuple[str,
...]:
+ """Normalize and validate configured version-comment brand aliases.
+
+ Each alias must be a string holding a single lowercase alphanumeric
+ word. Reserved tokens (``apache``, ``version``) are rejected. Empty
+ entries are ignored and duplicates collapse while preserving order.
+
+ Raises:
+ TypeError: if an entry is not a string.
+ ValueError: if an entry is not a valid brand token.
+ """
+ normalized: list[str] = []
+ for alias in aliases:
+ if not isinstance(alias, str):
+ raise TypeError(
+ "Doris version brand aliases must be strings, "
+ f"got: {alias!r}"
+ )
+ token = " ".join(alias.casefold().split())
+ if not token:
+ continue
+ if (
+ BRAND_TOKEN_PATTERN.fullmatch(token) is None
+ or token in RESERVED_BRAND_TOKENS
+ ):
+ raise ValueError(f"Invalid Doris version brand alias: {alias!r}")
+ normalized.append(token)
+ return tuple(dict.fromkeys(normalized))
diff --git a/test/protocol/test_multiworker_config.py
b/test/protocol/test_multiworker_config.py
index bf44588..547f532 100644
--- a/test/protocol/test_multiworker_config.py
+++ b/test/protocol/test_multiworker_config.py
@@ -130,16 +130,22 @@ def
test_capability_cache_controls_are_explicit_and_validated(
monkeypatch.setenv("CAPABILITY_SNAPSHOT_TTL_SECONDS", "120")
monkeypatch.setenv("CAPABILITY_PROBE_TIMEOUT_SECONDS", "7")
monkeypatch.setenv("CAPABILITY_STALE_GRACE_SECONDS", "480")
+ monkeypatch.setenv("CAPABILITY_VERSION_BRAND_ALIASES", "enterprisedb,
forkdb")
configured = DorisConfig.from_env()
assert configured.capability.snapshot_ttl_seconds == 120
assert configured.capability.probe_timeout_seconds == 7
assert configured.capability.stale_grace_seconds == 480
+ assert configured.capability.version_brand_aliases == [
+ "enterprisedb",
+ "forkdb",
+ ]
assert configured.to_dict()["capability"] == {
"snapshot_ttl_seconds": 120,
"probe_timeout_seconds": 7,
"stale_grace_seconds": 480,
+ "version_brand_aliases": ["enterprisedb", "forkdb"],
}
assert configured.validate() == []
@@ -164,10 +170,30 @@ def
test_capability_cache_controls_are_explicit_and_validated(
from_file.capability.snapshot_ttl_seconds = 0
from_file.capability.probe_timeout_seconds = 61
from_file.capability.stale_grace_seconds = -1
+ from_file.capability.version_brand_aliases = ["bad-token!"]
errors = from_file.validate()
assert "Capability snapshot TTL must be in the range 1-86400 seconds" in
errors
assert "Capability probe timeout must be in the range 1-60 seconds" in
errors
assert "Capability stale grace must be in the range 0-86400 seconds" in
errors
+ assert "Invalid Doris version brand alias: 'bad-token!'" in errors
+
+ # Reserved tokens and non-list JSON values fail the same contract that
+ # configure_version_brands() enforces at runtime.
+ from_file.capability.snapshot_ttl_seconds = 30
+ from_file.capability.probe_timeout_seconds = 3
+ from_file.capability.stale_grace_seconds = 60
+ from_file.capability.version_brand_aliases = ["version"]
+ assert "Invalid Doris version brand alias: 'version'" in
from_file.validate()
+ from_file.capability.version_brand_aliases = ["apache"]
+ assert "Invalid Doris version brand alias: 'apache'" in
from_file.validate()
+ from_file.capability.version_brand_aliases = ["enterprisedb", 42]
+ assert any(
+ "must be strings" in error for error in from_file.validate()
+ )
+ from_file.capability.version_brand_aliases = "enterprisedb"
+ assert any(
+ "must be a list of strings" in error for error in from_file.validate()
+ )
def test_governance_runtime_controls_load_serialize_and_validate(
diff --git a/test/tools/test_capability_detector.py
b/test/tools/test_capability_detector.py
index 8066cd6..f6f9acd 100644
--- a/test/tools/test_capability_detector.py
+++ b/test/tools/test_capability_detector.py
@@ -18,6 +18,7 @@
from __future__ import annotations
+from collections.abc import Iterator
from contextlib import asynccontextmanager
from types import SimpleNamespace
from typing import Any
@@ -32,7 +33,12 @@ from doris_mcp_server.tools.capability_detector import (
DorisCapabilityDetector,
_classify_profile_api_response,
)
-from doris_mcp_server.tools.doris_feature_matrix import DORIS_FEATURE_MATRIX
+from doris_mcp_server.tools.doris_feature_matrix import (
+ DORIS_FEATURE_MATRIX,
+ DORIS_PATCH_CERTIFICATION_MATRIX,
+ VersionCertificationStatus,
+)
+from doris_mcp_server.tools.doris_version import configure_version_brands
from doris_mcp_server.utils.db import DorisRouteIdentity
from doris_mcp_server.utils.doris_http_client import DorisHTTPResponse
from doris_mcp_server.utils.security import AuthContext
@@ -56,6 +62,15 @@ _STORAGE_HISTORY_PROBE_SQL = (
)
[email protected]
+def enterprise_brand_alias() -> Iterator[str]:
+ configure_version_brands(("enterprisedb",))
+ try:
+ yield "enterprisedb"
+ finally:
+ configure_version_brands(())
+
+
class _ProbeConnection:
def __init__(self) -> None:
self.statements: list[str] = []
@@ -1069,6 +1084,162 @@ async def
test_detector_uses_fallback_for_unknown_master_and_retains_follower()
assert evaluation.reason_code == "DORIS_VERSION_UNKNOWN"
[email protected]
+async def test_detector_propagates_comment_brand_to_brandless_components(
+ enterprise_brand_alias: str,
+) -> None:
+ connection = _ProbeConnection()
+ connection.row_overrides["SELECT @@version_comment;"] = [
+ {"@@version_comment": f"{enterprise_brand_alias} version 4.0.5"}
+ ]
+ connection.row_overrides["SHOW FRONTENDS"] = [
+ {
+ "Name": "fe-1",
+ "IsMaster": "true",
+ "Version": "4.0.5",
+ },
+ ]
+ connection.row_overrides["SHOW BACKENDS"] = [
+ {
+ "BackendId": "1",
+ "Version": "4.0.5-rc01-59de8c4c524",
+ },
+ ]
+ detector = DorisCapabilityDetector( # type: ignore[arg-type]
+ _ProbeConnectionManager(connection)
+ )
+
+ snapshot = await detector.detect_base(
+ None,
+ capability_generation=1,
+ provider_generation="provider.a",
+ )
+ report = DORIS_PATCH_CERTIFICATION_MATRIX.evaluate(snapshot.version_vector)
+
+ # Brandless component versions inherit the @@version_comment brand, so
+ # Apache Doris patch certification must not transfer to a distribution.
+ assert snapshot.version_vector.master_fe.brand == enterprise_brand_alias
+ assert snapshot.version_vector.master_fe.brand_verified is False
+ assert snapshot.version_vector.backends[0].brand == enterprise_brand_alias
+ assert report.uniform_observed_version == "4.0.5"
+ assert report.status is VersionCertificationStatus.TARGET_UNCERTIFIED
+ assert report.reason_code == "PATCH_CERTIFICATION_DISTRIBUTION_UNVERIFIED"
+ assert report.certified is False
+
+
[email protected]
+async def test_detector_does_not_treat_commit_substrings_as_brand_tokens() ->
None:
+ configure_version_brands(("db",))
+ try:
+ connection = _ProbeConnection()
+ connection.row_overrides["SHOW FRONTENDS"] = [
+ {
+ "Name": "fe-1",
+ "IsMaster": "true",
+ "Version": "4.0.5-adb1234",
+ },
+ ]
+ detector = DorisCapabilityDetector( # type: ignore[arg-type]
+ _ProbeConnectionManager(connection)
+ )
+
+ snapshot = await detector.detect_base(
+ None,
+ capability_generation=1,
+ provider_generation="provider.a",
+ )
+
+ assert snapshot.version_vector.master_fe.is_parsed is True
+ assert snapshot.version_vector.master_fe.brand == "doris"
+ assert snapshot.version_vector.master_fe.core == "4.0.5"
+ finally:
+ configure_version_brands(())
+
+
[email protected]
+async def test_detector_defaults_brandless_components_to_doris_brand() -> None:
+ connection = _ProbeConnection()
+ connection.row_overrides["SHOW FRONTENDS"] = [
+ {
+ "Name": "fe-1",
+ "IsMaster": "true",
+ "Version": "4.0.5",
+ },
+ ]
+ connection.row_overrides["SHOW BACKENDS"] = [
+ {
+ "BackendId": "1",
+ "Version": "4.0.5-rc01-59de8c4c524",
+ },
+ ]
+ detector = DorisCapabilityDetector( # type: ignore[arg-type]
+ _ProbeConnectionManager(connection)
+ )
+
+ snapshot = await detector.detect_base(
+ None,
+ capability_generation=1,
+ provider_generation="provider.a",
+ )
+ report = DORIS_PATCH_CERTIFICATION_MATRIX.evaluate(snapshot.version_vector)
+
+ assert snapshot.version_vector.master_fe.brand == "doris"
+ assert snapshot.version_vector.master_fe.brand_verified is True
+ assert snapshot.version_vector.backends[0].brand == "doris"
+ assert report.status is VersionCertificationStatus.CERTIFIED
+ assert report.certified is True
+
+
[email protected]
+async def test_detector_parses_enterprise_brand_versions(
+ enterprise_brand_alias: str,
+) -> None:
+ connection = _ProbeConnection()
+ connection.row_overrides["SELECT @@version_comment;"] = [
+ {"@@version_comment": f"{enterprise_brand_alias} version 4.0.6"}
+ ]
+ connection.row_overrides["SHOW FRONTENDS"] = [
+ {
+ "Name": "fe-1",
+ "IsMaster": "true",
+ "Version": f"{enterprise_brand_alias}-4.0.6-abc1234",
+ },
+ ]
+ connection.row_overrides["SHOW BACKENDS"] = [
+ {
+ "BackendId": "1",
+ "Version": f"{enterprise_brand_alias}-4.0.6-abc1234",
+ },
+ ]
+ detector = DorisCapabilityDetector( # type: ignore[arg-type]
+ _ProbeConnectionManager(connection)
+ )
+
+ snapshot = await detector.detect_base(
+ None,
+ capability_generation=1,
+ provider_generation="provider.a",
+ )
+ evaluation = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_catalogs",
+ versions=snapshot.version_vector,
+ )
+
+ assert snapshot.version_vector.master_fe.is_parsed is True
+ assert snapshot.version_vector.master_fe.core == "4.0.6"
+ assert snapshot.version_vector.master_fe.brand == enterprise_brand_alias
+ assert snapshot.version_vector.master_fe.brand_verified is False
+ assert snapshot.version_vector.backends[0].is_parsed is True
+ assert snapshot.mixed_versions is False
+ assert (
+ snapshot.probes["version_probe_completed"].status
+ is CapabilityProbeStatus.SUPPORTED
+ )
+ assert evaluation.compatible is True
+ assert evaluation.reason_code == "VERSION_RANGE_MATCHED"
+
+
@pytest.mark.asyncio
async def test_governance_probes_keep_pre_406_audit_lineage_available() ->
None:
connection = _ProbeConnection()
diff --git a/test/tools/test_doris_feature_matrix.py
b/test/tools/test_doris_feature_matrix.py
index 4316b2e..1f0651c 100644
--- a/test/tools/test_doris_feature_matrix.py
+++ b/test/tools/test_doris_feature_matrix.py
@@ -17,6 +17,7 @@
from __future__ import annotations
from collections import Counter
+from collections.abc import Iterator
from typing import Any, cast
import pytest
@@ -43,7 +44,19 @@ from doris_mcp_server.tools.doris_feature_matrix import (
VersionCertificationStatus,
matching_version_ranges,
)
-from doris_mcp_server.tools.doris_version import parse_doris_version_comment
+from doris_mcp_server.tools.doris_version import (
+ configure_version_brands,
+ parse_doris_version_comment,
+)
+
+
[email protected]
+def enterprise_brand_alias() -> Iterator[str]:
+ configure_version_brands(("enterprisedb",))
+ try:
+ yield "enterprisedb"
+ finally:
+ configure_version_brands(())
def _vector(
@@ -104,6 +117,26 @@ def _certification_evidence(
)
+def _distribution_certification_evidence(
+ brand: str,
+ version: str = "4.0.5",
+) -> PatchCertificationEvidence:
+ return PatchCertificationEvidence(
+ certification_id=f"{brand}_{version.replace('.', '_')}_linux_amd64",
+ version=version,
+ brand=brand,
+ master_fe_version_comment=f"{brand} version {version}",
+ backend_version_comments=(f"{brand}-{version}-abc1234",),
+ platform="linux_amd64",
+ deployment_mode="unknown",
+ cases=_certification_cases(),
+ domain_names=tuple(EXPECTED_DOMAIN_CHILDREN),
+ child_contract_count=55,
+ evidence_sha256="b" * 64,
+ verified_on="2026-08-01",
+ )
+
+
def test_matrix_contains_exact_8_domain_55_child_contract() -> None:
assert len(DORIS_FEATURE_MATRIX.features) == 55
assert tuple(EXPECTED_DOMAIN_CHILDREN) == (
@@ -407,6 +440,133 @@ def
test_patch_certification_fails_closed_for_unknown_or_mixed_components() -> N
assert mixed.uniform_observed_version is None
+def test_distribution_brand_does_not_inherit_apache_certification(
+ enterprise_brand_alias: str,
+) -> None:
+ versions = DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 4.0.5",
+ backends=(f"{enterprise_brand_alias}-4.0.5-abc1234",),
+ )
+
+ feature = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=versions,
+ )
+ assert feature.compatible is True
+ assert feature.reason_code == "VERSION_RANGE_MATCHED"
+ assert feature.certification_status is (
+ VersionCertificationStatus.TARGET_UNCERTIFIED
+ )
+ assert feature.certified is False
+
+ report = DORIS_PATCH_CERTIFICATION_MATRIX.evaluate(versions)
+ assert report.uniform_observed_version == "4.0.5"
+ assert report.status is VersionCertificationStatus.TARGET_UNCERTIFIED
+ assert report.reason_code == "PATCH_CERTIFICATION_DISTRIBUTION_UNVERIFIED"
+ assert report.targeted is True
+ assert report.certified is False
+ assert report.evidence_ids == ()
+
+
+def test_mixed_brand_components_are_not_apache_certified(
+ enterprise_brand_alias: str,
+) -> None:
+ versions = DorisClusterVersionVector.from_comments(
+ master_fe="Doris version doris-4.0.5",
+ backends=(f"{enterprise_brand_alias}-4.0.5-abc1234",),
+ )
+
+ report = DORIS_PATCH_CERTIFICATION_MATRIX.evaluate(versions)
+ assert report.status is VersionCertificationStatus.TARGET_UNCERTIFIED
+ assert report.reason_code == "PATCH_CERTIFICATION_DISTRIBUTION_UNVERIFIED"
+ assert report.certified is False
+
+ # Feature-level certification also checks provenance across the whole
+ # version vector, not just the scope-effective component.
+ feature = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=versions,
+ )
+ assert feature.certification_status is (
+ VersionCertificationStatus.TARGET_UNCERTIFIED
+ )
+ assert feature.certified is False
+
+
+def test_distribution_evidence_certifies_only_its_own_brand(
+ enterprise_brand_alias: str,
+) -> None:
+ matrix = DorisPatchCertificationMatrix(
+ minimum_supported_version="2.0.0",
+ target_versions=CERTIFICATION_TARGET_VERSIONS,
+ evidence=(
+ _certification_evidence("4.0.5"),
+ _distribution_certification_evidence(enterprise_brand_alias),
+ ),
+ )
+
+ distribution = matrix.evaluate(
+ DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 4.0.5",
+ backends=(f"{enterprise_brand_alias}-4.0.5-abc1234",),
+ )
+ )
+ assert distribution.status is VersionCertificationStatus.CERTIFIED
+ assert distribution.certified is True
+ assert distribution.evidence_ids == ("enterprisedb_4_0_5_linux_amd64",)
+
+ apache = matrix.evaluate(_vector("4.0.5"))
+ assert apache.status is VersionCertificationStatus.CERTIFIED
+ assert apache.evidence_ids == ("doris_4_1_2_linux_amd64",)
+
+ apache_only = DorisPatchCertificationMatrix(
+ minimum_supported_version="2.0.0",
+ target_versions=CERTIFICATION_TARGET_VERSIONS,
+ evidence=(_certification_evidence("4.0.5"),),
+ )
+ cross_brand = apache_only.evaluate(
+ DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 4.0.5",
+ backends=(f"{enterprise_brand_alias}-4.0.5-abc1234",),
+ )
+ )
+ assert cross_brand.status is VersionCertificationStatus.TARGET_UNCERTIFIED
+ assert cross_brand.certified is False
+
+
+def test_distribution_evidence_validates_comments_without_brand_registry() ->
None:
+ evidence = _distribution_certification_evidence("enterprisedb")
+ assert evidence.brand == "enterprisedb"
+
+ payload = evidence.model_dump(mode="python")
+ payload["backend_version_comments"] = ("enterprisedb-4.1.1-abc1234",)
+ with pytest.raises(
+ ValidationError,
+ match="enterprisedb core version 4.0.5",
+ ):
+ PatchCertificationEvidence.model_validate(payload)
+
+ payload = evidence.model_dump(mode="python")
+ payload["brand"] = "EnterpriseDB"
+ with pytest.raises(ValidationError, match="canonical lowercase brand
token"):
+ PatchCertificationEvidence.model_validate(payload)
+
+ for invalid_comment in (
+ "notenterprisedb version 4.0.5",
+ "enterprisedb version 14.0.50",
+ "notenterprisedb-14.0.50-abc1234",
+ ):
+ payload = evidence.model_dump(mode="python")
+ payload["backend_version_comments"] = (invalid_comment,)
+ with pytest.raises(
+ ValidationError,
+ match="enterprisedb core version 4.0.5",
+ ):
+ PatchCertificationEvidence.model_validate(payload)
+
+
def test_patch_evidence_rejects_incomplete_host_or_component_proof() -> None:
evidence = _certification_evidence()
payload = evidence.model_dump(mode="python")
@@ -819,6 +979,21 @@ def test_an_unparseable_component_version_fails_closed()
-> None:
)
+def test_unparseable_observed_component_prevents_feature_certification() ->
None:
+ result = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe="Doris version doris-4.0.5",
+ backends=("unknown-build",),
+ ),
+ )
+
+ assert result.compatible is True
+ assert result.certification_status is
VersionCertificationStatus.TARGET_UNCERTIFIED
+ assert result.certified is False
+
+
def test_version_below_2_0_0_is_rejected_before_child_evaluation() -> None:
result = DORIS_FEATURE_MATRIX.evaluate(
domain="doris_catalog",
@@ -831,6 +1006,84 @@ def
test_version_below_2_0_0_is_rejected_before_child_evaluation() -> None:
assert result.minimum_supported_version == "2.0.0"
+def test_enterprise_brand_versions_flow_through_every_version_gate(
+ enterprise_brand_alias: str,
+) -> None:
+ below_minimum = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 1.2.8",
+ ),
+ )
+ assert below_minimum.compatible is False
+ assert below_minimum.reason_code == "DORIS_VERSION_BELOW_MINIMUM"
+
+ base = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 2.1.5",
+ ),
+ )
+ assert base.compatible is True
+ assert base.reason_code == "VERSION_RANGE_MATCHED"
+
+ # Patch ranges stay enforced on distribution brands: the native lineage
+ # variant requires >=4.0.6, so a 2.1.5 distribution cluster falls back
+ # to the audit variant instead of exposing the native one.
+ native_lineage = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_governance",
+ child_name="trace_column_lineage",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 2.1.5",
+ ),
+ variant_name="native_lineage_events",
+ )
+ assert native_lineage.compatible is False
+ assert native_lineage.reason_code == "VERSION_RANGE_NOT_MATCHED"
+
+ audit_lineage = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_governance",
+ child_name="trace_column_lineage",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 2.1.5",
+ ),
+ variant_name="audit_sql_inference_primary",
+ )
+ assert audit_lineage.compatible is True
+
+ # Mixed-component evaluation uses the same conservative minimum for
+ # distribution brands.
+ mixed = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_cluster",
+ child_name="get_cache_status",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} version 4.1.3",
+ backends=(f"{enterprise_brand_alias}-4.0.5-abc1234",),
+ ),
+ variant_name="advanced_cache_types",
+ )
+ assert mixed.compatible is False
+ assert mixed.reason_code == "VERSION_RANGE_NOT_MATCHED"
+ assert mixed.effective_version == "4.0.5"
+
+
+def test_unrecognized_distribution_comment_format_still_fails_closed(
+ enterprise_brand_alias: str,
+) -> None:
+ result = DORIS_FEATURE_MATRIX.evaluate(
+ domain="doris_catalog",
+ child_name="list_tables",
+ versions=DorisClusterVersionVector.from_comments(
+ master_fe=f"{enterprise_brand_alias} pro 2.1.5",
+ ),
+ )
+
+ assert result.compatible is False
+ assert result.reason_code == "DORIS_VERSION_UNKNOWN"
+
+
def test_target_patch_is_distinct_from_certified_patch() -> None:
result = DORIS_FEATURE_MATRIX.evaluate(
domain="doris_catalog",
diff --git a/test/tools/test_doris_version.py b/test/tools/test_doris_version.py
index ad1dd1b..41a83d5 100644
--- a/test/tools/test_doris_version.py
+++ b/test/tools/test_doris_version.py
@@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
+from collections.abc import Iterator
from unittest.mock import AsyncMock, MagicMock
import pytest
@@ -22,6 +23,8 @@ import pytest
from doris_mcp_server.tools.doris_version import (
DORIS_VERSION_COMMENT_QUERY,
DorisVersionParseStatus,
+ configure_version_brands,
+ known_version_brands,
parse_doris_version_comment,
parse_doris_version_rows,
probe_doris_version,
@@ -29,6 +32,15 @@ from doris_mcp_server.tools.doris_version import (
from doris_mcp_server.utils.db import DorisConnection, QueryResult
[email protected]
+def enterprise_brand_alias() -> Iterator[str]:
+ configure_version_brands(("enterprisedb",))
+ try:
+ yield "enterprisedb"
+ finally:
+ configure_version_brands(())
+
+
def test_version_probe_uses_version_comment() -> None:
assert DORIS_VERSION_COMMENT_QUERY == "SELECT @@version_comment;"
@@ -81,11 +93,105 @@ def test_parse_supported_version_comments(
assert version.raw == comment
[email protected](
+ "comment",
+ [
+ "enterprisedb version 2.1.5",
+ "enterprisedb-4.0.6-abc1234",
+ ],
+)
+def test_unregistered_brand_comments_fail_closed(comment: str) -> None:
+ version = parse_doris_version_comment(comment)
+
+ assert version.parse_status is DorisVersionParseStatus.UNKNOWN
+ assert version.is_parsed is False
+ assert version.core is None
+
+
[email protected](
+ ("comment", "core"),
+ [
+ ("enterprisedb version 2.1.5", "2.1.5"),
+ ("enterprisedb version enterprisedb-2.1.5-rc04", "2.1.5"),
+ ("enterprisedb-4.0.6-abc1234", "4.0.6"),
+ ],
+)
+def test_registered_brand_alias_comments_parse_three_part_version(
+ comment: str,
+ core: str,
+ enterprise_brand_alias: str,
+) -> None:
+ version = parse_doris_version_comment(comment)
+
+ assert version.parse_status is DorisVersionParseStatus.PARSED
+ assert version.core == core
+ assert version.normalized == core
+ assert version.brand == enterprise_brand_alias
+ assert version.brand_verified is False
+
+
+def test_brand_alias_configuration_replaces_previous_set(
+ enterprise_brand_alias: str,
+) -> None:
+ assert known_version_brands() == ("doris", enterprise_brand_alias)
+
+ configure_version_brands(("forkdb",))
+
+ assert known_version_brands() == ("doris", "forkdb")
+ assert (
+ parse_doris_version_comment("enterprisedb version 2.1.5").is_parsed
+ is False
+ )
+ assert parse_doris_version_comment("forkdb version 2.1.5").brand ==
"forkdb"
+
+
[email protected](
+ "alias",
+ [
+ "version",
+ "apache",
+ "bad-token!",
+ "two words",
+ "1db",
+ ],
+)
+def test_configure_version_brands_rejects_invalid_aliases(alias: str) -> None:
+ with pytest.raises(ValueError, match="Invalid Doris version brand alias"):
+ configure_version_brands((alias,))
+
+
+def test_configure_version_brands_rejects_non_string_aliases() -> None:
+ with pytest.raises(TypeError, match="must be strings"):
+ configure_version_brands(("enterprisedb", 42))
+
+
+def test_configure_version_brands_ignores_empty_aliases() -> None:
+ assert configure_version_brands(("", " ")) == ("doris",)
+ assert known_version_brands() == ("doris",)
+
+
[email protected](
+ "comment",
+ [
+ "Doris version doris-3.0.3-rc03-43f06a5e26 (Cloud Mode)",
+ "Apache Doris version 4.0.7",
+ "doris-4.1.3-abcdef1234",
+ ],
+)
+def test_apache_doris_comments_carry_verified_brand(comment: str) -> None:
+ version = parse_doris_version_comment(comment)
+
+ assert version.brand == "doris"
+ assert version.brand_verified is True
+
+
@pytest.mark.parametrize(
"comment",
[
"",
"MySQL 8.0.36",
+ "8.0.33",
+ "otherdb version 3.2.0",
"version 4.1.3",
"Doris version unknown",
"Doris version 4.1.3-preview1",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]