# Tests for skip_broken and allowerasing
# (and a bit of nobest because the test case is too good to pass up)
#
# There are a lot of fairly complex, corner cases we test here especially towards the end.
#
# The test repo is generated by the setup_rpm_repo role.
#
# See test/integration/targets/setup_rpm_repo/library/create_repo.py for how the repo is laid out.
#
# This allows us to test various upgrades, downgrades, and installs with broken dependencies.
# skip_broken should usually be successful in the upgrade/downgrade case, it will just do nothing.
#
# There is a nobest testcase or two thrown in, simply because this organization provides a very
# good test case for that behavior as well. For example, just installing "broken-a" with no version
# will try to install 2.0.0 which is broken. With nobest=true, it will fall back to 1.2.4. Similar
# for upgrading.
- block:
    - name: Install two packages
      dnf:
        name:
          - broken-a-1.2.3
          - broken-b

    # This will fail. We have broken-a-1.2.3, and broken-b with a strong
    # dependency on it. broken-c has a strong dependency on broken-a-1.2.4.
    # Since installing that would break broken-b, we get a conflict.
    - name: Try installing a third package, intentionally broken
      dnf:
        name:
          - broken-c
      ignore_errors: true
      register: dnf_fail

    - assert:
        that:
          - dnf_fail is failed
          - "'Depsolve Error' in dnf_fail.msg"

    # skip_broken should still install nothing because the conflict is
    # still an issue. But it should skip over the broken packages and not
    # fail.
    - name: Try installing it with skip_broken
      dnf:
        name:
          - broken-c
        skip_broken: true
      register: skip_broken_res

    - name: Assert that nothing got installed
      assert:
        that:
          - skip_broken_res.msg == 'Nothing to do'
          - skip_broken_res.rc == 0
          - skip_broken_res.results == []

    - name: Remove all test packages
      dnf:
        name:
          - broken-*
        state: absent

    # broken-d depends on (unversioned) broken-a.
    # broken-a-2.0.0 has a broken dependency that doesn't exist.
    # skip_broken should cause us to skip our explicit broken-a-2.0.0
    # and bring in broken-a-1.2.4 as a dep of broken-d.
    - name: Ensure proper failure with explicit broken version
      dnf:
        name:
          - broken-a-2.0.0
          - broken-d
      ignore_errors: true
      register: dnf_fail

    - name: Assert that nothing got installed
      assert:
        that:
          - dnf_fail is failed
          - "'Depsolve Error' in dnf_fail.msg"

    - name: skip_broken with explicit version
      dnf:
        name:
          - broken-a-2.0.0
          - broken-d
        skip_broken: true
      register: skip_broken_res

    - name: Assert that the right things got installed
      assert:
        that:
          - skip_broken_res.rc == 0
          - skip_broken_res.results|length == 2
          # FIXME: The following assertions have always been broken. Fix the code and/or test as needed.
          # - 'skip_broken_res.results|select("contains", "Installed: broken-a-1.2.4")|length > 0'
          # - 'skip_broken_res.results|select("contains", "Installed: broken-d-1.2.5")|length > 0'

    - name: Remove all test packages
      dnf:
        name:
          - broken-*
        state: absent

    # Walk the logic of _mark_package_install() here
    # We need to use a full-ish NVR/wildcard. _is_newer_version_installed()
    # will be false otherwise, no matter what. This might be a bug.
    # Relatedly, the real "Case 1" in the code seemingly can't be reached:
    # _is_newer_version_installed wants NVR, _is_installed wants name.
    # Both can't be true at the same time given one pkg_spec. Thus, we start
    # at "Case 2"

    # prereq
    - name: Install broken-a-1.2.4
      dnf:
        name:
          - broken-a-1.2.4
        state: present

    # Case 2: newer version is installed, allow_downgrade is true,
    # is_installed is false since we gave full NVR.
    # "upgrade" to broken-a-1.2.3, allow_downgrade=true
    - name: Do an "upgrade" to an older version of broken-a, allow_downgrade=true
      dnf:
        name:
          - broken-a-1.2.3-1*
        state: latest
        allow_downgrade: true
      check_mode: true
      register: res

    - vars:
        pkg_state: '{{ "Downgrade:" if dnf5 else "Installed:" }}'
      assert:
        that:
          - res is changed
          - res.results|select("contains", pkg_state ~ " broken-a-1.2.3")|length > 0

    # Still case 2, but with broken package to test skip_broken
    # skip_broken: false
    - name: Do an "upgrade" to an older known broken version of broken-a, allow_downgrade=true, skip_broken=false
      dnf:
        name:
          - broken-a-1.2.3.4-1*
        state: latest
        allow_downgrade: true
      check_mode: true
      ignore_errors: true
      register: res

    - assert:
        that:
          # 1.2.3.4 has non-existent dep. Fail without skip_broken.
          - res is failed
          - "'Depsolve Error' in res.msg"

    # skip_broken: true
    - name: Do an "upgrade" to an older known broken version of broken-a, allow_downgrade=true, skip_broken=true
      dnf:
        name:
          - broken-a-1.2.3.4-1*
        state: latest
        allow_downgrade: true
        skip_broken: true
      check_mode: true
      register: res

    - assert:
        that:
          - res is not changed
          - res.rc == 0
          - res.msg == "Nothing to do"

    # Case 3: newer version installed, allow_downgrade=true, but
    # upgrade=false (i.e., state: present or installed)
    - name: Install an older version of broken-a than currently installed
      dnf:
        name:
          - broken-a-1.2.3-1*
        state: present
        allow_downgrade: true
      check_mode: true
      register: res

    - vars:
          pkg_state: '{{ "Downgrade:" if dnf5 else "Installed:" }}'
      assert:
        that:
          - res is changed
          - res.results|select("contains", pkg_state ~ " broken-a-1.2.3")|length > 0

    # Case 3 still, with broken package and skip_broken tests like above.
    - name: Install an older known broken version of broken-a, allow_downgrade=true, skip_broken=false
      dnf:
        name:
          - broken-a-1.2.3.4-1*
        state: present
        allow_downgrade: true
      check_mode: true
      ignore_errors: true
      register: res

    - assert:
        that:
          # 1.2.3.4 has non-existent dep. Fail without skip_broken.
          - res is failed
          - "'Depsolve Error' in res.msg"

    # skip_broken: true
    - name: Install an older known broken version of broken-a, allow_downgrade=true, skip_broken=true
      dnf:
        name:
          - broken-a-1.2.3.4-1*
        state: present
        allow_downgrade: true
        skip_broken: true
      check_mode: true
      register: res

    - assert:
        that:
          - res is not changed
          - res.rc == 0
          - res.msg == "Nothing to do"

    # Case 4: "upgrade" to broken-a-1.2.3, allow_downgrade=false
    # is_newer_version_installed is true, allow_downgrade is false
    - name: Do an "upgrade" to an older version of broken-a, allow_downgrade=false
      dnf:
        name:
          - broken-a-1.2.3-1.noarch
        state: latest
        allow_downgrade: false
      check_mode: true
      register: res

    - assert:
        that:
          - res is not changed
          - res.rc == 0
          - res.msg == "Nothing to do"

    # skip_broken doesn't apply to case 5 or 6 (older version installed).
    # base.upgrade doesn't allow a strict= kwarg. However, nobest works here.

    # Case 5: older version of package is installed, we specify name, no version
    # otherwise we'd land in an earlier case. At this point, 1.2.4 is installed.
    # broken-a-2.0.0 is available as an update but has a broken dependency.
    - name: Update broken-a without nobest=true
      dnf:
        name:
          - broken-a
        state: latest
        best: true
      ignore_errors: true
      register: dnf_fail

    - assert:
        that:
          - dnf_fail is failed
          - "'Depsolve Error' in dnf_fail.msg"

    # With nobest: true, we will be "successful" but not actually perform
    # any upgrade. That is, we are content not having the "best"/latest
    # version.
    - name: Update broken-a with nobest=true
      dnf:
        name:
          - broken-a
        state: latest
        nobest: true
      register: nobest

    - assert:
        that:
          - nobest.rc == 0
          - nobest.results == []

    # Case 6: Current or older version already installed (no version specified
    # in our pkg_spec) and we're requesting present, not latest.
    #
    # This isn't really relevant to skip_broken or nobest, but let's test it
    # anyway since we're already walking the logic of the method.
    - name: Install broken-a (even though it is already installed)
      dnf:
        name:
          - broken-a
        state: present
      register: res

    - assert:
        that:
          - res is not changed

    # Case 7 is already tested quite extensively above in the earlier tests.
    # ######################################################################

    - block:
        - name: Test available package is installed while unavailable one is skipped due to skip_broken=true
          dnf:
            name:
              - dinginessentail
              - not-in-repo
            skip_broken: true
          register: res

        - assert:
            that:
              - res is changed
      # There is a change in dnf5 that splits skip_broken into two options:
      # skip_broken and skip_unavailable.
      # TODO: for this test to pass on dnf5 the skip_unavailable option would have to be
      # added to the dnf5 module and used here instead of skip_broken.
      when: not dnf5

  always:
    - name: Remove all test packages installed
      dnf:
        name:
          - broken-*
          - dinginessentail
        state: absent
