test(bzlmod): support toolchain tests on bzlmod (#1507)

This makes the necessary changes to the toolchains acceptance tests
and also moves the tests to the `//tests` directory as this is the
current convention.

Fixes #1469.
Fixes #1496.
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index a8ef70c..491c685 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -100,10 +100,7 @@
     # TODO: Change to "rolling" once
     # https://github.com/bazelbuild/bazel/commit/f3aafea59ae021c6a12086cb2cd34c5fa782faf1
     # is available in rolling.
-    # NOTE @aignas 2023-10-17: as of https://github.com/bazelbuild/bazel/issues/19838
-    # this is the last known-to-work bazel version, use `last_green` or `rolling` once the
-    # issue is fixed.
-    bazel: "2b8219042c132483e0af39ef20d67dfd6442af01"
+    bazel: "last_green"
     environment:
       RULES_PYTHON_ENABLE_PYSTAR: "1"
     test_flags:
@@ -118,10 +115,7 @@
     # TODO: Change to "rolling" once
     # https://github.com/bazelbuild/bazel/commit/f3aafea59ae021c6a12086cb2cd34c5fa782faf1
     # is available in rolling.
-    # NOTE @aignas 2023-10-17: as of https://github.com/bazelbuild/bazel/issues/19838
-    # this is the last known-to-work bazel version, use `last_green` or `rolling` once the
-    # issue is fixed.
-    bazel: "2b8219042c132483e0af39ef20d67dfd6442af01"
+    bazel: "last_green"
     environment:
       RULES_PYTHON_ENABLE_PYSTAR: "1"
     test_flags:
diff --git a/BUILD.bazel b/BUILD.bazel
index 4d4d3ec..8dd2242 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -40,8 +40,8 @@
     ],
     visibility = [
         "//examples:__pkg__",
-        "//python/tests/toolchains:__pkg__",
         "//tests:__pkg__",
+        "//tests/toolchains:__pkg__",
     ],
 )
 
diff --git a/python/BUILD.bazel b/python/BUILD.bazel
index a14889a..f58a6dc 100644
--- a/python/BUILD.bazel
+++ b/python/BUILD.bazel
@@ -37,6 +37,7 @@
         "//python/config_settings:distribution",
         "//python/constraints:distribution",
         "//python/entry_points:distribution",
+        "//python/extensions:distribution",
         "//python/private:distribution",
         "//python/runfiles:distribution",
     ],
diff --git a/python/extensions/BUILD.bazel b/python/extensions/BUILD.bazel
index 7f6873d..4be3e37 100644
--- a/python/extensions/BUILD.bazel
+++ b/python/extensions/BUILD.bazel
@@ -19,5 +19,5 @@
 filegroup(
     name = "distribution",
     srcs = glob(["**"]),
-    visibility = ["//extensions:__pkg__"],
+    visibility = ["//python:__pkg__"],
 )
diff --git a/python/tests/toolchains/BUILD.bazel b/tests/toolchains/BUILD.bazel
similarity index 100%
rename from python/tests/toolchains/BUILD.bazel
rename to tests/toolchains/BUILD.bazel
diff --git a/python/tests/toolchains/defs.bzl b/tests/toolchains/defs.bzl
similarity index 76%
rename from python/tests/toolchains/defs.bzl
rename to tests/toolchains/defs.bzl
index 653cde6..8776eba 100644
--- a/python/tests/toolchains/defs.bzl
+++ b/tests/toolchains/defs.bzl
@@ -16,6 +16,7 @@
 """
 
 load("//python:versions.bzl", "PLATFORMS", "TOOL_VERSIONS")
+load("//python/private:bzlmod_enabled.bzl", "BZLMOD_ENABLED")  # buildifier: disable=bzl-visibility
 
 _WINDOWS_RUNNER_TEMPLATE = """\
 @ECHO OFF
@@ -24,12 +25,28 @@
 """
 
 def _acceptance_test_impl(ctx):
-    workspace = ctx.actions.declare_file("/".join([ctx.attr.python_version, "WORKSPACE"]))
-    ctx.actions.expand_template(
-        template = ctx.file._workspace_tmpl,
-        output = workspace,
-        substitutions = {"%python_version%": ctx.attr.python_version},
-    )
+    files = []
+
+    if BZLMOD_ENABLED:
+        module_bazel = ctx.actions.declare_file("/".join([ctx.attr.python_version, "MODULE.bazel"]))
+        ctx.actions.expand_template(
+            template = ctx.file._module_bazel_tmpl,
+            output = module_bazel,
+            substitutions = {"%python_version%": ctx.attr.python_version},
+        )
+        files.append(module_bazel)
+
+        workspace = ctx.actions.declare_file("/".join([ctx.attr.python_version, "WORKSPACE"]))
+        ctx.actions.write(workspace, "")
+        files.append(workspace)
+    else:
+        workspace = ctx.actions.declare_file("/".join([ctx.attr.python_version, "WORKSPACE"]))
+        ctx.actions.expand_template(
+            template = ctx.file._workspace_tmpl,
+            output = workspace,
+            substitutions = {"%python_version%": ctx.attr.python_version},
+        )
+        files.append(workspace)
 
     build_bazel = ctx.actions.declare_file("/".join([ctx.attr.python_version, "BUILD.bazel"]))
     ctx.actions.expand_template(
@@ -37,23 +54,27 @@
         output = build_bazel,
         substitutions = {"%python_version%": ctx.attr.python_version},
     )
+    files.append(build_bazel)
 
     python_version_test = ctx.actions.declare_file("/".join([ctx.attr.python_version, "python_version_test.py"]))
     ctx.actions.symlink(
         target_file = ctx.file._python_version_test,
         output = python_version_test,
     )
+    files.append(python_version_test)
 
     run_acceptance_test_py = ctx.actions.declare_file("/".join([ctx.attr.python_version, "run_acceptance_test.py"]))
     ctx.actions.expand_template(
         template = ctx.file._run_acceptance_test_tmpl,
         output = run_acceptance_test_py,
         substitutions = {
+            "%is_bzlmod%": str(BZLMOD_ENABLED),
             "%is_windows%": str(ctx.attr.is_windows),
             "%python_version%": ctx.attr.python_version,
             "%test_location%": "/".join([ctx.attr.test_location, ctx.attr.python_version]),
         },
     )
+    files.append(run_acceptance_test_py)
 
     toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"]
     py3_runtime = toolchain.py3_runtime
@@ -81,14 +102,9 @@
             ),
             is_executable = True,
         )
+    files.append(executable)
+    files.extend(ctx.files._distribution)
 
-    files = [
-        build_bazel,
-        executable,
-        python_version_test,
-        run_acceptance_test_py,
-        workspace,
-    ] + ctx.files._distribution
     return [DefaultInfo(
         executable = executable,
         files = depset(
@@ -120,26 +136,31 @@
         "_build_bazel_tmpl": attr.label(
             doc = "The BUILD.bazel template.",
             allow_single_file = True,
-            default = Label("//python/tests/toolchains/workspace_template:BUILD.bazel.tmpl"),
+            default = Label("//tests/toolchains/workspace_template:BUILD.bazel.tmpl"),
         ),
         "_distribution": attr.label(
             doc = "The rules_python source distribution.",
             default = Label("//:distribution"),
         ),
+        "_module_bazel_tmpl": attr.label(
+            doc = "The MODULE.bazel template.",
+            allow_single_file = True,
+            default = Label("//tests/toolchains/workspace_template:MODULE.bazel.tmpl"),
+        ),
         "_python_version_test": attr.label(
             doc = "The python_version_test.py used to test the Python version.",
             allow_single_file = True,
-            default = Label("//python/tests/toolchains/workspace_template:python_version_test.py"),
+            default = Label("//tests/toolchains/workspace_template:python_version_test.py"),
         ),
         "_run_acceptance_test_tmpl": attr.label(
             doc = "The run_acceptance_test.py template.",
             allow_single_file = True,
-            default = Label("//python/tests/toolchains:run_acceptance_test.py.tmpl"),
+            default = Label("//tests/toolchains:run_acceptance_test.py.tmpl"),
         ),
         "_workspace_tmpl": attr.label(
             doc = "The WORKSPACE template.",
             allow_single_file = True,
-            default = Label("//python/tests/toolchains/workspace_template:WORKSPACE.tmpl"),
+            default = Label("//tests/toolchains/workspace_template:WORKSPACE.tmpl"),
         ),
     },
     test = True,
diff --git a/python/tests/toolchains/run_acceptance_test.py.tmpl b/tests/toolchains/run_acceptance_test.py.tmpl
similarity index 70%
rename from python/tests/toolchains/run_acceptance_test.py.tmpl
rename to tests/toolchains/run_acceptance_test.py.tmpl
index 5748047..c52e078 100644
--- a/python/tests/toolchains/run_acceptance_test.py.tmpl
+++ b/tests/toolchains/run_acceptance_test.py.tmpl
@@ -15,6 +15,7 @@
 import os
 import subprocess
 import unittest
+import pathlib
 
 class TestPythonVersion(unittest.TestCase):
     @classmethod
@@ -48,23 +49,37 @@
         # * USE_BAZEL_VERSION=/tmp/<something>
         os.environ.pop("USE_BAZEL_VERSION", None)
 
-        with open(".bazelrc", "w") as bazelrc:
-            bazelrc.write(
-                os.linesep.join(
-                    [
-                        'build --override_repository rules_python="{}"'.format(
-                            rules_python_path.replace("\\", "/")
-                        ),
-                        "build --test_output=errors",
-                    ]
-                )
+        bazelrc_lines = [
+            "build --test_output=errors",
+        ]
+
+        if %is_bzlmod%:
+            bazelrc_lines.extend(
+                [
+                    'build --override_module rules_python="{}"'.format(
+                        rules_python_path.replace("\\", "/")
+                    ),
+                    "common --enable_bzlmod",
+                ]
             )
+        else:
+            bazelrc_lines.extend(
+                [
+                    'build --override_repository rules_python="{}"'.format(
+                        rules_python_path.replace("\\", "/")
+                    ),
+                    "common --noexperimental_enable_bzlmod",
+                ]
+            )
+
+        bazelrc = pathlib.Path(".bazelrc")
+        bazelrc.write_text(os.linesep.join(bazelrc_lines))
 
     def test_match_toolchain(self):
         output = subprocess.check_output(
-          f"bazel run --announce_rc @python//:python3 -- --version",
-          shell = True, # Shell needed to look up via PATH
-          text=True,
+            f"bazel run --announce_rc @python//:python3 -- --version",
+            shell = True, # Shell needed to look up via PATH
+            text=True,
         ).strip()
         self.assertEqual(output, "Python %python_version%")
 
diff --git a/python/tests/toolchains/versions_test.bzl b/tests/toolchains/versions_test.bzl
similarity index 100%
rename from python/tests/toolchains/versions_test.bzl
rename to tests/toolchains/versions_test.bzl
diff --git a/python/tests/toolchains/workspace_template/BUILD.bazel b/tests/toolchains/workspace_template/BUILD.bazel
similarity index 79%
rename from python/tests/toolchains/workspace_template/BUILD.bazel
rename to tests/toolchains/workspace_template/BUILD.bazel
index dd70844..7f3e7b0 100644
--- a/python/tests/toolchains/workspace_template/BUILD.bazel
+++ b/tests/toolchains/workspace_template/BUILD.bazel
@@ -1,5 +1,6 @@
 exports_files([
     "BUILD.bazel.tmpl",
+    "MODULE.bazel.tmpl",
     "WORKSPACE.tmpl",
     "python_version_test.py",
 ])
diff --git a/python/tests/toolchains/workspace_template/BUILD.bazel.tmpl b/tests/toolchains/workspace_template/BUILD.bazel.tmpl
similarity index 100%
rename from python/tests/toolchains/workspace_template/BUILD.bazel.tmpl
rename to tests/toolchains/workspace_template/BUILD.bazel.tmpl
diff --git a/tests/toolchains/workspace_template/MODULE.bazel.tmpl b/tests/toolchains/workspace_template/MODULE.bazel.tmpl
new file mode 100644
index 0000000..9e3a844
--- /dev/null
+++ b/tests/toolchains/workspace_template/MODULE.bazel.tmpl
@@ -0,0 +1,19 @@
+module(
+    name = "module_test",
+    version = "0.0.0",
+    compatibility_level = 1,
+)
+
+bazel_dep(name = "bazel_skylib", version = "1.3.0")
+bazel_dep(name = "rules_python", version = "0.0.0")
+local_path_override(
+    module_name = "rules_python",
+    path = "",
+)
+
+python = use_extension("@rules_python//python/extensions:python.bzl", "python")
+python.toolchain(
+    is_default = True,
+    python_version = "%python_version%",
+)
+use_repo(python, "python_versions", python = "python_%python_version%".replace(".", "_"))
diff --git a/python/tests/toolchains/workspace_template/README.md b/tests/toolchains/workspace_template/README.md
similarity index 100%
rename from python/tests/toolchains/workspace_template/README.md
rename to tests/toolchains/workspace_template/README.md
diff --git a/python/tests/toolchains/workspace_template/WORKSPACE.tmpl b/tests/toolchains/workspace_template/WORKSPACE.tmpl
similarity index 100%
rename from python/tests/toolchains/workspace_template/WORKSPACE.tmpl
rename to tests/toolchains/workspace_template/WORKSPACE.tmpl
diff --git a/python/tests/toolchains/workspace_template/python_version_test.py b/tests/toolchains/workspace_template/python_version_test.py
similarity index 100%
rename from python/tests/toolchains/workspace_template/python_version_test.py
rename to tests/toolchains/workspace_template/python_version_test.py