# test code for the mysql_variables module
# (c) 2014,  Wayne Rosario <wrosario@ansible.com>

# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

# ============================================================
# Verify mysql_variable successfully queries a variable
#
- vars:
    mysql_parameters: &mysql_params
      login_user: '{{ mysql_user }}'
      login_password: '{{ mysql_password }}'
      login_host: '{{ mysql_host }}'
      login_port: '{{ mysql_primary_port }}'

  block:

    - set_fact:
        set_name: 'version'

    - name: read mysql variables (expect changed=false)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
      register: result

    - include_tasks: assert_var_output.yml
      vars:
        changed: false
        output: "{{ result }}"
        var_name: "{{ set_name }}"

    # ============================================================
    # Verify mysql_variable successfully updates a variable (issue:4568)
    #
    - set_fact:
        set_name: 'delay_key_write'
        set_value: 'ON'

    - name: set mysql variable
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '{{ set_value }}'

    - name: update mysql variable to same value (expect changed=false)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '{{ set_value }}'
      register: result

    - include_tasks: assert_var.yml
      vars:
        changed: false
        output: "{{ result }}"
        var_name: "{{ set_name }}"
        var_value: "{{ set_value }}"

    # ============================================================
    # Verify mysql_variable successfully updates a variable using single quotes
    #
    - set_fact:
        set_name: 'wait_timeout'
        set_value: '300'

    - name: set mysql variable to a temp value
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '200'

    - name: update mysql variable value (expect changed=true)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '{{ set_value }}'
      register: result

    - assert:
        that:
        - result.queries == ["SET GLOBAL `{{ set_name }}` = {{ set_value }}"]

    - include_tasks: assert_var.yml
      vars:
        changed: true
        output: "{{ result }}"
        var_name: "{{ set_name }}"
        var_value: '{{ set_value }}'

    # ============================================================
    # Verify mysql_variable successfully updates a variable using double quotes
    #
    - set_fact:
        set_name: "wait_timeout"
        set_value: "400"

    - name: set mysql variable to a temp value
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: "200"

    - name: update mysql variable value (expect changed=true)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '{{ set_value }}'
      register: result

    - include_tasks: assert_var.yml
      vars:
        changed: true
        output: "{{ result }}"
        var_name: "{{ set_name }}"
        var_value: '{{ set_value }}'

    # ============================================================
    # Verify mysql_variable successfully updates a variable using no quotes
    #
    - set_fact:
        set_name: wait_timeout
        set_value: 500

    - name: set mysql variable to a temp value
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: 200

    - name: update mysql variable value (expect changed=true)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: '{{ set_value }}'
      register: result

    - include_tasks: assert_var.yml
      vars:
        changed: true
        output: "{{ result }}"
        var_name: "{{ set_name }}"
        var_value: '{{ set_value }}'

    # ============================================================
    # Verify mysql_variable successfully updates a variable using an expression (e.g. 1024*4)
    #
    - name: set mysql variable value to an expression
      mysql_variables:
        <<: *mysql_params
        variable: max_connect_errors
        value: "1024*4"
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'Incorrect argument type to variable'

    # ============================================================
    # Verify mysql_variable fails when setting an incorrect value (out of range)
    #
    - name: Set mysql variable value to a number out of range
      mysql_variables:
        <<: *mysql_params
        variable: max_connect_errors
        value: '-1'
      register: oor_result
      ignore_errors: true

    - include_tasks: assert_var.yml
      vars:
        changed: true
        output: "{{ oor_result }}"
        var_name: max_connect_errors
        var_value: 1
      when:
        - connector_name == 'mysqlclient'
        - db_engine == 'mysql'  # mysqlclient returns "changed" with MariaDB

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ oor_result }}"
        msg: 'Truncated incorrect'
      when:
        - connector_name == 'pymsql'

    # ============================================================
    # Verify mysql_variable fails when setting an incorrect value (incorrect type)
    #
    - name: set mysql variable value to a non-valid value number
      mysql_variables:
        <<: *mysql_params
        variable: max_connect_errors
        value: TEST
      register: nvv_result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ nvv_result }}"
        msg: 'Incorrect argument type to variable'

    # ============================================================
    # Verify mysql_variable fails when setting an unknown variable
    #
    - name: set a non mysql variable
      mysql_variables:
        <<: *mysql_params
        variable: my_sql_variable
        value: ON
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'Variable not available'

    # ============================================================
    # Verify mysql_variable fails when setting a read-only variable
    #
    - name: set value of a read only mysql variable
      mysql_variables:
        <<: *mysql_params
        variable: character_set_system
        value: utf16
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'read only variable'

    #=============================================================
    # Verify mysql_variable works with the login_user and login_password parameters
    #
    - set_fact:
        set_name: wait_timeout
        set_value: 77

    - name: query mysql_variable using login_user and password_password
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
      register: result

    - include_tasks: assert_var_output.yml
      vars:
        changed: false
        output: "{{ result }}"
        var_name: "{{ set_name }}"

    - name: set mysql variable to temp value using user login and password (expect changed=true)
      mysql_variables:
        <<: *mysql_params
        variable: '{{ set_name }}'
        value: 20
      register: result

    - name: update mysql variable value using user login and password (expect changed=true)
      mysql_variables:
        <<: *mysql_params
        variable: '{{set_name}}'
        value: '{{set_value}}'
      register: result

    - include_tasks: assert_var.yml
      vars:
        changed: true
        output: "{{result}}"
        var_name: "{{set_name}}"
        var_value: '{{set_value}}'

    #============================================================
    # Verify mysql_variable fails with an incorrect login_password parameter
    #
    - set_fact:
        set_name: connect_timeout
        set_value: 10

    - name: query mysql_variable using incorrect login_password
      mysql_variables:
        login_user: '{{ mysql_user }}'
        login_password: 'wrongpassword'
        login_host: '{{ mysql_host }}'
        login_port: '{{ mysql_primary_port }}'
        variable: '{{ set_name }}'
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'unable to connect to database'

    - name: update mysql variable value using incorrect login_password (expect failed=true)
      mysql_variables:
        login_user: '{{ mysql_user }}'
        login_password: 'wrongpassword'
        login_host: '{{ mysql_host }}'
        login_port: '{{ mysql_primary_port }}'
        variable: '{{ set_name }}'
        value: '{{ set_value }}'
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'unable to connect to database'

    #============================================================
    # Verify mysql_variable fails with an incorrect login_host parameter
    #
    - name: query mysql_variable using incorrect login_host
      mysql_variables:
        login_user: '{{ mysql_user }}'
        login_password: '{{ mysql_password }}'
        login_host: '12.0.0.9'
        login_port: '{{ mysql_primary_port }}'
        variable: wait_timeout
        connect_timeout: 5
      register: result
      ignore_errors: true

    - include_tasks: assert_fail_msg.yml
      vars:
        output: "{{ result }}"
        msg: 'unable to connect to database'

    - block:

        #=========================================
        # Check mode 'persist' and 'persist_only':
        #
        - name: update mysql variable value (expect changed=true) in persist mode
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '{{ set_value }}'
            mode: persist
          register: result

        - assert:
            that:
            - result.queries == ["SET PERSIST `{{ set_name }}` = {{ set_value }}"]

        - include_tasks: assert_var.yml
          vars:
            changed: true
            output: "{{ result }}"
            var_name: "{{ set_name }}"
            var_value: '{{ set_value }}'

        - name: try to update mysql variable value (expect changed=false) in persist mode again
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '{{ set_value }}'
            mode: persist
          register: result

        - include_tasks: assert_var.yml
          vars:
            changed: false
            output: "{{ result }}"
            var_name: "{{ set_name }}"
            var_value: '{{ set_value }}'

        - name: set mysql variable to a temp value
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '200'
            mode: persist

        - name: update mysql variable value (expect changed=true) in persist_only mode
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '{{ set_value }}'
            mode: persist_only
          register: result

        - assert:
            that:
            - result is changed
            - result.queries == ["SET PERSIST_ONLY `{{ set_name }}` = {{ set_value }}"]

        - name: try to update mysql variable value (expect changed=false) in persist_only mode again
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '{{ set_value }}'
            mode: persist_only
          register: result

        - assert:
            that:
            - result is not changed

        - set_fact:
            set_name: max_connections
            set_value: 105
            def_val: 151

        - name: update mysql variable value (expect changed=true) in persist_only mode
          mysql_variables:
            <<: *mysql_params
            variable: '{{ set_name }}'
            value: '{{ set_value }}'
            mode: persist_only
          register: result

        - include_tasks: assert_var.yml
          vars:
            changed: true
            output: "{{ result }}"
            var_name: "{{ set_name }}"
            var_value: '{{ def_val }}'

      when:
        - db_engine == 'mysql'
        - db_version is version('8.0', '>=')

    # Bugfix of https://github.com/ansible/ansible/issues/54239
    # - name: set variable containing dot
    #   mysql_variables:
    #     <<: *mysql_params
    #     variable: validate_password.policy
    #     value: LOW
    #     mode: persist_only
    #   register: result
    #
    # - assert:
    #     that:
    #       - result is changed
    #       - result.queries == ["SET PERSIST_ONLY `validate_password`.`policy` = LOW"]