Setting up Open CMIS TCK

This commit is contained in:
2026-05-08 00:02:20 +02:00
parent 1ef1955fa0
commit 062af60af9
12 changed files with 433 additions and 38 deletions

View File

@@ -5,6 +5,7 @@ from __future__ import annotations
import argparse
import json
import os
import subprocess
from datetime import datetime, timezone
from pathlib import Path
@@ -45,6 +46,8 @@ def main() -> int:
parser.add_argument("--artifact-dir", type=Path, required=True)
parser.add_argument("--run-dir", type=Path)
parser.add_argument("--extension-path", type=Path, default=Path(__file__).resolve().parents[1])
parser.add_argument("--credentials-ref", default="")
parser.add_argument("--target-profile-dir", type=Path)
parser.add_argument("--user")
parser.add_argument("--password")
parser.add_argument("--maven-executable", default="mvn")
@@ -60,7 +63,8 @@ def main() -> int:
def run_console_adapter(args: argparse.Namespace) -> dict[str, Any]:
artifact_dir = args.artifact_dir.resolve()
artifact_dir.mkdir(parents=True, exist_ok=True)
session_path = artifact_dir / "session.properties"
private_session_path = artifact_dir / "session-private.properties"
redacted_session_path = artifact_dir / "session.properties.redacted"
groups_path = artifact_dir / "groups.txt"
invocation_path = artifact_dir / "console-runner-invocation.json"
console_stdout = artifact_dir / "console-runner-stdout.txt"
@@ -78,25 +82,41 @@ def run_console_adapter(args: argparse.Namespace) -> dict[str, Any]:
[],
)
_write_session_parameters(session_path, args)
credentials = _load_credentials(args)
if credentials["status"] == "blocked":
return _result(
"blocked",
credentials["observations"],
args,
group_classes,
args.run_dir,
artifact_dir,
[],
extra_facts={"blocked_reason": "credentials_unavailable"},
)
_write_session_parameters(private_session_path, redacted_session_path, args, credentials)
_write_groups(groups_path, group_classes)
command = _maven_command(args, session_path, groups_path)
command = _maven_command(args, private_session_path, groups_path)
invocation = {
"created_at": _now(),
"command": command,
"check_group": args.check_group,
"group_classes": group_classes,
"session_parameters": str(session_path),
"session_parameters": str(redacted_session_path),
"private_session_parameters": str(private_session_path),
"groups_file": str(groups_path),
"auth_mode": credentials["auth_mode"],
}
invocation_path.write_text(json.dumps(invocation, indent=2, sort_keys=True) + "\n", encoding="utf-8")
artifact_refs = [
str(session_path.relative_to(artifact_dir)),
str(redacted_session_path.relative_to(artifact_dir)),
str(groups_path.relative_to(artifact_dir)),
str(invocation_path.relative_to(artifact_dir)),
]
if not group_classes:
private_session_path.unlink(missing_ok=True)
return _result(
"skipped",
[f"No OpenCMIS TCK group classes are mapped for {args.check_group}; treated as a known-gap review group."],
@@ -107,6 +127,7 @@ def run_console_adapter(args: argparse.Namespace) -> dict[str, Any]:
artifact_refs,
)
if args.dry_run:
private_session_path.unlink(missing_ok=True)
return _result(
"skipped",
["Dry run completed; OpenCMIS TCK ConsoleRunner was not invoked."],
@@ -117,13 +138,16 @@ def run_console_adapter(args: argparse.Namespace) -> dict[str, Any]:
artifact_refs,
)
completed = subprocess.run(
command,
capture_output=True,
text=True,
timeout=args.timeout_seconds,
check=False,
)
try:
completed = subprocess.run(
command,
capture_output=True,
text=True,
timeout=args.timeout_seconds,
check=False,
)
finally:
private_session_path.unlink(missing_ok=True)
console_stdout.write_text(completed.stdout, encoding="utf-8")
console_stderr.write_text(completed.stderr, encoding="utf-8")
artifact_refs.extend(
@@ -151,7 +175,12 @@ def run_console_adapter(args: argparse.Namespace) -> dict[str, Any]:
)
def _write_session_parameters(path: Path, args: argparse.Namespace) -> None:
def _write_session_parameters(
private_path: Path,
redacted_path: Path,
args: argparse.Namespace,
credentials: dict[str, Any],
) -> None:
values = {
"org.apache.chemistry.opencmis.binding.spi.type": "browser",
"org.apache.chemistry.opencmis.binding.browser.url": args.browser_url,
@@ -160,14 +189,99 @@ def _write_session_parameters(path: Path, args: argparse.Namespace) -> None:
"org.apache.chemistry.opencmis.binding.compression": "true",
"org.apache.chemistry.opencmis.binding.cookies": "true",
}
if args.user is not None:
values["org.apache.chemistry.opencmis.user"] = args.user
if args.password is not None:
values["org.apache.chemistry.opencmis.password"] = args.password
path.write_text(
user = credentials.get("user")
password = credentials.get("password")
if isinstance(user, str) and user:
values["org.apache.chemistry.opencmis.user"] = user
if isinstance(password, str) and password:
values["org.apache.chemistry.opencmis.password"] = password
private_path.write_text(
"\n".join(f"{key}={value}" for key, value in values.items()) + "\n",
encoding="utf-8",
)
redacted_values = dict(values)
if "org.apache.chemistry.opencmis.password" in redacted_values:
redacted_values["org.apache.chemistry.opencmis.password"] = "<redacted>"
redacted_path.write_text(
"\n".join(f"{key}={value}" for key, value in redacted_values.items()) + "\n",
encoding="utf-8",
)
def _load_credentials(args: argparse.Namespace) -> dict[str, Any]:
if args.user is not None or args.password is not None:
return {
"status": "available",
"auth_mode": "argv",
"user": args.user,
"password": args.password,
}
credentials_ref = (args.credentials_ref or "").strip()
if not credentials_ref:
return {"status": "available", "auth_mode": "anonymous"}
if credentials_ref.startswith("env:"):
return _load_env_credentials(credentials_ref)
if credentials_ref.startswith("file:"):
return _load_file_credentials(credentials_ref, args.target_profile_dir)
return {
"status": "blocked",
"auth_mode": "unknown",
"observations": [
"Unsupported credentials_ref. Use env:USER_VAR,PASSWORD_VAR or file:/path/to/credentials.json."
],
}
def _load_env_credentials(credentials_ref: str) -> dict[str, Any]:
names = credentials_ref.removeprefix("env:").split(",", 1)
if len(names) != 2 or not names[0] or not names[1]:
return {
"status": "blocked",
"auth_mode": "env",
"observations": [
"Environment credentials_ref must be env:USER_VAR,PASSWORD_VAR."
],
}
user = os.environ.get(names[0])
password = os.environ.get(names[1])
missing = [name for name, value in [(names[0], user), (names[1], password)] if not value]
if missing:
return {
"status": "blocked",
"auth_mode": "env",
"observations": [
"Missing credential environment variable(s): " + ", ".join(missing) + "."
],
}
return {"status": "available", "auth_mode": "env", "user": user, "password": password}
def _load_file_credentials(
credentials_ref: str,
target_profile_dir: Path | None,
) -> dict[str, Any]:
raw_path = credentials_ref.removeprefix("file:")
path = Path(raw_path).expanduser()
if not path.is_absolute() and target_profile_dir is not None:
path = target_profile_dir / path
if not path.exists():
return {
"status": "blocked",
"auth_mode": "file",
"observations": [f"Credential file does not exist: {path}."],
}
payload = json.loads(path.read_text(encoding="utf-8"))
user = payload.get("user")
password = payload.get("password")
if not isinstance(user, str) or not isinstance(password, str):
return {
"status": "blocked",
"auth_mode": "file",
"observations": [
"Credential file must contain string fields 'user' and 'password'."
],
}
return {"status": "available", "auth_mode": "file", "user": user, "password": password}
def _write_groups(path: Path, group_classes: list[str]) -> None:
@@ -217,6 +331,7 @@ def _result(
artifact_refs: list[str],
cases: list[dict[str, str]] | None = None,
returncode: int | None = None,
extra_facts: dict[str, Any] | None = None,
) -> dict[str, Any]:
cases = cases or []
counts: dict[str, int] = {}
@@ -224,18 +339,21 @@ def _result(
counts[case["status"]] = counts.get(case["status"], 0) + 1
if not counts:
counts[status] = 1
facts = {
"adapter": "opencmis-console-runner",
"artifact_dir": str(artifact_dir),
"check_group": args.check_group,
"group_classes": group_classes,
"returncode": returncode,
"result_counts": counts,
}
if extra_facts:
facts.update(extra_facts)
return {
"result": status,
"observations": observations,
"tests": cases,
"facts": {
"adapter": "opencmis-console-runner",
"artifact_dir": str(artifact_dir),
"check_group": args.check_group,
"group_classes": group_classes,
"returncode": returncode,
"result_counts": counts,
},
"facts": facts,
"artifact_refs": [
_artifact_ref(artifact_dir / ref, run_dir, artifact_dir)
for ref in artifact_refs