class SchemaTest(unittest.TestCase): def setUp(self): client = MongoClient() client.drop_database('metaphor2_test_db') self.db = client.metaphor2_test_db self.schema = Schema(self.db) self.maxDiff = None def test_load_basic_spec(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, "age": { "type": "int" } }, }, } }) self.schema.load_schema() self.assertEquals(1, len(self.schema.specs)) self.assertEquals( "str", self.schema.specs['employee'].fields['name'].field_type) self.assertEquals( "int", self.schema.specs['employee'].fields['age'].field_type) def test_load_basic_link_with_reverse_link(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, "department": { "fields": { "manager": { "type": "link", "target_spec_name": "employee", }, }, }, } }) self.schema.load_schema() self.assertEquals(2, len(self.schema.specs)) self.assertEquals( "str", self.schema.specs['employee'].fields['name'].field_type) self.assertEquals( "link", self.schema.specs['department'].fields['manager'].field_type) self.assertEquals( "employee", self.schema.specs['department'].fields['manager'].target_spec_name) self.assertEquals( "reverse_link", self.schema.specs['employee']. fields['link_department_manager'].field_type) self.assertEquals( "department", self.schema.specs['employee']. fields['link_department_manager'].target_spec_name) def test_load_collection_with_parent_link(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, "department": { "fields": { "employees": { "type": "collection", "target_spec_name": "employee", }, }, }, }, }) self.schema.load_schema() self.assertEquals(2, len(self.schema.specs)) self.assertEquals( "str", self.schema.specs['employee'].fields['name'].field_type) self.assertEquals( "collection", self.schema.specs['department'].fields['employees'].field_type) self.assertEquals( "employee", self.schema.specs['department'].fields['employees']. target_spec_name) self.assertEquals( "parent_collection", self.schema.specs['employee']. fields['parent_department_employees'].field_type) self.assertEquals( "department", self.schema.specs['employee']. fields['parent_department_employees'].target_spec_name) def test_load_link_collection_with_reverse_link(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, "department": { "fields": { "parttimers": { "type": "linkcollection", "target_spec_name": "employee", }, }, }, } }) self.schema.load_schema() self.assertEquals(2, len(self.schema.specs)) self.assertEquals( "str", self.schema.specs['employee'].fields['name'].field_type) self.assertEquals( "linkcollection", self.schema.specs['department'].fields['parttimers'].field_type) self.assertEquals( "employee", self.schema.specs['department'].fields['parttimers']. target_spec_name) self.assertEquals( "reverse_link_collection", self.schema.specs['employee']. fields['link_department_parttimers'].field_type) self.assertEquals( "department", self.schema.specs['employee']. fields['link_department_parttimers'].target_spec_name) def test_save_resource_encode_id(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, } }) self.schema.load_schema() employee_id = self.schema.insert_resource('employee', {'name': 'Bob'}, 'employees') new_resource = self.db.resource_employee.find_one() self.assertEquals(ObjectId(employee_id[2:]), self.schema.decodeid(employee_id)) self.assertEquals( { '_id': self.schema.decodeid(employee_id), '_grants': [], '_canonical_url': '/employees/%s' % employee_id, 'name': 'Bob', '_parent_canonical_url': '/', '_parent_field_name': 'employees', '_parent_id': None, '_parent_type': 'root', }, new_resource) self.assertEquals('ID%s' % (new_resource['_id'], ), employee_id) def test_update_field(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, } }) self.schema.load_schema() employee_id = self.schema.insert_resource('employee', {'name': 'Bob'}, 'employees') self.schema.update_resource_fields('employee', employee_id, {'name': 'Ned'}) reload_employee = self.db.resource_employee.find_one( {'_id': self.schema.decodeid(employee_id)}) self.assertEquals('Ned', reload_employee['name']) def test_roots(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, }, }, "department": { "fields": { "manager": { "type": "link", "target_spec_name": "employee", }, }, }, }, "root": { "employees": { "type": "collection", "target_spec_name": "employee", }, "departments": { "type": "collection", "target_spec_name": "department", } }, }) self.schema.load_schema() self.assertEquals(2, len(self.schema.specs)) self.assertEquals(2, len(self.schema.root.fields)) self.assertEquals('collection', self.schema.root.fields['employees'].field_type) self.assertEquals( 'employee', self.schema.root.fields['employees'].target_spec_name) self.assertEquals('collection', self.schema.root.fields['departments'].field_type) self.assertEquals( 'department', self.schema.root.fields['departments'].target_spec_name) def test_canonical_url(self): self.db.metaphor_schema.insert_one({ "specs": { "employee": { "fields": { "name": { "type": "str" }, "age": { "type": "int" }, "division": { "type": "link", "target_spec_name": "division", }, }, }, "division": { "fields": { "name": { "type": "str", }, "yearly_sales": { "type": "int", }, "sections": { "type": "collection", "target_spec_name": "section", } }, }, "section": { "fields": { "name": { "type": "str", }, }, }, }, "root": { "employees": { "type": "collection", "target_spec_name": "employee", }, "divisions": { "type": "collection", "target_spec_name": "division", } }, }) self.schema.load_schema() division_id_1 = self.schema.insert_resource('division', { 'name': 'sales', 'yearly_sales': 100 }, 'divisions') self.assertEquals( { '_id': self.schema.decodeid(division_id_1), '_grants': [], '_canonical_url': '/divisions/%s' % division_id_1, '_parent_id': None, '_parent_type': 'root', '_parent_field_name': 'divisions', '_parent_canonical_url': '/', 'name': 'sales', 'yearly_sales': 100, }, self.db['resource_division'].find_one( {'_id': self.schema.decodeid(division_id_1)})) section_id_1 = self.schema.insert_resource( 'section', {'name': 'appropriation'}, parent_type='division', parent_id=division_id_1, parent_field_name='sections') self.assertEquals( { '_id': self.schema.decodeid(division_id_1), '_grants': [], '_canonical_url': '/divisions/%s' % division_id_1, '_parent_id': None, '_parent_type': 'root', '_parent_field_name': 'divisions', '_parent_canonical_url': '/', 'name': 'sales', 'yearly_sales': 100, }, self.db['resource_division'].find_one( {'_id': self.schema.decodeid(division_id_1)})) self.assertEquals( { '_id': self.schema.decodeid(section_id_1), '_grants': [], '_canonical_url': '/divisions/%s/sections/%s' % (division_id_1, section_id_1), '_parent_id': self.schema.decodeid(division_id_1), '_parent_type': 'division', '_parent_field_name': 'sections', '_parent_canonical_url': '/divisions/%s' % division_id_1, 'name': 'appropriation', }, self.db['resource_section'].find_one( {'_id': self.schema.decodeid(section_id_1)})) def test_calc_infer_type(self): spec = self.schema.add_spec('employees') self.schema.add_field(spec, 'name', 'str') calc_field = self.schema.add_calc(spec, 'current_name', 'self.name') self.assertEquals('str', calc_field.infer_type().field_type) self.assertTrue(calc_field.is_primitive()) self.assertFalse(calc_field.is_collection()) def test_calc_infer_type_collection(self): spec = self.schema.add_spec('employees') buddy_spec = self.schema.add_spec('buddy') self.schema.add_field(spec, 'buddies', 'collection', 'buddy') calc_field = self.schema.add_calc(spec, 'all_buddies', 'self.buddies') self.assertEquals(buddy_spec, calc_field.infer_type()) self.assertFalse(calc_field.is_primitive()) self.assertTrue(calc_field.is_collection()) def test_delete_linkcollection_entry(self): pass def test_parse_fields_test(self): # add parsing and validation for field types pass def test_initialize_schema(self): self.schema.create_initial_schema() schema = self.db.metaphor_schema.find_one() self.assertEqual( { "groups": { "target_spec_name": "group", "type": "collection" }, "users": { "target_spec_name": "user", "type": "collection" } }, schema['root']) self.assertEqual( { 'grant': { 'fields': { 'type': { 'type': 'str' }, 'url': { 'type': 'str' } } }, 'group': { 'fields': { 'grants': { 'target_spec_name': 'grant', 'type': 'collection' }, 'name': { 'type': 'str' } } }, 'user': { 'fields': { 'admin': { 'type': 'bool' }, 'create_grants': { 'calc_str': "self.groups.grants[type='create'].url", 'deps': [ 'grant.type', 'grant.url', 'group.grants', 'user.groups' ], 'type': 'calc' }, 'delete_grants': { 'calc_str': "self.groups.grants[type='delete'].url", 'deps': [ 'grant.type', 'grant.url', 'group.grants', 'user.groups' ], 'type': 'calc' }, 'groups': { 'target_spec_name': 'group', 'type': 'linkcollection' }, 'password': { 'type': 'str' }, 'put_grants': { 'calc_str': "self.groups.grants[type='put'].url", 'deps': [ 'grant.type', 'grant.url', 'group.grants', 'user.groups' ], 'type': 'calc' }, 'read_grants': { 'calc_str': "self.groups.grants[type='read'].url", 'deps': [ 'grant.type', 'grant.url', 'group.grants', 'user.groups' ], 'type': 'calc' }, 'update_grants': { 'calc_str': "self.groups.grants[type='update'].url", 'deps': [ 'grant.type', 'grant.url', 'group.grants', 'user.groups' ], 'type': 'calc' }, 'username': { 'type': 'str' } } } }, schema['specs']) def test_load_calcs_by_dependency(self): self.schema.create_spec('employee') self.schema.create_field('employee', 'name', 'str') self.schema.create_spec('branch') self.schema.create_field('branch', 'income', 'int') self.schema.create_field('branch', 'employees', 'collection', 'employee') self.schema.create_spec('section') self.schema.create_field('section', 'branch', 'link', 'branch') self.schema.load_schema() self.schema.create_field( 'employee', 'average_section_income', 'calc', None, 'average(self.parent_branch_employees.income)') self.schema.create_field('branch', 'section', 'calc', None, 'self.link_section_branch') self.schema.create_field('section', 'employees', 'calc', None, 'self.branch.employees') expected = { "employee": { "fields": { "name": { "type": "str" }, "average_section_income": { "type": "calc", "calc_str": "average(self.parent_branch_employees.income)", "deps": ["branch.income"], }, }, }, "branch": { "fields": { "income": { "type": "int", }, "employees": { "type": "collection", "target_spec_name": "employee", }, "section": { "type": "calc", "calc_str": "self.link_section_branch", "deps": ["section.branch"], }, }, }, "section": { "fields": { "branch": { "type": "link", "target_spec_name": "branch", }, "employees": { "type": "calc", "calc_str": "self.branch.employees", "deps": ["branch.employees", "section.branch"], }, }, }, } self.assertEqual(expected, self.db.metaphor_schema.find_one()['specs']) self.schema.load_schema() self.assertEquals(3, len(self.schema.specs)) def test_load_calcs_by_dependency_almost_circular(self): self.schema.create_spec('primary') self.schema.create_field('primary', 'name', 'str') self.schema.create_field('primary', 'calced_name', 'calc', calc_str="self.name + 'a'") self.schema.create_spec('secondary') self.schema.create_field('secondary', 'name', 'str') self.schema.create_field('secondary', 'calced_name', 'calc', calc_str="self.name + 'b'") self.schema.create_field('primary', 'secondary', 'link', 'secondary') self.schema.create_field('secondary', 'primary', 'link', 'primary') self.schema.create_field('primary', 'secondary_name', 'calc', calc_str="self.secondary.calced_name") self.schema.create_field('secondary', 'primary_name', 'calc', calc_str="self.primary.calced_name") self.schema.load_schema() self.assertEquals(2, len(self.schema.specs))
class ApiTest(unittest.TestCase): def setUp(self): self.maxDiff = None client = MongoClient() client.drop_database('metaphor2_test_db') self.db = client.metaphor2_test_db self.schema = Schema(self.db) self.schema.create_initial_schema() self.employee_spec = self.schema.create_spec('employee') self.schema.create_field('employee', 'name', 'str') self.schema.create_field('employee', 'age', 'int') self.schema.create_field('employee', 'created', 'datetime') self.division_spec = self.schema.create_spec('division') self.schema.create_field('division', 'name', 'str') self.schema.create_field('division', 'employees', 'collection', 'employee') self.schema.create_field('division', 'parttimers', 'linkcollection', 'employee') self.schema.create_field('division', 'contractors', 'linkcollection', 'employee') self.schema.create_field('root', 'employees', 'collection', 'employee') self.schema.create_field('root', 'former_employees', 'collection', 'employee') self.schema.create_field('root', 'divisions', 'collection', 'division') self.calcs_spec = self.schema.create_spec('calcs') self.schema.create_field('calcs', 'total_employees', 'calc', calc_str='sum(employees.age)') self.schema.create_field('calcs', 'total_former_employees', 'calc', calc_str='sum(former_employees.age)') #self.schema.create_field('calcs', 'total_division_parttimers', 'calc', calc_str='sum(divisions.parttimers.age)') #self.schema.create_field('calcs', 'total_division_contractors', 'calc', calc_str='sum(divisions.contractors.age)') self.schema.create_field('root', 'calcs', 'collection', 'calcs') self.api = Api(self.schema) def test_move_resource(self): employee_id_1 = self.api.post('/employees', {'name': 'bob', 'age': 1}) # check calc self.calcs_id = self.api.post('/calcs', {}) self.assertEqual(1, self.api.get('/calcs/%s' % self.calcs_id)['total_employees']) self.api.put('/former_employees', {'_from': '/employees/%s' % employee_id_1}) former_employees = self.api.get('/former_employees') self.assertEqual(1, former_employees['count']) self.assertEqual('bob', former_employees['results'][0]['name']) # no longer at original collection self.assertEqual(0, self.api.get('/employees')['count']) # assert calc update # apparently sum of empty list gives null self.assertEqual(None, self.api.get('/calcs/%s' % self.calcs_id)['total_employees']) self.assertEqual(1, self.api.get('/calcs/%s' % self.calcs_id)['total_former_employees']) def test_move_collection(self): employee_id_1 = self.api.post('/employees', {'name': 'bob', 'age': 1}) employee_id_2 = self.api.post('/employees', {'name': 'ned', 'age': 1}) self.api.put('/former_employees', {'_from': '/employees'}) former_employees = self.api.get('/former_employees') self.assertEqual(2, former_employees['count']) self.assertEqual('bob', former_employees['results'][0]['name']) self.assertEqual('ned', former_employees['results'][1]['name']) # no longer at original collection self.assertEqual(0, self.api.get('/employees')['count']) def test_move_between_nested_collections(self): division_id_1 = self.api.post('/divisions', {'name': 'sales'}) division_id_2 = self.api.post('/divisions', {'name': 'marketting'}) employee_id_1 = self.api.post('/divisions/%s/employees' % division_id_1, {'name': 'bob'}) employee_id_2 = self.api.post('/divisions/%s/employees' % division_id_2, {'name': 'ned'}) self.api.put('/divisions/%s/employees' % division_id_2, {'_from': '/divisions/%s/employees/%s' % (division_id_1, employee_id_1)}) self.assertEqual(0, self.api.get('/divisions/%s/employees' % division_id_1)['count']) self.assertEqual(2, self.api.get('/divisions/%s/employees' % division_id_2)['count']) self.assertEqual('bob', self.api.get('/divisions/%s/employees/%s' % (division_id_2, employee_id_1))['name']) self.assertEqual('ned', self.api.get('/divisions/%s/employees/%s' % (division_id_2, employee_id_2))['name']) def test_basic_update(self): employee_id_1 = self.api.post('/employees', {'name': 'bob', 'age': 1}) # check calc self.calcs_id = self.api.post('/calcs', {}) self.assertEqual(1, self.api.get('/calcs/%s' % self.calcs_id)['total_employees'])
class UpdaterTest(unittest.TestCase): def setUp(self): self.maxDiff = None client = MongoClient() client.drop_database('metaphor2_test_db') self.db = client.metaphor2_test_db self.schema = Schema(self.db) self.schema.create_initial_schema() self.updater = Updater(self.schema) self.employee_spec = self.schema.create_spec('employee') self.schema.create_field('employee', 'name', 'str') self.schema.create_field('employee', 'age', 'int') self.division_spec = self.schema.create_spec('division') self.schema.create_field('division', 'name', 'str') self.schema.create_field('division', 'employees', 'collection', 'employee') self.schema.create_field('root', 'divisions', 'collection', 'division') def test_updater(self): self.schema.add_calc(self.division_spec, 'older_employees', 'self.employees[age>30]') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) self.updater.update_calc('division', 'older_employees', division_id_1) division_data = self.db.resource_division.find_one() self.assertEquals( { '_id': self.schema.decodeid(division_id_1), '_grants': [], '_canonical_url': '/divisions/%s' % division_id_1, 'name': 'sales', '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'older_employees': [ObjectId(employee_id_1[2:])], }, division_data) employee_id_2 = self.schema.insert_resource('employee', { 'name': 'Ned', 'age': 41 }, 'employees', 'division', division_id_1) # check again self.updater.update_calc('division', 'older_employees', division_id_1) division_data = self.db.resource_division.find_one() self.assertEquals( { '_id': self.schema.decodeid(division_id_1), '_grants': [], '_canonical_url': '/divisions/%s' % division_id_1, 'name': 'sales', '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'older_employees': [ObjectId(employee_id_1[2:]), ObjectId(employee_id_2[2:])], }, division_data) def test_reverse_aggregation(self): self.schema.add_calc(self.division_spec, 'older_employees', 'self.employees[age>30]') self.schema.add_calc(self.division_spec, 'average_age', 'average(self.employees.age)') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) division_id_2 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_2 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_2) average_agg = self.updater.build_reverse_aggregations_to_calc( 'division', 'average_age', self.employee_spec, employee_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(employee_id_1) } }, { "$lookup": { "from": "resource_division", "localField": "_parent_id", "foreignField": "_id", "as": "_field_employees", } }, { '$group': { '_id': '$_field_employees' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], average_agg) affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'average_age', self.employee_spec, employee_id_1) self.assertEquals([self.schema.decodeid(division_id_1)], list(affected_ids)) # check another collection employee_id_3 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_2) affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'average_age', self.employee_spec, employee_id_3) self.assertEquals([self.schema.decodeid(division_id_2)], list(affected_ids)) # different calc older_agg = self.updater.build_reverse_aggregations_to_calc( 'division', 'older_employees', self.employee_spec, employee_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(employee_id_1) } }, { "$lookup": { "from": "resource_division", "localField": "_parent_id", "foreignField": "_id", "as": "_field_employees", } }, { '$group': { '_id': '$_field_employees' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], average_agg) def test_reverse_aggregation_link(self): self.schema.add_field(self.division_spec, 'manager', 'link', 'employee') self.schema.add_calc(self.division_spec, 'manager_age', 'self.manager.age') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) self.schema.update_resource_fields('division', division_id_1, {'manager': employee_id_1}) agg = self.updater.build_reverse_aggregations_to_calc( 'division', 'manager_age', self.employee_spec, employee_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(employee_id_1) } }, { "$lookup": { "from": "resource_division", "localField": "_id", "foreignField": "manager", "as": "_field_manager", } }, { '$group': { '_id': '$_field_manager' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) # check affected ids affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'manager_age', self.employee_spec, employee_id_1) self.assertEquals([self.schema.decodeid(division_id_1)], list(affected_ids)) # check having two links division_id_2 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') self.schema.update_resource_fields('division', division_id_2, {'manager': employee_id_1}) affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'manager_age', self.employee_spec, employee_id_1) self.assertEquals([ self.schema.decodeid(division_id_1), self.schema.decodeid(division_id_2) ], list(affected_ids)) def test_reverse_aggregation_link_collection(self): self.schema.add_field(self.division_spec, 'managers', 'linkcollection', 'employee') self.schema.add_calc(self.division_spec, 'average_manager_age', 'average(self.managers.age)') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) self.schema.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_1) agg = self.updater.build_reverse_aggregations_to_calc( 'division', 'average_manager_age', self.employee_spec, employee_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(employee_id_1) } }, { "$lookup": { "from": "resource_division", "foreignField": "managers._id", "localField": "_id", "as": "_field_managers", } }, { '$group': { '_id': '$_field_managers' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) # check affected ids affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'average_manager_age', self.employee_spec, employee_id_1) self.assertEquals([self.schema.decodeid(division_id_1)], list(affected_ids)) division_id_2 = self.schema.insert_resource('division', {'name': 'marketting'}, 'divisions') self.schema.create_linkcollection_entry('division', division_id_2, 'managers', employee_id_1) affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'average_manager_age', self.employee_spec, employee_id_1) self.assertEquals([ self.schema.decodeid(division_id_1), self.schema.decodeid(division_id_2) ], list(affected_ids)) def test_reverse_aggregation_calc_through_calc(self): self.schema.add_calc(self.division_spec, 'older_employees', 'self.employees[age>30]') self.schema.add_calc(self.division_spec, 'older_employees_called_ned', 'self.older_employees[name="ned"]') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') division_id_2 = self.schema.insert_resource('division', {'name': 'marketting'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 21 }, 'employees', 'division', division_id_1) employee_id_2 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 31 }, 'employees', 'division', division_id_1) employee_id_3 = self.schema.insert_resource('employee', { 'name': 'fred', 'age': 41 }, 'employees', 'division', division_id_1) employee_id_4 = self.schema.insert_resource('employee', { 'name': 'sam', 'age': 25 }, 'employees', 'division', division_id_2) employee_id_5 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 35 }, 'employees', 'division', division_id_2) self.updater.update_calc('division', 'older_employees', division_id_1) self.updater.update_calc('division', 'older_employees_called_ned', division_id_1) self.updater.update_calc('division', 'older_employees', division_id_2) self.updater.update_calc('division', 'older_employees_called_ned', division_id_2) agg = self.updater.build_reverse_aggregations_to_calc( 'division', 'older_employees_called_ned', self.employee_spec, employee_id_2) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(employee_id_2) } }, { "$lookup": { "from": "resource_division", "foreignField": "older_employees", "localField": "_id", "as": "_field_older_employees", } }, { '$group': { '_id': '$_field_older_employees' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) # check affected ids affected_ids = self.updater.get_affected_ids_for_resource( 'division', 'older_employees_called_ned', self.employee_spec, employee_id_2) self.assertEquals([self.schema.decodeid(division_id_1)], list(affected_ids)) def test_reverse_aggregation_parent_link(self): self.schema.add_calc(self.employee_spec, 'division_name', 'self.parent_division_employees.name') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) self.updater.update_calc('employee', 'division_name', employee_id_1) agg = self.updater.build_reverse_aggregations_to_calc( 'employee', 'division_name', self.division_spec, division_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(division_id_1) } }, { "$lookup": { "from": "resource_employee", "foreignField": "_parent_id", "localField": "_id", "as": "_field_parent_division_employees", } }, { '$group': { '_id': '$_field_parent_division_employees' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) # check affected ids affected_ids = self.updater.get_affected_ids_for_resource( 'employee', 'division_name', self.division_spec, division_id_1) self.assertEquals([self.schema.decodeid(employee_id_1)], list(affected_ids)) def test_reverse_aggregation_reverse_link(self): self.schema.add_field(self.division_spec, 'manager', 'link', 'employee') self.schema.add_calc(self.employee_spec, 'divisions_i_manage', 'self.link_division_manager') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') division_id_2 = self.schema.insert_resource('division', {'name': 'marketting'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 31 }, 'employees', 'division', division_id_1) self.schema.update_resource_fields('division', division_id_1, {'manager': employee_id_1}) self.schema.update_resource_fields('division', division_id_2, {'manager': employee_id_1}) self.updater.update_calc('employee', 'divisions_i_manage', employee_id_1) agg = self.updater.build_reverse_aggregations_to_calc( 'employee', 'divisions_i_manage', self.division_spec, division_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(division_id_1) } }, { "$lookup": { "from": "resource_employee", "foreignField": "_id", "localField": "manager", "as": "_field_link_division_manager", } }, { '$group': { '_id': '$_field_link_division_manager' } }, { "$unwind": "$_id" }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) division_id_2 = self.schema.insert_resource('division', {'name': 'marketting'}, 'divisions') self.schema.create_linkcollection_entry('division', division_id_2, 'managers', employee_id_1) def test_reverse_aggregation_loopback(self): self.schema.add_field(self.division_spec, 'managers', 'linkcollection', 'employee') self.schema.add_calc(self.employee_spec, 'all_my_subordinates', 'self.link_division_managers.employees') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 21 }, 'employees', 'division', division_id_1) employee_id_2 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 31 }, 'employees', 'division', division_id_1) employee_id_3 = self.schema.insert_resource('employee', { 'name': 'fred', 'age': 41 }, 'employees', 'division', division_id_1) employee_id_4 = self.schema.insert_resource('employee', { 'name': 'mike', 'age': 51 }, 'employees', 'division', division_id_1) # add manager self.updater.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_1) # bobs addition alters bobs calc self.assertEquals([self.schema.decodeid(employee_id_1)], list( self.updater.get_affected_ids_for_resource( 'employee', 'all_my_subordinates', self.employee_spec, employee_id_1))) # a little unsure of this agg = self.updater.build_reverse_aggregations_to_calc( 'employee', 'all_my_subordinates', self.division_spec, division_id_1) self.assertEquals([[ { "$match": { "_id": self.schema.decodeid(division_id_1) } }, { '$lookup': { 'as': '_field_link_division_managers', 'foreignField': '_id', 'from': 'resource_employee', 'localField': 'managers._id' } }, { '$group': { '_id': '$_field_link_division_managers' } }, { '$unwind': '$_id' }, { "$replaceRoot": { "newRoot": "$_id" } }, ]], agg) def test_reverse_aggregation_simple_collection(self): self.schema.add_calc(self.division_spec, 'all_employees', 'self.employees') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'bob', 'age': 21 }) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'ned', 'age': 31 }) self.assertEquals([self.schema.decodeid(division_id_1)], list( self.updater.get_affected_ids_for_resource( 'division', 'all_employees', self.employee_spec, employee_id_1))) def test_reverse_aggregation_switch(self): self.schema.add_calc( self.division_spec, 'all_employees', 'self.name -> ("sales": (self.employees[age > 25]), "marketting": self.employees)' ) division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'bob', 'age': 21 }) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'ned', 'age': 31 }) self.assertEquals({self.schema.decodeid(division_id_1)}, set( self.updater.get_affected_ids_for_resource( 'division', 'all_employees', self.employee_spec, employee_id_1))) def test_reverse_aggregation_ternary(self): self.schema.add_calc( self.division_spec, 'all_employees', 'self.name = "sales" -> (self.employees[age > 25]) : self.employees' ) division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'bob', 'age': 21 }) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'ned', 'age': 31 }) self.assertEquals({self.schema.decodeid(division_id_1)}, set( self.updater.get_affected_ids_for_resource( 'division', 'all_employees', self.employee_spec, employee_id_1))) def test_delete_resource_deletes_children(self): division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'bob', 'age': 21 }) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'ned', 'age': 31 }) self.assertEqual(1, self.db['resource_division'].count()) self.assertEqual(2, self.db['resource_employee'].count()) self.updater.delete_resource('division', division_id_1, None, 'divisions') self.assertEqual(0, self.db['resource_division'].count()) self.assertEqual(0, self.db['resource_employee'].count()) def test_delete_resource_deletes_links_to_resource(self): self.schema.add_field(self.division_spec, 'employees', 'linkcollection', 'employee') self.schema.add_field(self.division_spec, 'manager', 'link', 'employee') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') employee_id_1 = self.schema.insert_resource('employee', {'name': 'Fred'}, 'employees') self.schema.create_linkcollection_entry('division', division_id_1, 'employees', employee_id_1) self.schema.update_resource_fields('division', division_id_1, {'manager': employee_id_1}) self.updater.delete_resource('employee', employee_id_1, 'root', 'employees') self.assertEqual( { '_canonical_url': '/divisions/%s' % division_id_1, '_canonical_url_manager': '/employees/%s' % employee_id_1, '_grants': [], '_id': self.schema.decodeid(division_id_1), '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'employees': [], 'manager': None, 'name': 'sales' }, self.db['resource_division'].find_one()) def test_updates_calc_linked_to_calc(self): self.schema.create_field('root', 'parttimers', 'collection', 'employee') self.schema.create_field('employee', 'income', 'int') self.schema.create_field('employee', 'vat', 'int') self.schema.create_field('employee', 'income_after_vat', 'calc', calc_str='self.income - self.vat') self.schema.create_field('division', 'parttimers', 'linkcollection', 'employee') self.schema.create_field( 'division', 'employee_total', 'calc', calc_str="sum(self.employees.income_after_vat)") self.schema.create_field( 'division', 'parttime_total', 'calc', calc_str="sum(self.parttimers.income_after_vat)") division_id_1 = self.updater.create_resource('division', 'root', 'divisions', None, {'name': 'sales'}) employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Fred', 'income': 10000, 'vat': 2000 }) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Ned', 'income': 20000, 'vat': 4000 }) employee_id_3 = self.updater.create_resource('employee', 'root', 'parttimers', None, { 'name': 'Bob', 'income': 40000, 'vat': 8000 }) self.updater.create_linkcollection_entry('division', division_id_1, 'parttimers', employee_id_3) self.assertEqual( 24000, self.db['resource_division'].find_one()['employee_total']) self.assertEqual( 32000, self.db['resource_division'].find_one()['parttime_total']) # assert calc change propagates self.updater.update_fields('employee', employee_id_3, {'vat': 9000}) self.assertEqual( 24000, self.db['resource_division'].find_one()['employee_total']) self.assertEqual( 31000, self.db['resource_division'].find_one()['parttime_total']) def test_update_adjacent_calc_after_update(self): self.schema.create_field( 'employee', 'division_name', 'calc', calc_str='self.parent_division_employees.name') self.schema.create_field('employee', 'both_names', 'calc', calc_str='self.name + self.division_name') division_id_1 = self.updater.create_resource('division', 'root', 'divisions', None, {'name': 'sales'}) employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, {'name': 'Fred'}) self.assertEqual('Fredsales', self.db['resource_employee'].find_one()['both_names']) self.updater.update_fields('division', division_id_1, {'name': 'marketting'}) self.assertEqual('Fredmarketting', self.db['resource_employee'].find_one()['both_names'])