- hosts: testhost
  tasks:
  # basic test of FQ module lookup and that we got the right one (user-dir hosted)
  - name: exec FQ module in a user-dir testns collection
    testns.testcoll.testmodule:
    register: testmodule_out

  # verifies that distributed collection subpackages are visible under a multi-location namespace (testns exists in user and sys locations)
  - name: exec FQ module in a sys-dir testns collection
    testns.coll_in_sys.systestmodule:
    register: systestmodule_out

  # verifies that content-adjacent collections were automatically added to the installed content roots
  - name: exec FQ module from content-adjacent collection
    testns.content_adj.contentadjmodule:
    register: contentadjmodule_out

  # content should only be loaded from the first visible instance of a collection
  - name: attempt to look up FQ module in a masked collection
    testns.testcoll.plugin_lookup:
      type: module
      name: testns.testcoll.maskedmodule
    register: maskedmodule_out

  # ensure the ansible ns can have real collections added to it
  - name: call an external module in the ansible namespace
    ansible.bullcoll.bullmodule:
    register: bullmodule_out

  # ensure the ansible ns cannot override ansible.builtin externally
  - name: call an external module in the ansible.builtin collection (should use the built in module)
    ansible.builtin.ping:
    register: builtin_ping_out

  # action in a collection subdir
  - name: test subdir action FQ
    testns.testcoll.action_subdir.subdir_ping_action:
    register: subdir_ping_action_out

  # module in a collection subdir
  - name: test subdir module FQ
    testns.testcoll.module_subdir.subdir_ping_module:
    register: subdir_ping_module_out

  # module with a granular module_utils import (from (this collection).module_utils.leaf import thingtocall)
  - name: exec module with granular module utils import from this collection
    testns.testcoll.uses_leaf_mu_granular_import:
    register: granular_out

  # module with a granular nested module_utils import (from (this collection).module_utils.base import thingtocall,
  # where base imports secondary from the same collection's module_utils)
  - name: exec module with nested module utils from this collection
    testns.testcoll.uses_base_mu_granular_nested_import:
    register: granular_nested_out

  # module with a flat module_utils import (import (this collection).module_utils.leaf)
  - name: exec module with flat module_utils import from this collection
    testns.testcoll.uses_leaf_mu_flat_import:
    register: flat_out

  # module with a full-module module_utils import using 'from' (from (this collection).module_utils import leaf)
  - name: exec module with full-module module_utils import using 'from' from this collection
    testns.testcoll.uses_leaf_mu_module_import_from:
    register: from_out

  - assert:
      that:
      - testmodule_out.source == 'user'
      - systestmodule_out.source == 'sys'
      - contentadjmodule_out.source == 'content_adj'
      - not maskedmodule_out.plugin_path
      - bullmodule_out.source == 'user_ansible_bullcoll'
      - builtin_ping_out.source is not defined
      - builtin_ping_out.ping == 'pong'
      - subdir_ping_action_out is not changed
      - subdir_ping_module_out is not changed
      - granular_out.mu_result == 'thingtocall in leaf'
      - granular_nested_out.mu_result == 'thingtocall in base called thingtocall in secondary'
      - flat_out.mu_result == 'thingtocall in leaf'
      - from_out.mu_result == 'thingtocall in leaf'
      - from_out.mu2_result == 'thingtocall in secondary'

- hosts: testhost
  tasks:
  - name: exercise filters/tests/lookups
    assert:
      that:
      - "'data' | testns.testcoll.testfilter == 'data_via_testfilter_from_userdir'"
      - "'data' | testns.testcoll.testfilter2 == 'data_via_testfilter2_from_userdir'"
      - "'data' | testns.testcoll.filter_subdir.test_subdir_filter == 'data_via_testfilter_from_subdir'"
      - "'from_user' is testns.testcoll.testtest"
      - "'from_user2' is testns.testcoll.testtest2"
      - "'subdir_from_user' is testns.testcoll.test_subdir.subdir_test"
      - lookup('testns.testcoll.mylookup') == 'mylookup_from_user_dir'
      - lookup('testns.testcoll.mylookup2') == 'mylookup2_from_user_dir'
      - lookup('testns.testcoll.lookup_subdir.my_subdir_lookup') == 'subdir_lookup_from_user_dir'

  - debug:
      msg: "{{ 'foo'|testns.testbroken.broken }}"
    register: result
    ignore_errors: true

  - assert:
      that:
        - |
          'This is a broken filter plugin.' in result.msg

  - debug:
      msg: "{{ 'foo'|missing.collection.filter }}"
    register: result
    ignore_errors: true

  - assert:
      that:
        - result is failed

# ensure that the synthetic ansible.builtin collection limits to builtin plugins, that ansible.legacy loads overrides
# from legacy plugin dirs, and that a same-named plugin loaded from a real collection is not masked by the others
- hosts: testhost
  tasks:
  - name: test unqualified ping from library dir
    ping:
    register: unqualified_ping_out

  - name: test legacy-qualified ping from library dir
    ansible.legacy.ping:
    register: legacy_ping_out

  - name: test builtin ping
    ansible.builtin.ping:
    register: builtin_ping_out

  - name: test collection-based ping
    testns.testcoll.ping:
    register: collection_ping_out

  - assert:
      that:
      - unqualified_ping_out.source == 'legacy_library_dir'
      - legacy_ping_out.source == 'legacy_library_dir'
      - builtin_ping_out.ping == 'pong'
      - collection_ping_out.source == 'user'

# verify the default value for the collections list is empty
- hosts: testhost
  tasks:
  - name: sample default collections value
    testns.testcoll.plugin_lookup:
    register: coll_default_out

  - assert:
      that:
      # in original release, collections defaults to empty, which is mostly equivalent to ansible.legacy
      - not coll_default_out.collection_list


# ensure that inheritance/masking works as expected, that the proper default values are injected when missing,
# and that the order is preserved if one of the magic values is explicitly specified
- name: verify collections keyword play/block/task inheritance and magic values
  hosts: testhost
  collections:
  - bogus.fromplay
  tasks:
  - name: sample play collections value
    testns.testcoll.plugin_lookup:
    register: coll_play_out

  - name: collections override block-level
    collections:
    - bogus.fromblock
    block:
    - name: sample block collections value
      testns.testcoll.plugin_lookup:
      register: coll_block_out

    - name: sample task collections value
      collections:
      - bogus.fromtask
      testns.testcoll.plugin_lookup:
      register: coll_task_out

    - name: sample task with explicit core
      collections:
      - ansible.builtin
      - bogus.fromtaskexplicitcore
      testns.testcoll.plugin_lookup:
      register: coll_task_core

    - name: sample task with explicit legacy
      collections:
      - ansible.legacy
      - bogus.fromtaskexplicitlegacy
      testns.testcoll.plugin_lookup:
      register: coll_task_legacy

  - assert:
      that:
      # ensure that parent value inheritance is masked properly by explicit setting
      - coll_play_out.collection_list == ['bogus.fromplay', 'ansible.legacy']
      - coll_block_out.collection_list == ['bogus.fromblock', 'ansible.legacy']
      - coll_task_out.collection_list == ['bogus.fromtask', 'ansible.legacy']
      - coll_task_core.collection_list == ['ansible.builtin', 'bogus.fromtaskexplicitcore']
      - coll_task_legacy.collection_list == ['ansible.legacy', 'bogus.fromtaskexplicitlegacy']

- name: verify unqualified plugin resolution behavior
  hosts: testhost
  collections:
  - testns.testcoll
  - testns.coll_in_sys
  - testns.contentadj
  tasks:
  # basic test of unqualified module lookup and that we got the right one (user-dir hosted, there's another copy of
  # this one in the same-named collection in sys dir that should be masked
  - name: exec unqualified module in a user-dir testns collection
    testmodule:
    register: testmodule_out

  # use another collection to verify that we're looking in all collections listed on the play
  - name: exec unqualified module in a sys-dir testns collection
    systestmodule:
    register: systestmodule_out

  - assert:
      that:
      - testmodule_out.source == 'user'
      - systestmodule_out.source == 'sys'

# test keyword-static execution of a FQ collection-backed role with "tasks/main.yaml"
- name: verify collection-backed role execution (keyword static)
  hosts: testhost
  collections:
  # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config
  - ansible.builtin
  vars:
    test_role_input: keyword static
  roles:
  - role: testns.testcoll.testrole_main_yaml
  tasks:
  - name: ensure role executed
    assert:
      that:
      - test_role_output.msg == test_role_input
      - testrole_source == 'collection'


# test dynamic execution of a FQ collection-backed role
- name: verify collection-backed role execution (dynamic)
  hosts: testhost
  collections:
  # set to ansible.builtin only to ensure that roles function properly without inheriting the play's collections config
  - ansible.builtin
  vars:
    test_role_input: dynamic
  tasks:
  - include_role:
      name: testns.testcoll.testrole
  - name: ensure role executed
    assert:
      that:
      - test_role_output.msg == test_role_input
      - testrole_source == 'collection'

# test task-static execution of a FQ collection-backed role
- name: verify collection-backed role execution (task static)
  hosts: testhost
  collections:
  - ansible.builtin
  vars:
    test_role_input: task static
  tasks:
  - import_role:
      name: testns.testcoll.testrole
  - name: ensure role executed
    assert:
      that:
      - test_role_output.msg == test_role_input
      - testrole_source == 'collection'


# test a legacy playbook-adjacent role, ensure that play collections config is not inherited
- name: verify legacy playbook-adjacent role behavior
  hosts: testhost
  collections:
  - bogus.bogus
  vars:
    test_role_input: legacy playbook-adjacent
  roles:
  - testrole
# FIXME: this should technically work to look up a playbook-adjacent role
#  - ansible.legacy.testrole
  tasks:
  - name: ensure role executed
    assert:
      that:
      - test_role_output.msg == test_role_input
      - testrole_source == 'legacy roles dir'


# test dynamic execution of a FQ collection-backed role
- name: verify collection-backed role execution in subdir (include)
  hosts: testhost
  vars:
    test_role_input: dynamic (subdir)
  tasks:
  - include_role:
      name: testns.testcoll.role_subdir.subdir_testrole
  - name: ensure role executed
    assert:
      that:
      - test_role_output.msg == test_role_input
      - testrole_source == 'collection'


# test collection-relative role deps (keyword static)
- name: verify collection-relative role deps
  hosts: testhost
  vars:
    outer_role_input: keyword static outer
    test_role_input: keyword static inner
  roles:
    - testns.testcoll.calls_intra_collection_dep_role_unqualified
  tasks:
    - assert:
        that:
          - outer_role_output.msg == outer_role_input
          - test_role_output.msg == test_role_input
          - testrole_source == 'collection'

# test collection-relative role deps (task static)
- name: verify collection-relative role deps
  hosts: testhost
  vars:
    outer_role_input: task static outer
    test_role_input: task static inner
  tasks:
    - import_role:
        name: testns.testcoll.calls_intra_collection_dep_role_unqualified
    - assert:
        that:
          - outer_role_output.msg == outer_role_input
          - test_role_output.msg == test_role_input
          - testrole_source == 'collection'

# test collection-relative role deps (task dynamic)
- name: verify collection-relative role deps
  hosts: testhost
  vars:
    outer_role_input: task dynamic outer
    test_role_input: task dynamic inner
  tasks:
    - include_role:
        name: testns.testcoll.calls_intra_collection_dep_role_unqualified
    - assert:
        that:
          - outer_role_output.msg == outer_role_input
          - test_role_output.msg == test_role_input
          - testrole_source == 'collection'


- name: validate static task include behavior
  hosts: testhost
  collections:
  - bogus.bogus
  tasks:
  - import_tasks: includeme.yml


- name: validate dynamic task include behavior
  hosts: testhost
  collections:
  - bogus.bogus
  tasks:
  - include_tasks: includeme.yml


- name: test a collection-hosted connection plugin against a host from a collection-hosted inventory plugin
  hosts: dynamic_host_a
  vars:
    ansible_connection: testns.testcoll.localconn
    ansible_localconn_connectionvar: from_play
  tasks:
  - raw: echo 'hello world'
    register: connection_out

  - assert:
      that:
      - connection_out.stdout == "localconn ran echo 'hello world'"
      # ensure that the connection var we overrode above made it into the running config
      - connection_out.stderr == "connectionvar is from_play"

- hosts: testhost
  tasks:
  - assert:
      that:
      - hostvars['dynamic_host_a'] is defined
      - hostvars['dynamic_host_a'].connection_out.stdout == "localconn ran echo 'hello world'"

- name: Ensure a collection role can call a standalone role
  hosts: testhost
  roles:
    - testns.testcoll.call_standalone

- name: Test FQCN handlers
  hosts: testhost
  vars:
    handler_counter: 0
  roles:
    - testns.testcoll.test_fqcn_handlers
