test.erp5.testExecuteJupyter.py 12.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2002-2015 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program 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 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################

from Products.ERP5Type.tests.SecurityTestCase import SecurityTestCase
from Products.ERP5Type.tests.utils import addUserToDeveloperRole
30
from Products.ERP5Type.tests.utils import createZODBPythonScript, removeZODBPythonScript
31 32 33

import time
import json
34
import transaction
35 36 37 38 39 40 41 42 43

class TestExecuteJupyter(SecurityTestCase):
  
  def afterSetUp(self):
    """
    Ran to set the environment
    """
    self.notebook_module = self.portal.getDefaultModule(portal_type='Data Notebook')
    self.assertTrue(self.notebook_module is not None)
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    # Create user to be used in tests
    user_folder = self.getPortal().acl_users
    user_folder._doAddUser('dev_user', '', ['Manager',], [])
    user_folder._doAddUser('member_user', '', ['Member','Authenticated',], [])
    # Assign developer role to user
    addUserToDeveloperRole('dev_user')
    self.tic()

  def _newNotebook(self, reference=None):
    """
    Function to create new notebook
    """
    return self.notebook_module.DataNotebookModule_addDataNotebook(
      title='Some Notebook Title',
      reference=reference,
      form_id='DataNotebookModule_viewAddNotebookDialog',
      batch_mode=True
      )

64
  def _newNotebookLine(self, notebook_module=None, notebook_code=None):
65
    """
66
    Function to create new notebook line
67
    """
68
    return notebook_module.DataNotebook_addDataNotebookLine(
69 70 71 72
      notebook_code=notebook_code,
      batch_mode=True
      )

73
  def testJupyterCompileErrorRaise(self):
74
    """
75 76
    Test if JupyterCompile portal_component raises error on the server side.
    Take the case in which one line in a statement is valid and another is not.
77 78 79 80 81
    """
    portal = self.getPortalObject()
    script_id = "JupyterCompile_errorResult"
    script_container = portal.portal_skins.custom

82
    new_test_title = "Wendelin Test 1"
83 84
    # Check if the existing title is different from new_test_title or not
    if portal.getTitle()==new_test_title:
85
      new_test_title = "Wendelin"
86 87

    python_script = """
88 89
portal = context.getPortalObject()
portal.setTitle('%s')
90 91 92 93 94
print an_undefined_variable
"""%new_test_title

    # Create python_script object with the above given code and containers
    createZODBPythonScript(script_container, script_id, '', python_script)
95
    self.tic()
96 97 98 99 100 101 102 103

    # Call the above created script in jupyter_code
    jupyter_code = """
portal = context.getPortalObject()
portal.%s()
"""%script_id

    # Make call to Base_runJupyter to run the jupyter code which is making
104 105 106 107 108 109 110 111 112 113 114 115 116 117
    # a call to the newly created ZODB python_script and assert if the call raises
    # NameError as we are sending an invalid python_code to it
    self.assertRaises(
                      NameError,
                      portal.Base_runJupyter,
                      jupyter_code=jupyter_code,
                      old_local_variable_dict={}
                      )
    # Abort the current transaction of test so that we can proceed to new one
    transaction.abort()
    # Clear the portal cache from previous transaction
    self.portal.portal_caches.clearAllCache()
    # Remove the ZODB python script created above
    removeZODBPythonScript(script_container, script_id)
118 119 120 121

    # Test that calling Base_runJupyter shouldn't change the context Title
    self.assertNotEqual(portal.getTitle(), new_test_title)

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
  def testUserCannotAccessBaseExecuteJupyter(self):
    """
    Test if non developer user can't access Base_executeJupyter
    """
    portal = self.portal

    self.login('member_user')
    result = portal.Base_executeJupyter.Base_checkPermission('portal_components', 'Manage Portal')

    self.assertFalse(result)

  def testUserCanCreateNotebookWithoutCode(self):
    """
    Test the creation of Data Notebook object
    """
    portal = self.portal

    notebook = self._newNotebook(reference='new_notebook_without any_code')
    self.tic()

    notebook_search_result = portal.portal_catalog(
                                      portal_type='Data Notebook',
                                      title='Some Notebook Title'
                                      )

    result_title = [obj.getTitle() for obj in notebook_search_result]
    if result_title:
      self.assertEquals(notebook.getTitle(), result_title[0])
150

151 152
  def testUserCanCreateNotebookWithCode(self):
    """
153
    Test if user can create Data Notebook Line object or not
154 155 156
    """
    portal = self.portal

157
    notebook = self._newNotebook(reference='new_notebook_with_code %s' %time.time())
158
    self.tic()
159 160

    notebook_code='some_random_invalid_notebook_code %s' % time.time()
161
    notebook_line = self._newNotebookLine(
162 163 164 165 166
                            notebook_module=notebook,
                            notebook_code=notebook_code
                            )
    self.tic()

167
    notebook_line_search_result = portal.portal_catalog(portal_type='Data Notebook Line')
168

169 170
    result_reference_list = [obj.getReference() for obj in notebook_line_search_result]
    result_id_list = [obj.getId() for obj in notebook_line_search_result]
171

172 173 174 175
    if result_reference_list:
      self.assertIn(notebook.getReference(), result_reference_list)
      self.assertEquals(notebook_line.getReference(), notebook.getReference())
      self.assertIn(notebook_line.getId(), result_id_list)
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

  def testBaseExecuteJupyterAddNewNotebook(self):
    """
    Test the functionality of Base_executeJupyter python script.
    This test will cover folowing cases - 
    1. Call to Base_executeJupyter without python_expression
    2. Creating new notebook using the script
    """
    portal = self.portal
    self.login('dev_user')
    reference = 'Test.Notebook.AddNewNotebook %s' % time.time()
    title = 'Test new NB Title %s' % time.time()

    portal.Base_executeJupyter(title=title, reference=reference)
    self.tic()

    notebook_list = portal.portal_catalog(
                                    portal_type='Data Notebook',
                                    reference=reference
                                    )

    self.assertEquals(len([obj.getTitle() for obj in notebook_list]), 1)

199
  def testBaseExecuteJupyterAddNotebookLine(self):
200
    """
201
    Test if the notebook adds code history to the Data Notebook Line
202 203
    portal type while multiple calls are made to Base_executeJupyter with
    notebooks having same reference
204 205 206 207
    """
    portal = self.portal
    self.login('dev_user')
    python_expression = "print 52"
208
    reference = 'Test.Notebook.AddNewNotebookLine %s' % time.time()
209
    title = 'Test NB Title %s' % time.time()
210

211 212 213 214 215 216 217 218 219 220 221
    # Calling the function twice, first to create a new notebook and then
    # sending python_expression to check if it adds to the same notebook
    portal.Base_executeJupyter(title=title, reference=reference)
    self.tic()

    portal.Base_executeJupyter(
                              reference=reference,
                              python_expression=python_expression
                              )
    self.tic()

222
    notebook = portal.portal_catalog.getResultValue(
223 224 225 226
                                          portal_type='Data Notebook',
                                          reference=reference
                                          )

227
    notebook_line_search_result = portal.portal_catalog.getResultValue(
228 229
                                              portal_type='Data Notebook Line',
                                              reference=reference
230
                                              )
231 232 233 234 235 236
    # As we use timestamp in the reference and the notebook is created in this
    # function itself so, if anyhow a new Data Notebook Line has been created,
    # then it means that the code has been added to Input and Output of Data
    # Notebook Line portal_type
    if notebook_line_search_result:
      self.assertEquals(notebook.getReference(), notebook_line_search_result.getReference())
237 238 239

  def testBaseExecuteJupyterErrorHandling(self):
    """
240 241 242
    Test if the Base_executeJupyter with invalid python code raises error on
    server side. We are not catching the exception here. Expected result is
    raise of exception.
243 244 245 246 247 248 249
    """
    portal = self.portal
    self.login('dev_user')
    python_expression = 'some_random_invalid_python_code'
    reference = 'Test.Notebook.ExecuteJupyterErrorHandling %s' % time.time()
    title = 'Test NB Title %s' % time.time()

250 251 252 253 254 255 256
    self.assertRaises(
                      NameError, 
                      portal.Base_executeJupyter,
                      title=title,
                      reference=reference,
                      python_expression=python_expression
                      )
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

  def testBaseExecuteJupyterSaveActiveResult(self):
    """
    Test if the result is being saved inside active_process and the user can
    access the loacl variable and execute python expression on them
    """
    portal = self.portal
    self.login('dev_user')
    python_expression = 'a=2; b=3; print a+b'
    reference = 'Test.Notebook.ExecutePythonExpressionWithVariables %s' % time.time()
    title = 'Test NB Title %s' % time.time()

    portal.Base_executeJupyter(
                              title=title,
                              reference=reference,
                              python_expression=python_expression
                              )
    self.tic()

    notebook_list = portal.portal_catalog(
                                          portal_type='Data Notebook',
                                          reference=reference
                                          )
    notebook = notebook_list[0]
    process_id = notebook.getProcess()
    active_process = portal.portal_activities[process_id]
    result_list = active_process.getResultList()
    result = {'a':2, 'b':3}
    self.assertDictContainsSubset(result, result_list[0].summary)

  def testBaseExecuteJupyterRerunWithPreviousLocalVariables(self):
    """
    Test if the Base_compileJupyter function in extension is able to recognize
    the local_variables from the previous run and execute the python code
    """
    portal = self.portal
    self.login('dev_user')
    python_expression = 'a=2; b=3; print a+b'
    reference = 'Test.Notebook.ExecutePythonExpressionWithVariables %s' % time.time()
    title = 'Test NB Title %s' % time.time()

    portal.Base_executeJupyter(
                              title=title,
                              reference=reference,
                              python_expression=python_expression
                              )
    self.tic()

    python_expression = 'x=5; b=4; print a+b+x'
    result = portal.Base_executeJupyter(
                                        reference=reference,
                                        python_expression=python_expression
                                        )
    self.tic()

    expected_result = '11'
    self.assertEquals(json.loads(result)['code_result'].rstrip(), expected_result)

  def testBaseExecuteJupyterWithContextObjectsAsLocalVariables(self):
    """
    Test Base_executeJupyter with context objects as local variables
    """
    portal = self.portal
    self.login('dev_user')
    python_expression = 'a=context.getPortalObject(); print a.getTitle()'
    reference = 'Test.Notebook.ExecutePythonExpressionWithVariables %s' % time.time()
    title = 'Test NB Title %s' % time.time()

    result = portal.Base_executeJupyter(
                                        title=title,
                                        reference=reference,
                                        python_expression=python_expression
                                        )
    self.tic()

    expected_result = portal.getTitle()
    self.assertEquals(json.loads(result)['code_result'].rstrip(), expected_result)