@@ -597,4 +597,169 @@ class AnsibleRunnerSpec extends Specification{
597597 }
598598
599599
600+ def " escapeYamlKey : should quote keys with special characters" () {
601+ given:
602+ def builder = AnsibleRunner.playbookInline(" test" )
603+ builder.customTmpDirPath(" / tmp" )
604+ def runner = builder.build()
605+
606+ expect:
607+ runner.escapeYamlKey(key) == expectedResult
608+
609+ where:
610+ key || expectedResult
611+ " simple- key" || " simple- key"
612+ " key :with :colons" || " \" key:with:colons\" "
613+ " key[brackets]" || " \" key[brackets]\" "
614+ " key{braces}" || " \" key{braces}\" "
615+ " key#hash" || " \" key#hash\" "
616+ " key&ersand" || " \" key&ersand\" "
617+ " key*asterisk" || " \" key*asterisk\" "
618+ " key!exclamation" || " \" key!exclamation\" "
619+ " key|pipe" || " \" key|pipe\" "
620+ " -starts-with-dash" || " \" -starts-with-dash\" "
621+ " ?starts-with-q" || " \" ?starts-with-q\" "
622+ " 123numeric" || " \" 123numeric\" "
623+ " key with spaces" || " key with spaces" // spaces are handled differently
624+ }
625+
626+ def " escapeYamlValue: should quote values with special characters" () {
627+ given :
628+ def builder = AnsibleRunner . playbookInline(" test" )
629+ builder. customTmpDirPath(" /tmp" )
630+ def runner = builder. build()
631+
632+ expect :
633+ runner. escapeYamlValue(value) == expectedResult
634+
635+ where :
636+ value || expectedResult
637+ " simple-value" || " simple-value"
638+ " value:colon" || " \" value:colon\" "
639+ " value[bracket]" || " \" value[bracket]\" "
640+ " " || " \" \" " // empty/whitespace should be quoted
641+ " value@at" || " \" value@at\" "
642+ " -starts-dash" || " \" -starts-dash\" "
643+ }
644+
645+ def " escapeYamlKey: should escape backslashes and quotes" () {
646+ given :
647+ def builder = AnsibleRunner . playbookInline(" test" )
648+ builder. customTmpDirPath(" /tmp" )
649+ def runner = builder. build()
650+
651+ expect :
652+ runner. escapeYamlKey(' key"with"quotes' ) == ' "key\\ "with\\ "quotes"'
653+ runner. escapeYamlKey(' key\\ backslash' ) == ' "key\\\\ backslash"'
654+ }
655+
656+ def " isValidVaultFormat: should validate vault format correctly" () {
657+ given :
658+ def builder = AnsibleRunner . playbookInline(" test" )
659+ builder. customTmpDirPath(" /tmp" )
660+ def runner = builder. build()
661+
662+ expect :
663+ runner. isValidVaultFormat(vaultValue) == expectedResult
664+
665+ where :
666+ vaultValue || expectedResult
667+ " !vault |\n encryptedcontent" || true
668+ " !vault |\n line1\n line2" || true
669+ " !vault\n content" || true // without pipe
670+ " not a vault" || false
671+ null || false
672+ " " || false
673+ " !vault" || true // minimal valid
674+ " !vault |\n " || false // no content
675+ }
676+
677+ def " buildGroupVarsYaml: should create valid YAML with host passwords and users" () {
678+ given :
679+ def builder = AnsibleRunner . playbookInline(" test" )
680+ builder. customTmpDirPath(" /tmp" )
681+ def runner = builder. build()
682+
683+ def hostPasswords = [
684+ " web-server-1" : " !vault |\n encrypted1" ,
685+ " db-server-1" : " !vault |\n encrypted2"
686+ ]
687+ def hostUsers = [
688+ " web-server-1" : " webadmin" ,
689+ " db-server-1" : " dbadmin"
690+ ]
691+
692+ when :
693+ def yaml = runner. buildGroupVarsYaml(hostPasswords, hostUsers)
694+
695+ then :
696+ yaml. contains(" host_passwords:" )
697+ yaml. contains(" web-server-1: !vault |" )
698+ yaml. contains(" encrypted1" )
699+ yaml. contains(" db-server-1: !vault |" )
700+ yaml. contains(" encrypted2" )
701+ yaml. contains(" host_users:" )
702+ yaml. contains(" web-server-1: webadmin" )
703+ yaml. contains(" db-server-1: dbadmin" )
704+ }
705+
706+ def " buildGroupVarsYaml: should escape special characters in node names" () {
707+ given :
708+ def builder = AnsibleRunner . playbookInline(" test" )
709+ builder. customTmpDirPath(" /tmp" )
710+ def runner = builder. build()
711+
712+ def hostPasswords = [
713+ " web:server:1" : " !vault |\n encrypted"
714+ ]
715+ def hostUsers = [
716+ " web:server:1" : " admin"
717+ ]
718+
719+ when :
720+ def yaml = runner. buildGroupVarsYaml(hostPasswords, hostUsers)
721+
722+ then :
723+ yaml. contains(' "web:server:1": !vault |' )
724+ yaml. contains(' "web:server:1": admin' )
725+ }
726+
727+ def " buildGroupVarsYaml: should throw exception for invalid vault format" () {
728+ given :
729+ def builder = AnsibleRunner . playbookInline(" test" )
730+ builder. customTmpDirPath(" /tmp" )
731+ def runner = builder. build()
732+
733+ def hostPasswords = [
734+ " web-server-1" : " not-a-vault-value"
735+ ]
736+ def hostUsers = [
737+ " web-server-1" : " admin"
738+ ]
739+
740+ when :
741+ runner. buildGroupVarsYaml(hostPasswords, hostUsers)
742+
743+ then :
744+ def e = thrown(RuntimeException )
745+ e. message. contains(" Invalid vault format for host: web-server-1" )
746+ }
747+
748+ def " buildGroupVarsYaml: should handle empty host lists" () {
749+ given :
750+ def builder = AnsibleRunner . playbookInline(" test" )
751+ builder. customTmpDirPath(" /tmp" )
752+ def runner = builder. build()
753+
754+ def hostPasswords = [:]
755+ def hostUsers = [:]
756+
757+ when :
758+ def yaml = runner. buildGroupVarsYaml(hostPasswords, hostUsers)
759+
760+ then :
761+ yaml. contains(" host_passwords:" )
762+ yaml. contains(" host_users:" )
763+ }
764+
600765}
0 commit comments