Repository navigation
ITF-73: delimited text file steps (file.write, file.verify, file.delete) - #1
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
New idm-connector-file module: named folders from idm.test.files with declared access, plain-name containment (no separators, .., roots, or symlinks resolving outside), atomic temp-then-rename writes that never overwrite or leave a temporary file, newest-matching-file selection since a start instant with clock skew, and Commons CSV reading/writing with per-folder and per-call format. Failures carry a FileProblem and never name a path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
StepType gains a default execute(params, captureRequested, StepContext) that delegates to the existing method, so no step changes. The engine always calls it with one context per scenario run, shared by every step, wait attempt and cleanup entry; file.verify will use its scenarioStartedAt to ignore files from earlier runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
FileEvidence records a file.* step's folder name (never its path), file, operation, presence, size, header and rows. Its constructor masks secret-named columns, cuts long values and keeps at most 50 rows within 64K characters, always keeping rows that where: selected; it is idempotent, as SecretMask rebuilds evidence through it. The HTML report renders it as a table with selected rows marked; JSON writes it with kind 'file'. SecretMask masks its file name, column names and values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
file.write writes rows (literal or from fixtures) under columns or the first row's keys, refusing unknown keys and non-scalar values. file.verify reads the newest matching file since the scenario started, selects rows with where: and asserts expect as db.verify does; its expect checks and comparison move to RowExpectations, shared with db.verify, whose messages are unchanged. file.delete treats an absent file as success. Configuration problems are final setup failures; I/O and unparsable files are retried. A new StepType.literalProblem hook lets the validator reject a literal file name that leaves its folder, and a pattern where one exact name is needed. Messages in English and Spanish. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
StepRegistry registers file.write, file.verify and file.delete (17 step types) with the DelimitedFileConnector; every caller passes it. ScenarioNeeds collects the folders file.verify reads and file.write and file.delete write. The real-environment guards refuse a run whose scenarios use an undeclared or missing folder, or one whose declared access or operating-system permissions do not allow what they do there; they create nothing in the folder. The authoring reference documents the three steps, as ScenarioReferenceTest requires. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
files-delimited-round-trip.yaml writes an HR export (one value with a comma and quotes) into the folder scratch, waits for it and checks the user's row with where:, checks the second row's empty field, and deletes the file in cleanup. The test profile declares scratch as read-write; BaseIntegrationTest and ContainerScenarios point it at a fresh directory under target/. DelimitedFileScenarioTest runs the scenario alone and checks that cleanup left the folder empty and that evidence names the folder, never its path. Also fixes FileStepsTest relying on Map.of iteration order for the order of checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
…ry call (ITF-73) ScenarioSupportIsolationTest calls StepRegistry.of through reflection, so the compiler did not catch the new eighth argument. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
The lab panel lists folder:<name> systems (idm.test.files.<name>) the scenarios use or the settings configure, with path (required), access and format fields, none secret. Saving refuses a value the runner could not bind (access, line-ending, header, clock-skew), so a typo cannot stop the next run. The connection test probes the folder as declared, removing its probe file, and names the folder, never its path. Delete treats a folder like any other connection. idmtest run tests cover the round-trip scenario against a workspace folder and the folder guard; the editor schema lists the three steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
…(ITF-73) Folder is a lab kind (folder:<name>) in + add, with access as a select and the path required; an existing folder's tab gets the same select. The finding panel shows file evidence as text: the file and folder name, a line per row with the rows where: selected marked, and what was left out, never the folder's path. file.* steps map to their folder. English and Spanish texts; Vitest covers the form, + add, the evidence text and the step mapping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
Authoring guide, configuration reference (idm.test.files: every key and default, matching temp-suffix to the driver, mounted shares only), the authoring reference's targets and validation tables, the workbench guide (folders in the lab panel and their connection test), the real-environment guards in running-tests.md and the scenarios-real README, the capability matrix, README and CLAUDE.md. Spec section 6 now says how it was built: Spring only in the connector's config package, StepContext, literalProblem, RowExpectations, a guard that creates nothing, folders as lab tabs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013yRxskCGWHj1E9z8bmiVzX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Many IdM projects bring identities in through the NetIQ Identity Manager Driver for Delimited Text, for example an HR export as CSV. Until now a scenario could neither drop a file for the driver nor check a file the driver writes. This PR adds three step types so that "file in, then check LDAP, the database or AD" (and the reverse) can be tested end to end.
Design:
docs/specs/2026-11-03-delimited-text-file-steps-design.md· Plan:docs/plans/2026-11-03-itf-73-delimited-text-file-steps.mdWhat
Folders
idm.test.files.<name>, withpath, anaccesssetting (read/write/read-write, defaultread) and a file format.${...}.file.writefile.verifyclock-skewallows for a share's clock.where:and checksrowCount/row, the same waydb.verifydoes.file.deleteHow
idm-connector-file:DelimitedFileConnector(Apache Commons CSV). Spring appears only in itsconfigpackage.FileEvidence: masked and size-limited, and it always keeps the rowswhere:selected.StepContext: gives a step the scenario's start time, through a defaultexecuteoverload.StepType.literalProblem: a validation hook for literal parameter values.RowExpectations:db.verify's expectation checks, now shared.db.verify's messages are unchanged.ScenarioNeedsgains the folders read and written. A real-environment guard refuses an undeclared or missing folder, or one with too little access as declared or as the operating system allows. The guard creates nothing in the folder, because a driver may be watching it.folder:<name>). Saving refuses a value the runner could not bind.SecretMaskcovers file evidence.idm.test.files), workbench guide, run guards, capability matrix, README,CLAUDE.md.Testing
FileStepsTest, including a wait through the engine while another thread writes the file; guard tests; lab and connection-test tests;idmtest runtests; Vitest tests.files-delimited-round-trip.yaml: writes, waits and checks withwhere:, then deletes in cleanup, against a fresh folder undertarget/.DelimitedFileScenarioTestthen checks that the folder is left empty and that no path appears in the evidence.idmtest runof the round trip and a masking scenario. Neither the folder path nor thesecrets.envsecret appears inreport.html,run.logorresults.json. A workbench session confirmed the lab panel and a run../mvnw clean installpassed in every module, but on JDK 21 (-Djava.version=21 -Dmaven.compiler.release=21) and withLC_ALL=C.UTF-8, because the build container had no Java 25 JDK. Please run./mvnw clean installon Java 25 before merging.ContainerScenarios, which needs Docker. It now declares thescratchfolder too.Out of scope
results.jsonkeeps step parameters unmasked by key. This is existing, documented behaviour, so a literal password in afile.writerow shows up there, asuserPasswordinldap.createalready does. I've suggested a separate task for it.Generated by Claude Code