Files
2026-07-13 13:12:33 +08:00

177 lines
5.5 KiB
Python

"""docx skill — load, eligibility, and create→inspect round-trip."""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
from opensquilla.skills.eligibility import EligibilityContext, check_eligibility
from opensquilla.skills.loader import SkillLoader
from opensquilla.skills.meta.executors.skill_exec import run_skill_exec_step
from opensquilla.skills.meta.types import MetaStep
ROOT = Path(__file__).resolve().parents[1]
BUNDLED = ROOT / "src" / "opensquilla" / "skills" / "bundled"
DOCX_DIR = BUNDLED / "docx"
SCRIPTS = DOCX_DIR / "scripts"
def _spec_to_loader() -> object:
return SkillLoader(bundled_dir=BUNDLED).get_by_name("docx")
def test_skill_loads() -> None:
spec = _spec_to_loader()
assert spec is not None
assert spec.name == "docx"
assert spec.metadata is not None
assert spec.provenance.origin == "clawhub-mit0"
assert spec.provenance.license == "MIT-0"
assert spec.entrypoint is not None
def test_eligibility_with_python_present(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"opensquilla.skills.eligibility.shutil.which",
lambda name: "/usr/bin/python3" if name in {"python", "python3"} else None,
)
spec = _spec_to_loader()
assert spec is not None
assert check_eligibility(spec, EligibilityContext.auto())
def test_eligibility_without_python(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
"opensquilla.skills.eligibility.shutil.which",
lambda name: None,
)
spec = _spec_to_loader()
assert spec is not None
assert not check_eligibility(spec, EligibilityContext.auto())
def test_create_then_inspect_round_trip(tmp_path: Path) -> None:
sys.path.insert(0, str(SCRIPTS))
try:
import create_docx # type: ignore[import-not-found]
import inspect_docx # type: ignore[import-not-found]
finally:
sys.path.pop(0)
spec = {
"metadata": {"title": "Round-trip", "author": "Tester"},
"body": [
{"kind": "heading", "level": 1, "text": "Hello"},
{"kind": "paragraph", "text": "World."},
{"kind": "table", "rows": [["A", "B"], ["1", "2"]]},
],
}
out_path = tmp_path / "out.docx"
doc = create_docx.build(spec)
doc.save(str(out_path))
assert out_path.exists()
inspected = inspect_docx.inspect(out_path)
assert inspected["sections"] >= 1
texts = [p["text"] for p in inspected["paragraphs"]]
assert "Hello" in texts
assert "World." in texts
assert inspected["tables"] and inspected["tables"][0][0] == ["A", "B"]
assert inspected["has_tracked_changes"] is False
def test_edit_replace_text(tmp_path: Path) -> None:
sys.path.insert(0, str(SCRIPTS))
try:
import create_docx # type: ignore[import-not-found]
import edit_docx # type: ignore[import-not-found]
import inspect_docx # type: ignore[import-not-found]
finally:
sys.path.pop(0)
src = tmp_path / "src.docx"
create_docx.build(
{"body": [{"kind": "paragraph", "text": "Hello {{NAME}}, welcome."}]}
).save(str(src))
from docx import Document
doc = Document(str(src))
ops = [{"op": "replace_text", "find": "{{NAME}}", "with": "Wei"}]
edit_docx.apply_ops(doc, ops)
out = tmp_path / "out.docx"
doc.save(str(out))
inspected = inspect_docx.inspect(out)
text = " ".join(p["text"] for p in inspected["paragraphs"])
assert "{{NAME}}" not in text
assert "Wei" in text
def test_inspect_cli_outputs_json(tmp_path: Path) -> None:
sys.path.insert(0, str(SCRIPTS))
try:
import create_docx # type: ignore[import-not-found]
import inspect_docx # type: ignore[import-not-found]
finally:
sys.path.pop(0)
src = tmp_path / "src.docx"
create_docx.build({"body": [{"kind": "paragraph", "text": "x"}]}).save(str(src))
payload = inspect_docx.inspect(src)
encoded = json.dumps(payload, ensure_ascii=False)
assert "paragraphs" in encoded
assert "tables" in encoded
@pytest.mark.asyncio
async def test_skill_exec_exports_markdown_docx(tmp_path: Path) -> None:
sys.path.insert(0, str(SCRIPTS))
try:
import inspect_docx # type: ignore[import-not-found]
finally:
sys.path.pop(0)
loader = SkillLoader(
bundled_dir=BUNDLED,
snapshot_path=tmp_path / "skills_snapshot.json",
)
loader.invalidate_cache()
out_path = tmp_path / "competitive-intel.docx"
step = MetaStep(
id="export_docx",
kind="skill_exec",
skill="docx",
with_args={
"markdown": (
"# Competitive intel brief\n\n"
"Acme has a new hiring signal.\n\n"
"| Account | Signal |\n"
"|---|---|\n"
"| Acme | hiring |\n"
),
"output_path": str(out_path),
},
)
result = await run_skill_exec_step(
step,
"docx",
{"collected": {"intel_clarify": {"export_docx": "YES"}}},
{"intel_brief_audit": "Competitive intel brief"},
skill_loader=loader,
workspace_dir=str(tmp_path),
)
assert result == str(out_path)
inspected = inspect_docx.inspect(out_path)
texts = [p["text"] for p in inspected["paragraphs"]]
assert "Competitive intel brief" in texts
assert "Acme has a new hiring signal." in texts
assert inspected["tables"][0][0] == ["Account", "Signal"]
assert inspected["tables"][0][1] == ["Acme", "hiring"]