2020-07-27 18:57:36 +00:00
|
|
|
# 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)
|
|
|
|
|
2022-05-23 18:02:39 +00:00
|
|
|
formula = 'Schools.lookupOrAddDerived(city="TESTCITY")\nraise Exception("test-error")\nNone'
|
2020-07-27 18:57:36 +00:00
|
|
|
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": ""
|
|
|
|
}],
|
2020-11-02 15:48:47 +00:00
|
|
|
["BulkUpdateRecord", "Address", [21, 22], {"A": [["E", "Exception"], ["E", "Exception"]]}],
|
2020-07-27 18:57:36 +00:00
|
|
|
# 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")
|
2022-05-23 18:02:39 +00:00
|
|
|
return None
|
2020-07-27 18:57:36 +00:00
|
|
|
'''
|
|
|
|
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"}],
|
|
|
|
["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"}],
|
|
|
|
["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"],
|
|
|
|
])
|