Blob Blame History Raw
#!/usr/bin/python3

# checkpoint:
# 1. export blueprint package list (copy)
# 2. blueprint description edit/update
# 3. hostname setting
# 4. user setting

import crypt
import re

import composerlib
import testlib


@testlib.nondestructive
class TestCustom(composerlib.ComposerCase):

    def testExport(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/composer", superuser=True)
        b.wait_present("#main")

        # go to blueprint openssh-server
        b.click("#openssh-server-name")

        # export package list
        actions_drop_down_sel = ".cmpsr-header__actions #dropdownKebab"
        with b.wait_timeout(300):
            b.click(actions_drop_down_sel)
        b.wait_attr(actions_drop_down_sel, "aria-expanded", "true")
        b.click("a:contains('Export')")
        b.wait_present("#cmpsr-modal-export")
        with b.wait_timeout(300):
            b.wait_in_text("#textInput2-modal-markup", "openssh-server")
        packages_list = b.text("#textInput2-modal-markup")
        b.click("#cmpsr-modal-export button[data-btn=copy-export]")
        b.click("#cmpsr-modal-export button[data-btn=close-export]")
        b.wait_not_present("#cmpsr-modal-export")

        list_from_backend = m.execute("""
            composer-cli blueprints depsolve openssh-server | tail -n +2
            """).split()
        # remove tailing ".x86_64" or ".noarch"(6 chars) or ".aarch64"(7 chars)
        # and "*:" in libpcap-14:1.9.1-2.fc31.x86_64
        regex = re.compile("-[0-9]*:")

        def remove(x):
            if x.endswith("aarch64"):
                return regex.sub("-", x[:-8])
            else:
                return regex.sub("-", x[:-7])
        format_list = map(remove, list_from_backend)

        self.assertEqual(packages_list, "\n".join(format_list))

        # collect code coverage result
        self.check_coverage()

    def testDescription(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/composer", superuser=True)
        b.wait_present("#main")

        # go to blueprint openssh-server
        b.click("#openssh-server-name")

        # update description (cancel first)
        updated_description = "edit ssh server image description"
        with b.wait_timeout(300):
            b.click("a:contains('ssh server image')")
        b.wait_present("#cmpsr-modal-edit-description")
        b.set_input_text("#textInput-modal-markup", updated_description)
        b.click("#cmpsr-modal-edit-description button:contains('Cancel')")
        b.wait_not_present("#cmpsr-modal-edit-description")
        b.wait_present("a:contains('ssh server image')")

        # update description
        actions_drop_down_sel = ".cmpsr-header__actions #dropdownKebab"
        b.click(actions_drop_down_sel)
        b.wait_attr(actions_drop_down_sel, "aria-expanded", "true")
        b.click("a:contains('Edit description')")
        b.wait_present("#cmpsr-modal-edit-description")
        b.set_input_text("#textInput-modal-markup", updated_description)
        b.click("#cmpsr-modal-edit-description button:contains('Save')")
        b.wait_not_present("#cmpsr-modal-edit-description")
        b.wait_present("a:contains('{}')".format(updated_description))
        # backend got updated as well
        desc = m.execute("""
            composer-cli blueprints show openssh-server | grep description | \
            awk -F '"' '{print $2}'
            """).rstrip()
        self.assertEqual(desc, updated_description)

        # collect code coverage result
        self.check_coverage()

    def testHostname(self):
        b = self.browser
        m = self.machine

        self.login_and_go("/composer", superuser=True)
        b.wait_present("#main")

        b.click("#openssh-server-name")
        with b.wait_timeout(300):
            b.click("#blueprint-tabs-tab-customizations")
        # set hostname to "openssh-server"
        b.click("button[aria-describedby=Hostname-help2]")
        b.set_input_text("input[aria-label=Hostname]", "hostname-openssh-server")
        b.click(".form-control-pf-save")
        b.wait_present("span:contains(hostname-openssh-server)")
        hostname = m.execute("""
            composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}'
            """).rstrip()
        b.wait_present("span:contains({})".format(hostname))

        # update hostname
        b.click(".form-control-pf-value")
        b.set_input_text("input[aria-label=Hostname]", "update-openssh-server")
        b.key_press("\r")
        b.wait_present("span:contains(update-openssh-server)")
        hostname = m.execute("""
            composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}'
            """).rstrip()
        b.wait_present("span:contains({})".format(hostname))

        # clicking x button will not update
        b.click(".form-control-pf-value")
        b.set_input_text("input[aria-label=Hostname]", "no-update-openssh-server")
        b.click(".form-control-pf-cancel")
        b.wait_present("span:contains(update-openssh-server)")
        hostname = m.execute("""
            composer-cli blueprints show openssh-server | grep hostname | awk -F '"' '{print $2}'
            """).rstrip()
        b.wait_present("span:contains({})".format(hostname))

        # invalid character, save button is disabled
        b.click(".form-control-pf-value")
        b.set_input_text("input[aria-label=Hostname]", "?")
        b.wait_attr(".form-control-pf-save", "disabled", "")

        # collect code coverage result
        self.check_coverage()

    def testUserAccount(self):
        b = self.browser
        m = self.machine

        # HACK: cockpit-composer may loose keystrokes, see issue #944
        def set_input_value(sel, val):
            w = ""
            for k in val:
                w += k
                while b.val(sel) != w:
                    b.focus(sel)
                    b.key_press(k)

        self.login_and_go("/composer", superuser=True)
        b.wait_present("#main")

        b.click("#openssh-server-name")
        with b.wait_timeout(300):
            b.click("#blueprint-tabs-tab-customizations")

        # add a new user
        b.click("button:contains('Create User Account')")
        b.wait_present("#cmpsr-modal-user-account")
        set_input_value("#textInput2-modal-user", "x y")
        b.wait_val("#textInput1-modal-user", "xy")
        b.click("input[type=checkbox]")
        set_input_value("#textInput1-modal-password", "fooBAR!@#123")
        set_input_value("#textInput2-modal-password", "fooBAR!@#123")
        ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUOtNJdBEXyKxBB898rdT54ULjMGuO6v4" \
                  "jLXmRsdRhR5Id/lKNc9hsdioPWUePgYlqML2iSV72vKQoVhkyYkpcsjr3zvBny9+5xej3+T" \
                  "BLoEMAm2hmllKPmxYJDU8jQJ7wJuRrOVOnk0iSNF+FcY/yaQ0owSF02Nphx47j2KWc0IjGG" \
                  "lt4fl0fmHJuZBA2afN/4IYIIsEWZziDewVtaEjWV3InMRLllfdqGMllhFR+ed2hQz9PN2Qc" \
                  "apmEvUR4UCy/mJXrke5htyFyHi8ECfyMMyYeHwbWLFQIve4CWix9qtksvKjcetnxT+WWrut" \
                  "dr3c9cfIj/c0v/Zg/c4zETxtp cockpit-test"
        b.set_input_text("#textInput5-modal-user", ssh_key)
        b.click("#cmpsr-modal-user-account button:contains('Create')")
        b.wait_not_present("#cmpsr-modal-user-account")
        b.wait_text("tr td[data-label='Full name']", "x y")
        b.wait_text("tr td[data-label='User name']", "xy")
        b.wait_present("tr td[data-label='Server administrator'] .fa-check")
        b.wait_present("tr td[data-label=Password] .fa-check")
        b.wait_in_text("tr td[data-label='SSH key']", "cockpit-test")

        # check backend to have correct password configured
        passwd_str = m.execute("""
            composer-cli blueprints show openssh-server | grep password | awk -F '"' '{print $2}'
            """).rstrip()
        salt = passwd_str[:passwd_str.rfind("$")]
        passwd_crypt = crypt.crypt("fooBAR!@#123", salt)
        self.assertEqual(passwd_str, passwd_crypt)
        # check backend to have correct ssh key configured
        ssh_key_backend = m.execute("""
            composer-cli blueprints show openssh-server | grep key | awk -F '"' '{print $2}'
            """).rstrip()
        self.assertEqual(ssh_key_backend, ssh_key)

        # update password
        new_password = "c456rty$%^RTY"
        b.click("button[aria-label='Edit User Account xy']")
        b.wait_present("#cmpsr-modal-user-account")
        b.click("button:contains('Set New Password')")
        set_input_value("#textInput1-modal-password", new_password)
        set_input_value("#textInput2-modal-password", new_password)
        b.click("#cmpsr-modal-user-account button:contains('Update')")
        b.wait_not_present("#cmpsr-modal-user-account")
        b.wait_present("tr td[data-label=Password] .fa-check")
        passwd_str = m.execute("""
            composer-cli blueprints show openssh-server | grep password | awk '{print $3}'
            """).strip('"\n')
        salt = passwd_str[:passwd_str.rfind("$")]
        passwd_crypt = crypt.crypt(new_password, salt)
        self.assertEqual(passwd_str, passwd_crypt)

        # remove password
        b.click("button[aria-label='Edit User Account xy']")
        b.wait_present("#cmpsr-modal-user-account")
        b.click("button:contains('Remove Password')")
        b.click("#cmpsr-modal-user-account button:contains('Update')")
        b.wait_not_present("#cmpsr-modal-user-account")
        b.wait_present("tr td[data-label=Password]")
        b.wait_not_present("tr td[data-label=Password] .fa-check")

        # duplicated user
        b.click("button:contains('Create User Account')")
        b.wait_present("#cmpsr-modal-user-account")
        set_input_value("#textInput2-modal-user", "x y")
        b.wait_val("#textInput1-modal-user", "xy")
        b.wait_in_text("#textInput1-modal-user-help1", "This user name already exists.")
        b.click("#cmpsr-modal-user-account button:contains('Cancel')")
        b.wait_not_present("#cmpsr-modal-user-account")

        # password checking error
        b.click("button:contains('Create User Account')")
        b.wait_present("#cmpsr-modal-user-account")
        set_input_value("#textInput2-modal-user", "admin user")
        b.wait_val("#textInput1-modal-user", "auser")
        b.click("input[type=checkbox]")
        set_input_value("#textInput1-modal-password", "aaa")
        set_input_value("#textInput2-modal-password", "bbb")
        b.wait_in_text("#textInput2-modal-password-help",
                       "The values entered for password do not match.")
        b.click("#cmpsr-modal-user-account .close")
        b.wait_not_present("#cmpsr-modal-user-account")

        # delete user
        delete_drop_down_sel = "button[aria-label='User Account Actions xy']"
        b.click(delete_drop_down_sel)
        b.wait_attr(delete_drop_down_sel, "aria-expanded", "true")
        b.click("a:contains('Delete User Account')")
        b.wait_not_present("table")

        # collect code coverage result
        self.check_coverage()


if __name__ == '__main__':
    testlib.test_main()