Skip to content

Commit 18b5c2f

Browse files
committed
added unit tests
1 parent d3ddc07 commit 18b5c2f

1 file changed

Lines changed: 189 additions & 0 deletions

File tree

src/test/groovy/com/rundeck/plugins/ansible/ansible/AnsibleRunnerSpec.groovy

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,4 +408,193 @@ class AnsibleRunnerSpec extends Specification{
408408
!runner.getTempSshVarsFile().exists()
409409
!runner.getTempBecameVarsFile().exists()
410410
}
411+
def "adhoc: uses -i, no -t, and sets callback envs"() {
412+
given:
413+
def runnerBuilder = AnsibleRunner.adHoc("ansible.builtin.ping", null)
414+
.inventory("/tmp/rdk-inv.ini")
415+
.limits("target1")
416+
.customTmpDirPath("/tmp")
417+
418+
def process = Mock(Process) {
419+
waitFor() >> 0
420+
getInputStream() >> new ByteArrayInputStream(new byte[0])
421+
getOutputStream() >> new ByteArrayOutputStream()
422+
getErrorStream() >> new ByteArrayInputStream(new byte[0])
423+
destroy() >> { }
424+
}
425+
def processExecutor = Mock(ProcessExecutor) {
426+
run() >> process
427+
}
428+
429+
List capturedArgs = null
430+
Map<String,String> capturedEnv = null
431+
432+
ProcessExecutor.ProcessExecutorBuilder processBuilder =
433+
Mock(ProcessExecutor.ProcessExecutorBuilder)
434+
435+
// keep fluent chain by always returning the builder
436+
processBuilder.build() >> processExecutor
437+
processBuilder.procArgs(_ as List) >> { List a -> capturedArgs = new ArrayList(a); return processBuilder }
438+
processBuilder.environmentVariables(_ as Map<String,String>) >> { Map<String,String> e -> capturedEnv = new HashMap<>(e); return processBuilder }
439+
processBuilder.baseDirectory(_ as File) >> { File f -> return processBuilder }
440+
processBuilder.stdinVariables(_ as List) >> { List v -> return processBuilder }
441+
processBuilder.promptStdinLogFile(_ as File) >> { File f -> return processBuilder }
442+
processBuilder.debug(_ as boolean) >> { boolean d -> return processBuilder }
443+
444+
runnerBuilder.processExecutorBuilder(processBuilder)
445+
446+
when:
447+
def rc = runnerBuilder.build().run()
448+
449+
then:
450+
rc == 0
451+
452+
// --- inventory & deprecation checks (flattened, robust) ---
453+
def invPath = "/tmp/rdk-inv.ini"
454+
assert capturedArgs != null : "proc args were null"
455+
456+
// Flatten in case the mock handed us a nested list (e.g., [[...]])
457+
def flatArgs = capturedArgs.flatten().collect { it?.toString() }
458+
println "Captured procArgs (flat): ${flatArgs}"
459+
460+
// preferred form: -i <path>
461+
int iPos = flatArgs.indexOf("-i")
462+
boolean hasDashISeparate = (iPos >= 0 && iPos + 1 < flatArgs.size() && flatArgs[iPos + 1] == invPath)
463+
464+
// defensive: -i=<path> or -i<path>
465+
boolean hasDashIEquals = flatArgs.any { it == "-i=${invPath}" || it == "-i${invPath}" }
466+
467+
// must not use deprecated long flag (covers "--inventory-file" and "--inventory-file=/path")
468+
boolean hasDeprecatedLong =
469+
flatArgs.contains("--inventory-file") ||
470+
flatArgs.any { it.startsWith("--inventory-file=") }
471+
472+
assert (hasDashISeparate || hasDashIEquals) :
473+
"Expected -i ${invPath} (or -i=${invPath}) in args, but got: ${flatArgs}"
474+
assert !hasDeprecatedLong : "Found deprecated --inventory-file in args: ${flatArgs}"
475+
476+
// also ensure no '-t'
477+
assert !flatArgs.contains("-t")
478+
479+
// env for ad-hoc tree replacement
480+
assert capturedEnv != null : "env was null"
481+
capturedEnv.get("ANSIBLE_LOAD_CALLBACK_PLUGINS") == "1"
482+
capturedEnv.get("ANSIBLE_CALLBACKS_ENABLED") == "ansible.builtin.tree"
483+
capturedEnv.get("ANSIBLE_CALLBACK_TREE_DIR") != null
484+
}
485+
486+
def "playbook path: uses -i but does NOT set ad-hoc callback envs"() {
487+
given:
488+
def runnerBuilder = AnsibleRunner.playbookPath("/tmp/playbook.yml")
489+
.inventory("/tmp/rdk-inv.ini")
490+
.customTmpDirPath("/tmp")
491+
492+
def process = Mock(Process) {
493+
waitFor() >> 0
494+
getInputStream() >> new ByteArrayInputStream(new byte[0])
495+
getOutputStream() >> new ByteArrayOutputStream()
496+
getErrorStream() >> new ByteArrayInputStream(new byte[0])
497+
destroy() >> { }
498+
}
499+
def processExecutor = Mock(ProcessExecutor) {
500+
run() >> process
501+
}
502+
503+
List capturedArgs = null
504+
Map<String,String> capturedEnv = null
505+
506+
def processBuilder = Mock(ProcessExecutor.ProcessExecutorBuilder)
507+
processBuilder.build() >> processExecutor
508+
processBuilder.procArgs(_ as List) >> { List a -> capturedArgs = new ArrayList(a); return processBuilder }
509+
processBuilder.environmentVariables(_ as Map<String,String>) >> { Map<String,String> e -> capturedEnv = new HashMap<>(e); return processBuilder }
510+
processBuilder.baseDirectory(_ as File) >> { File f -> return processBuilder }
511+
processBuilder.stdinVariables(_ as List) >> { List v -> return processBuilder }
512+
processBuilder.promptStdinLogFile(_ as File) >> { File f -> return processBuilder }
513+
processBuilder.debug(_ as boolean) >> { boolean d -> return processBuilder }
514+
515+
runnerBuilder.processExecutorBuilder(processBuilder)
516+
517+
when:
518+
def rc = runnerBuilder.build().run()
519+
520+
then:
521+
rc == 0
522+
assert capturedArgs != null : "proc args were null"
523+
524+
// Flatten & stringify to avoid type surprises (and nested lists)
525+
def flatArgs = capturedArgs.flatten().collect { it?.toString() }
526+
println "Captured procArgs (flat): ${flatArgs}"
527+
528+
// inventory via -i (or -i=)
529+
def invPath = "/tmp/rdk-inv.ini"
530+
int iPos = flatArgs.indexOf("-i")
531+
boolean hasDashISeparate = (iPos >= 0 && iPos + 1 < flatArgs.size() && flatArgs[iPos + 1] == invPath)
532+
boolean hasDashIEquals = flatArgs.any { it == "-i=${invPath}" || it == "-i${invPath}" }
533+
534+
// must not use deprecated long flag
535+
boolean hasDeprecatedLong =
536+
flatArgs.contains("--inventory-file") ||
537+
flatArgs.any { it.startsWith("--inventory-file=") }
538+
539+
assert (hasDashISeparate || hasDashIEquals) :
540+
"Expected -i ${invPath} (or -i=${invPath}) in args, but got: ${flatArgs}"
541+
assert !hasDeprecatedLong : "Found deprecated --inventory-file in args: ${flatArgs}"
542+
543+
// playbook path should NOT set the ad-hoc callback envs
544+
assert capturedEnv != null : "env was null"
545+
capturedEnv.get("ANSIBLE_LOAD_CALLBACK_PLUGINS") == null
546+
capturedEnv.get("ANSIBLE_CALLBACKS_ENABLED") == null
547+
capturedEnv.get("ANSIBLE_CALLBACK_TREE_DIR") == null
548+
}
549+
550+
551+
def "adhoc: respects user-provided callback envs (putIfAbsent)"() {
552+
given:
553+
def runnerBuilder = AnsibleRunner.adHoc("ansible.builtin.ping", null)
554+
.inventory("/tmp/rdk-inv.ini")
555+
.limits("target1")
556+
.options([
557+
"ANSIBLE_CALLBACKS_ENABLED": "custom.callback",
558+
"ANSIBLE_LOAD_CALLBACK_PLUGINS": "0",
559+
"ANSIBLE_CALLBACK_TREE_DIR": "/tmp/custom-tree"
560+
])
561+
.customTmpDirPath("/tmp")
562+
563+
def process = Mock(Process) {
564+
waitFor() >> 0
565+
getInputStream() >> new ByteArrayInputStream(new byte[0])
566+
getOutputStream() >> new ByteArrayOutputStream()
567+
getErrorStream() >> new ByteArrayInputStream(new byte[0])
568+
destroy() >> { }
569+
}
570+
def processExecutor = Mock(ProcessExecutor) {
571+
run() >> process
572+
}
573+
574+
Map<String,String> capturedEnv = null
575+
576+
def processBuilder = Mock(ProcessExecutor.ProcessExecutorBuilder)
577+
processBuilder.build() >> processExecutor
578+
processBuilder.procArgs(_ as List<String>) >> { List<String> a -> return processBuilder }
579+
processBuilder.environmentVariables(_ as Map<String,String>) >> { Map<String,String> e -> capturedEnv = new HashMap<>(e); return processBuilder }
580+
processBuilder.baseDirectory(_ as File) >> { File f -> return processBuilder }
581+
processBuilder.stdinVariables(_ as List) >> { List v -> return processBuilder }
582+
processBuilder.promptStdinLogFile(_ as File) >> { File f -> return processBuilder }
583+
processBuilder.debug(_ as boolean) >> { boolean d -> return processBuilder }
584+
585+
runnerBuilder.processExecutorBuilder(processBuilder)
586+
587+
when:
588+
def rc = runnerBuilder.build().run()
589+
590+
then:
591+
rc == 0
592+
assert capturedEnv != null : "env was null"
593+
// user-provided values win because putIfAbsent was used
594+
capturedEnv.get("ANSIBLE_CALLBACKS_ENABLED") == "custom.callback"
595+
capturedEnv.get("ANSIBLE_LOAD_CALLBACK_PLUGINS") == "0"
596+
capturedEnv.get("ANSIBLE_CALLBACK_TREE_DIR") == "/tmp/custom-tree"
597+
}
598+
599+
411600
}

0 commit comments

Comments
 (0)