gristlabs_grist-core/sandbox/grist/test_side_effects.py

120 lines
4.4 KiB
Python
Raw Normal View History

# This test verifies behavior when a formula produces side effects. The prime example is
# lookupOrAddDerived() function, which adds new records (and is the basis for summary tables).
import objtypes
import test_engine
import testutil
class TestSideEffects(test_engine.EngineTestCase):
address_table_data = [
["id", "city", "state", "amount" ],
[ 21, "New York", "NY" , 1 ],
[ 22, "Albany", "NY" , 2 ],
]
schools_table_data = [
["id", "city" , "name" ],
[1, "Boston" , "MIT" ],
[2, "New York" , "NYU" ],
]
sample = testutil.parse_test_sample({
"SCHEMA": [
[1, "Address", [
[1, "city", "Text", False, "", "", ""],
[2, "state", "Text", False, "", "", ""],
[3, "amount", "Numeric", False, "", "", ""],
]],
[2, "Schools", [
[11, "name", "Text", False, "", "", ""],
[12, "city", "Text", False, "", "", ""],
]],
],
"DATA": {
"Address": address_table_data,
"Schools": schools_table_data,
}
})
def test_failure_after_side_effect(self):
# Verify that when a formula fails after a side-effect, the effect is reverted.
self.load_sample(self.sample)
formula = 'Schools.lookupOrAddDerived(city="TESTCITY")\nraise Exception("test-error")'
out_actions = self.apply_user_action(['AddColumn', 'Address', "A", { 'formula': formula }])
self.assertPartialOutActions(out_actions, { "stored": [
["AddColumn", "Address", "A", {"formula": formula, "isFormula": True, "type": "Any"}],
["AddRecord", "_grist_Tables_column", 13, {
"colId": "A", "formula": formula, "isFormula": True, "label": "A",
"parentId": 1, "parentPos": 4.0, "type": "Any", "widgetOptions": ""
}],
# The thing to note here is that while lookupOrAddDerived() should have added a row to
# Schools, the Exception negated it, and there is no action to add that row.
]})
# Check that data is as expected: no new records in Schools, one new column in Address.
self.assertTableData('Schools', cols="all", data=self.schools_table_data)
self.assertTableData('Address', cols="all", data=[
["id", "city", "state", "amount", "A" ],
[ 21, "New York", "NY" , 1, objtypes.RaisedException(Exception()) ],
[ 22, "Albany", "NY" , 2, objtypes.RaisedException(Exception()) ],
])
def test_calc_actions_in_side_effect_rollback(self):
self.load_sample(self.sample)
# Formula which allows a side effect to be conditionally rolled back.
formula = '''
Schools.lookupOrAddDerived(city=$city)
if $amount < 0:
raise Exception("test-error")
'''
self.add_column('Schools', 'ucity', formula='$city.upper()')
self.add_column('Address', 'A', formula=formula)
self.assertTableData('Schools', cols="all", data=[
["id", "city", "name", "ucity"],
[1, "Boston", "MIT", "BOSTON"],
[2, "New York", "NYU", "NEW YORK"],
[3, "Albany", "", "ALBANY"],
])
# Check that a successful side-effect which adds a row triggers calc actions for that row.
out_actions = self.update_record('Address', 22, city="aaa", amount=1000)
self.assertPartialOutActions(out_actions, {
"stored": [
["UpdateRecord", "Address", 22, {"amount": 1000.0, "city": "aaa"}],
["AddRecord", "Schools", 4, {"city": "aaa"}],
],
"calc": [
["UpdateRecord", "Schools", 4, {"ucity": "AAA"}],
],
})
self.assertTableData('Schools', cols="all", data=[
["id", "city", "name", "ucity"],
[1, "Boston", "MIT", "BOSTON"],
[2, "New York", "NYU", "NEW YORK"],
[3, "Albany", "", "ALBANY"],
[4, "aaa", "", "AAA"],
])
# Check that a side effect that failed and got rolled back does not include calc actions for
# the rows that didn't stay.
out_actions = self.update_record('Address', 22, city="bbb", amount=-3)
self.assertPartialOutActions(out_actions, {
"stored": [
["UpdateRecord", "Address", 22, {"amount": -3.0, "city": "bbb"}],
],
"calc": [
["UpdateRecord", "Address", 22, {"A": ["E", "Exception"]}],
],
})
self.assertTableData('Schools', cols="all", data=[
["id", "city", "name", "ucity"],
[1, "Boston", "MIT", "BOSTON"],
[2, "New York", "NYU", "NEW YORK"],
[3, "Albany", "", "ALBANY"],
[4, "aaa", "", "AAA"],
])