A headless, agent-drivable test harness for NetIQ / OpenText Identity Manager
(DirXML) channel policies. It runs the IDM engine's own policy interpreter
(DirXMLScriptProcessor, XSLT, schema mapping) against author-supplied inputs —
no eDirectory, no running engine, no Designer — and lets you step through a
channel stage by stage, examining the document, trace, and directory
interactions at each step.
Higher fidelity than Designer's manual Policy Simulator (it's the real engine), and scriptable so an agent can author inputs, run, read the trace, edit the policy, and re-run in a loop.
New here? Read docs/intro.md — a plain-English overview of what an agent can do with this, how you provide an export and traces, and example asks. Then docs/quickstart.md walks you from setup to stepping your own driver.
Developing a driver shim? See docs/shim-dev-workflow.md for testing your shim from its own IntelliJ project, with the simulator and test cases in separate repos.
- Runs real policies headlessly — the engine's
DirXMLScriptProcessorwith mocked query/command seams and a null Driver. - Per-stage stepping — each channel stage is driven individually, capturing the XDS document entering and leaving it, the rule trace it produced, and the queries/commands it issued.
- In-memory fake directory — answers the queries a policy makes
(
token-query,do-find-matching-object, source/dest attribute reads) from loaded<instance>state, and absorbs write-back commands. Seed it from hand-authored<instance>XDS, a mined trace, or an LDIF dump of your vault. - Three driver-config sources — assemble the actual subscriber/publisher chain (IDM policy-set order: event → matching → create → placement → command → schema mapping → output transform) from a Designer export, a Designer project, or an LDIF/LDAP export of the live Identity Vault (one subtree dump carries the policies, GCVs, filter, and shim params for the whole driver set).
- Optional production-fidelity checks — hand the chain's final command to the real driver shim to confirm it consumes the policy output, and/or answer queries from live eDirectory over LDAP. Both opt-in; off by default.
- Real input events from a driver's cache — with a live connection,
bin/sim dxcachereads a stopped driver's event cache (its queued subscriber transactions) straight into a case, as an alternative to mining a trace. Uses DxCMD's LDAP extended ops; needs the optionallib/ldap.jar. - Real input events from the Event Logger DB —
bin/sim dbeventsqueries a DirXML Event Logger PostgreSQL history (by DN, driver, type, time) and writes each logged transaction as a pickable sample. Uses the open-source PostgreSQL JDBC driver, which Maven fetches automatically and releases bundle — no manual jar to stage. - Golden tests — compare final output (and directory end-state) against recorded goldens; non-zero exit on mismatch.
-
JDK 21 — the 4.10.1 engine jars are Java 21 bytecode.
-
Maven.
-
Nine proprietary NetIQ / OpenText jars in
lib/(gitignored — supply them yourself). These are the standard IDM driver-dependency set, found on an IDM engine server or Remote Loader install (and bundled with Designer):jar provides dirxml.jarthe IDM engine + policy interpreter (the core) dirxml_misc.jarengine support classes nxsl.jarXPath / XSLT / DirXML Script engine xp.jarthe Novell XML parser / DOM xds.jar(asXDS.jar)XDS document support jclient.jareDirectory client types (referenced, not connected) dhutil.jarlow-level NDS utilities CommonDriverShim.jardriver shim base types js.jarrepackaged Rhino — ECMAScript es:functionsMatch the version you target (this project uses 4.10.1). On a server these live in the engine/Remote-Loader classpath (e.g. an
.../libdirectory); copying a driver's full dependency set is the easy way to get them all. Runbin/sim doctorto confirm the set is complete.Optional 10th jar —
ldap.jar(Novell/OpenText JLDAP SDK,com.novell.ldap; ships with IDM and Designer) is needed only for the DxCMD features (bin/sim dxcache, reading a driver's event cache). Everything else runs without it.
Grab dirxml-simulator-<ver>.zip from the
releases, unzip
it, drop your nine NetIQ jars into its lib/, and run — no clone, no Maven:
unzip dirxml-simulator-*.zip && cd dirxml-simulator-*
cp /path/to/idm/*.jar lib/ # the 9 jars listed under Requirements
bin/sim doctor # -> DOCTOR: OK (Windows: bin\sim.cmd doctor)The archive bundles the compiled jar, the launchers (bin/sim for macOS/Linux,
bin/sim.cmd for Windows), the skill, sample cases, and docs. The proprietary
jars are never bundled — you supply them.
export JAVA_HOME=.../zulu-21
mvn test # run the suite
./tools/build-dist.sh # build the release archive (target/dist/…zip)This project ships a Claude Code skill (.claude/skills/dirxml-policy-testing/)
that teaches an agent to drive the harness — the author → run → step → test loop,
the case format, and the bin/sim commands. That's what turns "test this policy"
into something an agent does for you (see docs/intro.md).
Working in this repo — nothing to install. Open the DirXMLSimulator project in
Claude Code and the project skill is active automatically. Ask the agent to test or
debug a policy and it uses the skill (and bin/sim) on your behalf.
Use it from anywhere — install globally. To make the skill available in any Claude Code session (e.g. while you're in a Designer project), copy or symlink it into your user skills directory:
# from the repo root — symlink so it tracks updates to the skill
ln -s "$(pwd)/.claude/skills/dirxml-policy-testing" ~/.claude/skills/dirxml-policy-testing
# …or copy it
cp -R .claude/skills/dirxml-policy-testing ~/.claude/skills/The skill drives the bin/sim CLI, so the built harness must be reachable:
keep this repo checked out and built (mvn compile, then bin/sim doctor → OK),
and either run the agent from inside it or tell the agent where the project lives.
Using it. The agent selects the skill automatically from its description when
you ask about IDM / DirXML policy testing, tracing, or debugging; you can also
invoke it by name (dirxml-policy-testing) in clients that list skills. Hand it a
driver export and a trace and ask in plain English — see
docs/intro.md for example asks.
The skill is just markdown plus a CLI, so any coding agent can drive the harness.
The substance — how to run it, the case format, trace reading — is in
AGENTS.md (the cross-agent standard many tools read automatically)
and the skill's SKILL.md + reference/ files. Wire it into your tool of choice:
- OpenAI Codex / Jules / Factory / Cursor (agent mode) — read
AGENTS.mdautomatically; no setup needed once the repo is open. - Cursor (rules) — add a rule that points at the harness:
mkdir -p .cursor/rules printf -- '---\ndescription: Test/debug IDM (DirXML) policies with bin/sim\nalwaysApply: false\n---\nSee AGENTS.md and .claude/skills/dirxml-policy-testing/SKILL.md. Drive the harness via bin/sim.\n' > .cursor/rules/dirxml-policy-testing.mdc
- GitHub Copilot — point its repo instructions at the guide:
mkdir -p .github echo 'For IDM / DirXML policy testing, use the harness via bin/sim — see AGENTS.md and .claude/skills/dirxml-policy-testing/SKILL.md.' >> .github/copilot-instructions.md
- Any other agent — tell it to read
AGENTS.md(and the skill'sSKILL.md/reference/) and drive thebin/simCLI.
In every case the agent still needs the built harness reachable (JDK 21,
lib/ jars, bin/sim doctor → OK), exactly as above.
bin/sim run <caseDir> [--trace] # run chain, print final output (+ trace)
bin/sim step <caseDir> # per-stage input/output/queries/trace
bin/sim test <caseDir> # diff vs expected-*.xds; exit !=0 on mismatch
bin/sim test-all <dir> [--junit f] [--json f] # run every case under <dir>; CI summary + exit code
bin/sim compare <caseDir> --against <cfg> # same input through two policy sets; per-stage divergence
bin/sim coverage <dir> # rules fired vs defined across a corpus; lists never-fired rules
bin/sim record <caseDir> # write expected-output.xds / expected-directory.xdsrun, step, test, and compare accept --json for structured output an
agent or script can parse. A case can also carry an expected.assertions file
(XPath checks like exists //modify-attr[@attr-name='Email'] or vetoed) that
test/test-all evaluate — robust where a full golden is brittle. See
docs/regression-testing.md.
test-all discovers every case (a directory with an input.xds) under <dir>,
runs each as test does, prints a PASS/FAIL/ERROR/SKIP summary, and exits non-zero
if anything failed — point CI at it, optionally writing JUnit (--junit) or JSON
(--json) reports. bin/sim harvest <configDir> <outDir> mints that corpus
automatically by replaying real Event Logger DB events through your current
policies and snapshotting the output as goldens. See
docs/regression-testing.md for the full
regression + CI/CD workflow (with a GitHub Actions example).
cases/<name>/
case.properties # driverDN, dnFormat, fromNDS, traceLevel; OR a config source (below)
chain.txt # ordered stages: "stageName = policy.xml" per line
input.xds # the operation to run
directory.xds # optional: initial fake-directory state (<instance> set)
gcv.xml # optional: GCV definitions (or gcv.<name>=<value> in case.properties)
expected-output.xds # golden (written by `record`)
expected-directory.xds # optional golden: directory end-state
expected.assertions # optional XPath assertions on the final output (alt/complement to a golden)
mapping-tables/ # optional <TableName>.xml mapping tables for Map tokens (also auto-extracted from an export/LDIF)
To drive the chain from a real driver instead of chain.txt, set one config
source in case.properties (with channel=publisher|subscriber):
export=../../MyDriver.xml # a Designer driver export
# project=/path/to/designer_workspace + driver=MyDriver # a Designer project
# ldifConfig=/path/to/IDM_subtree.ldif + driver=MyDriver # a live-vault LDIF export
An LDIF/LDAP export of the live vault is often easiest — one subtree dump
carries the whole driver set's policies, GCVs, filter, and shim params, and can
also seed the fake directory (ldif=that-file.ldif). The LDIF must include the
DirXML data attributes (a plain ldapsearch * omits them); the
skill reference
gives the exact ldapsearch command. Optional, opt-in: shim=true drives the
real connector with the chain's output; ldap=ldaps://… answers queries from live
eDir.
Developing the shim itself in its own IntelliJ project? Keep the two repos
separate and the test cases in the shim repo, pointing shimJar= at its live
build output — see docs/shim-dev-workflow.md.
src/main/java/com/pointblue/dirxml/sim/
EngineContext— builds the headlessRuleStaticContext+ captured trace.CaptureEngineTrace— captures the rule-by-rule policy trace to a buffer.PolicyLoader/PolicyStage— load a<policy>/<style-sheet>/<attr-name-map>and wrap it as a channel stage.FakeDirectory— in-memory directory implementing the query/command seams.ChannelSimulator— drives an ordered stage list, capturingStageSnapshots; optional live-LDAP query source and real-shim command sink.DriverExport/DesignerProject/LdifDriverSource— assemble channel chains from a driver export, a Designer project, or a live-vault LDIF export.LdifReader— seed the fake directory from an LDIF dump.LdapValueNormalizer/LdapQueryProcessor/JndiLdapSearch— map LDAP↔native XDS by schema syntax; answer queries from live eDir.ShimAdapter/InitDocBuilder/ShimConfig— drive a real driver shim.MappingTableSource/MappingTableStore/NdsStreamProtocol— resolveMaptoken tables offline (casemapping-tables/dir or export/LDIF resources) via avnd.nds.stream:URL handler.Case/XmlCompare/Cli— the case model, golden compare, and CLI.
