From c5f043eb50e735418287ee394410e5cd66d07033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 12 Jul 2022 22:25:47 +0900
Subject: [PATCH] ERP5Type/tests: support Zope4's
 Testing.makerequest.makerequest

On Zope2, there was support for starting a ZServer (in
Testing.ZopeTestCase.utils.startZServer) and there was another makerequest
(in Testing.ZopeTestCase.utils.makerequest) which had knowledge of this
ZServer address. We are using our own implementation of startZServer,
which also knows how to start a WSGI server, but we we were interacting
with Testing.ZopeTestCase.utils so that its makerequest knows about
our web server.

On Zope4 there is no utility to start an http server from the tests and
the makerequest is also gone.

This revisits the web server from test interaction with makerequest:
 - no longer use PortalTestCase._app which creates an app with a request
   to http://nohost, but implement our own _app which creates a request
   to our web server
  - store our server address as class attributes on ProcessingNodeTestCase
   instead of patching pseudo constants in utils (these pseudo constants
   no longer exist)
  - in the case of ERP5TypeLiveTestCase, also rewrap the site to use a
  request to our web server (which was done on Zope2 by using
  Testing.ZopeTestCase.utils.makerequest and not
  Testing.makerequest.makerequest directly)
---
 .../ERP5Type/tests/ERP5TypeLiveTestCase.py    | 12 +++++++--
 product/ERP5Type/tests/ERP5TypeTestCase.py    | 25 ++++++++++++++++---
 .../ERP5Type/tests/ProcessingNodeTestCase.py  | 12 ++++-----
 3 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/product/ERP5Type/tests/ERP5TypeLiveTestCase.py b/product/ERP5Type/tests/ERP5TypeLiveTestCase.py
index 6bcac550455..dd9d9a4a738 100644
--- a/product/ERP5Type/tests/ERP5TypeLiveTestCase.py
+++ b/product/ERP5Type/tests/ERP5TypeLiveTestCase.py
@@ -89,12 +89,20 @@ class ERP5TypeLiveTestCase(ERP5TypeTestCaseMixin):
       site = getSite()
       # reconstruct the acquisition chain with an independent request.
       #   RequestContainer -> Application -> Site
-      from Testing.ZopeTestCase.utils import makerequest
+      from Testing.makerequest import makerequest
+      environ = {}
+      if self._server_address:
+        host, port = self._server_address
+        environ={
+          'SERVER_NAME': host,
+          'SERVER_PORT': port,
+        }
       portal = getattr(
-        makerequest(aq_base(site.aq_parent)),
+        makerequest(aq_base(site.aq_parent), environ=environ),
         site.getId())
 
       # Make the various get_request patches return this request.
+      # TODO: check this is still needed
       # This is for ERP5TypeTestCase patch
       from Testing.ZopeTestCase.connections import registry
       if registry:
diff --git a/product/ERP5Type/tests/ERP5TypeTestCase.py b/product/ERP5Type/tests/ERP5TypeTestCase.py
index 738dec13de4..41a4cd95114 100644
--- a/product/ERP5Type/tests/ERP5TypeTestCase.py
+++ b/product/ERP5Type/tests/ERP5TypeTestCase.py
@@ -57,7 +57,8 @@ getRequest.__code__ = (lambda: get_request()).__code__
 from zope.site.hooks import setSite
 
 from Testing import ZopeTestCase
-from Testing.ZopeTestCase import PortalTestCase, user_name
+from Testing.makerequest import makerequest
+from Testing.ZopeTestCase import PortalTestCase, user_name, ZopeLite
 from Products.ERP5Type.Core.Workflow import ValidationFailed
 from Products.PythonScripts.PythonScript import PythonScript
 from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
@@ -1019,9 +1020,25 @@ class ERP5TypeCommandLineTestCase(ERP5TypeTestCaseMixin):
     def _app(self):
       '''Opens a ZODB connection and returns the app object.
 
-      We override it to patch HTTP_ACCEPT_CHARSET into REQUEST to get the zpt
-      unicode conflict resolver to work properly'''
-      app = PortalTestCase._app(self)
+      We override this method so that the request knows about the address of
+      our http server and also to set HTTP_ACCEPT_CHARSET set in REQUEST, to
+      get the zpt unicode conflict resolver to
+      work properly.
+
+      We reimplement PortalTestCase._app instead of calling it, because it
+      opens a ZODB connection and wrap the root object a request to
+      http://nohost, but we prefer to create directly a connection to the
+      app wrapped in a request to our web server.
+      '''
+      app = ZopeLite.app()
+      environ = {}
+      if self._server_address:
+        host, port = self._server_address
+        environ['SERVER_NAME'] = host
+        environ['SERVER_PORT'] = port
+
+      app = makerequest(app, environ=environ)
+      registry.register(app)
       app.REQUEST['HTTP_ACCEPT_CHARSET'] = 'utf-8'
       return app
 
diff --git a/product/ERP5Type/tests/ProcessingNodeTestCase.py b/product/ERP5Type/tests/ProcessingNodeTestCase.py
index 667394b5167..3ff1296ad27 100644
--- a/product/ERP5Type/tests/ProcessingNodeTestCase.py
+++ b/product/ERP5Type/tests/ProcessingNodeTestCase.py
@@ -138,6 +138,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
   the node running the unit tests to tell other nodes on which portal activities
   should be processed.
   """
+  _server_address = None # (host, port) of the http server if it was started, None otherwise
 
   @staticmethod
   def asyncore_loop():
@@ -149,8 +150,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
 
   def startZServer(self, verbose=False):
     """Start HTTP ZServer in background"""
-    utils = ZopeTestCase.utils
-    if utils._Z2HOST is None:
+    if self._server_address is None:
       from Products.ERP5Type.tests.runUnitTest import log_directory
       log = os.path.join(log_directory, "Z2.log")
       message = "Running %s server at %s:%s\n"
@@ -192,7 +192,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
           logger.propagate = False
           hs = createServer(app_wrapper(webdav_ports=webdav_ports),
             logger, sockets=sockets)
-          utils._Z2HOST, utils._Z2PORT = hs.addr
+          ProcessingNodeTestCase._server_address = hs.addr
           t = Thread(target=hs.run)
           t.setDaemon(1)
           t.start()
@@ -204,7 +204,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
         except RuntimeError as e:
           ZopeTestCase._print(str(e))
         else:
-          utils._Z2HOST, utils._Z2PORT = hs.server_name, hs.server_port
+          ProcessingNodeTestCase._server_address = hs.server_name, hs.server_port
           _print(hs)
           try:
             _print(createZServer(log, zserver_type='webdav'))
@@ -220,7 +220,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
         if ActivityTool.currentNode == ActivityTool._server_address:
           ActivityTool.currentNode = None
         ActivityTool._server_address = None
-    return utils._Z2HOST, utils._Z2PORT
+    return self._server_address
 
   def _registerNode(self, distributing, processing):
     """Register node to process and/or distribute activities"""
@@ -241,7 +241,7 @@ class ProcessingNodeTestCase(ZopeTestCase.TestCase):
 
   @classmethod
   def unregisterNode(cls):
-    if ZopeTestCase.utils._Z2HOST is not None:
+    if cls._server_address is not None:
       self = cls('unregisterNode')
       self.app = self._app()
       self._registerNode(distributing=0, processing=0)
-- 
2.30.9