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.updater = Updater(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.employee_spec, 'age', 'int') self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.division_spec, 'name', 'str') self.schema.add_field(self.division_spec, 'employees', 'collection', 'employee') self.schema.add_field(self.schema.root, 'divisions', 'collection', 'division') def test_update_simple_field(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': [], 'name': 'sales', '_canonical_url': '/divisions/%s' % division_id_1, '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'older_employees': [ObjectId(employee_id_1[2:])], }, division_data) self.updater.update_fields('employee', employee_id_1, {"age": 20}) division_data = self.db.resource_division.find_one() self.assertEquals({ '_id': self.schema.decodeid(division_id_1), '_grants': [], 'name': 'sales', '_canonical_url': '/divisions/%s' % division_id_1, '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'older_employees': [], }, division_data) def test_update_containing_collection(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') 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': [], }, division_data) employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Bob', 'age': 41, }) 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_data = self.db.resource_employee.find_one() self.assertEquals({ '_id': self.schema.decodeid(employee_id_1), '_grants': [], '_canonical_url': '/divisions/%s/employees/%s' % (division_id_1, employee_id_1), 'name': 'Bob', 'age': 41, '_parent_canonical_url': '/divisions/%s' % division_id_1, '_parent_field_name': 'employees', '_parent_id': ObjectId(division_id_1[2:]), '_parent_type': 'division', }, employee_data) def test_update_link_collection(self): self.schema.add_field(self.division_spec, 'managers', 'linkcollection', 'employee') self.schema.add_calc(self.division_spec, 'older_managers', 'self.managers[age>30]') self.schema.add_calc(self.division_spec, 'older_non_retired_managers', 'self.older_managers[age<65]') log.debug("start") division_id_1 = self.schema.insert_resource( 'division', {'name': 'sales'}, 'divisions') log.debug("inserted") employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Bob', 'age': 41 }) log.debug("created 1") employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Ned', 'age': 70 }) log.debug("created 2") employee_id_3 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'Fred', 'age': 25 }) log.debug("created 3") self.updater.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_1) log.debug("created entry 1") self.updater.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_2) log.debug("created entry 2") self.updater.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_3) log.debug("created entry 3") division_data = self.db.resource_division.find_one() self.assertEquals("sales", division_data['name']) self.assertEquals(3, len(division_data['managers'])) self.assertTrue({"_id" : self.schema.decodeid(employee_id_1)} in division_data['managers']) self.assertTrue({"_id" : self.schema.decodeid(employee_id_2)} in division_data['managers']) self.assertTrue({"_id" : self.schema.decodeid(employee_id_3)} in division_data['managers']) self.assertEquals(sorted([ self.schema.decodeid(employee_id_1), self.schema.decodeid(employee_id_2), ]), sorted(division_data['older_managers'])) self.assertEquals([ self.schema.decodeid(employee_id_1)], division_data['older_non_retired_managers']) self.assertEquals({ "_id" : self.schema.decodeid(division_id_1), '_grants': [], '_canonical_url': '/divisions/%s' % division_id_1, "_parent_field_name" : "divisions", "_parent_id" : None, "_parent_type" : "root", "_parent_canonical_url" : '/', "name" : "sales", "managers" : [ { "_id" : self.schema.decodeid(employee_id_1) }, { "_id" : self.schema.decodeid(employee_id_2) }, { "_id" : self.schema.decodeid(employee_id_3) } ], "older_managers" : [ self.schema.decodeid(employee_id_1), self.schema.decodeid(employee_id_2), ], "older_non_retired_managers" : [ self.schema.decodeid(employee_id_1), ] }, division_data) 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.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}) employee_id_3 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'fred', 'age': 41}) employee_id_4 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, { 'name': 'mike', 'age': 51}) # add manager calc_spec = self.schema.calc_trees[('employee', 'all_my_subordinates')] self.assertEquals({'division.managers', 'division.employees'}, calc_spec.get_resource_dependencies()) self.updater.create_linkcollection_entry('division', division_id_1, 'managers', employee_id_1) employee_data = self.db.resource_employee.find_one() self.assertEquals({ "_id" : self.schema.decodeid(employee_id_1), '_grants': [], '_canonical_url': '/divisions/%s/employees/%s' % (division_id_1, employee_id_1), "_parent_field_name" : "employees", "_parent_id" : self.schema.decodeid(division_id_1), "_parent_type" : "division", "_parent_canonical_url" : "/divisions/%s" % division_id_1, "name" : "bob", "age": 21, "all_my_subordinates" : [ self.schema.decodeid(employee_id_1), self.schema.decodeid(employee_id_2), self.schema.decodeid(employee_id_3), self.schema.decodeid(employee_id_4), ]}, employee_data) def test_resource_deps_for_field(self): self.schema.add_calc(self.employee_spec, 'all_ages', 'divisions.employees.age + 10') # add manager calc_spec = self.schema.calc_trees[('employee', 'all_ages')] self.assertEquals({'division.employees', 'root.divisions', 'employee.age'}, calc_spec.get_resource_dependencies())
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.updater = Updater(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.schema.root, 'employees', 'collection', 'employee') self.api = Api(self.schema) self.db.delete_calc.create_index([ ('update_id', pymongo.ASCENDING), ('resource_id', pymongo.ASCENDING), ], unique=True) def test_aggregation_merge(self): employee_id_1 = self.api.post('employees', {'name': 'ned'}) employee_id_2 = self.api.post('employees', {'name': 'bob'}) cursor = self.db.resource_employee.aggregate([ { "$set": { "update_id": 1 } }, { "$project": { "resource_id": "$_id", "update_id": True, "_id": False } }, { "$merge": { "into": "delete_calc", "on": ["update_id", "resource_id"], "whenNotMatched": "insert" } }, ]) cursor = self.db.resource_employee.aggregate([ { "$set": { "update_id": 2 } }, { "$project": { "resource_id": "$_id", "update_id": True, "_id": False } }, { "$merge": { "into": "delete_calc", "on": ["update_id", "resource_id"], "whenNotMatched": "insert" } }, ]) employee_id_3 = self.api.post('employees', {'name': 'fred'}) cursor = self.db.resource_employee.aggregate([ { "$set": { "update_id": 2 } }, { "$project": { "resource_id": "$_id", "update_id": True, "_id": False } }, { "$merge": { "into": "delete_calc", "on": ["update_id", "resource_id"], "whenNotMatched": "insert" } }, ]) self.assertEqual([], list(cursor))
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 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.updater = Updater(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.employee_spec, 'age', 'int') self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.division_spec, 'name', 'str') self.schema.add_field(self.division_spec, 'employees', 'linkcollection', 'employee') self.schema.add_calc(self.division_spec, 'older_employees', 'self.employees[age>30]') self.schema.add_field(self.schema.root, 'divisions', 'collection', 'division') self.schema.add_field(self.schema.root, 'employees', 'collection', 'employee') def test_update_only_linked_resources(self): employee_id_1 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 10 }, 'employees') employee_id_2 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 10 }, 'employees') division_id_1 = self.schema.insert_resource('division', {'name': 'sales'}, 'divisions') division_id_2 = self.schema.insert_resource('division', {'name': 'marketting'}, 'divisions') self.schema.create_linkcollection_entry('division', division_id_1, 'employees', employee_id_1) self.schema.create_linkcollection_entry('division', division_id_2, 'employees', employee_id_1) self.schema.create_linkcollection_entry('division', division_id_2, 'employees', employee_id_2) self.assertEqual([ self.schema.decodeid(division_id_1), self.schema.decodeid(division_id_2) ], self.updater.get_affected_ids_for_resource( 'division', 'older_employees', self.employee_spec, employee_id_1)) self.assertEqual([self.schema.decodeid(division_id_2)], self.updater.get_affected_ids_for_resource( 'division', 'older_employees', self.employee_spec, employee_id_2))
class MoveResourceTest(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.updater = Updater(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.employee_spec, 'age', 'int') self.schema.add_field(self.schema.root, 'current_employees', 'collection', 'employee') self.schema.add_field(self.schema.root, 'former_employees', 'collection', 'employee') self.calcs_spec = self.schema.add_spec('calcs') def test_move_from_root(self): self.schema.add_calc(self.calcs_spec, 'sum_employee_age', 'sum(current_employees.age)') self.schema.add_field(self.schema.root, 'calcs', 'collection', 'calcs') # add root resources employee_id_1 = self.schema.insert_resource('employee', {'name': 'Bob', 'age': 10}, 'current_employees') employee_id_2 = self.schema.insert_resource('employee', {'name': 'Ned', 'age': 14}, 'current_employees') calcs_id_1 = self.schema.insert_resource('calcs', {}, 'calcs') # create move update self.move_resource = MoveResourceUpdate( self.updater, self.schema, None, 'root', 'root', 'former_employees', 'current_employees') from_path_agg, from_path_spec, from_path_is_coll = self.move_resource.from_path_agg() self.assertEqual([ {'$match': { '$and': [ {'_parent_field_name': 'current_employees'}, {'_parent_canonical_url': '/'} ] }}, ], from_path_agg) self.assertEqual(self.schema.specs['employee'], from_path_spec) self.assertTrue(from_path_is_coll) affected_ids_agg_before = self.move_resource.affected_aggs() self.assertEqual([ ('calcs', 'sum_employee_age', [ {'$lookup': {'as': '_field_sum_employee_age', 'from': 'resource_calcs', 'pipeline': []}}, {'$group': {'_id': '$_field_sum_employee_age'}}, {'$unwind': '$_id'}, {'$replaceRoot': {'newRoot': '$_id'}}]) ], affected_ids_agg_before) # check affected agg to path affected_ids_agg_after = self.move_resource.affected_aggs_to_path() self.assertEqual([ ], affected_ids_agg_after) # check affected ids self.assertEqual(set([ ('calcs', 'sum_employee_age', self.schema.decodeid(calcs_id_1)), ]), self.move_resource.affected_ids()) # check affected ids for to path self.assertEqual(set([ ]), self.move_resource.affected_ids_to_path()) def test_move_from_root_after_aggs(self): self.schema.add_calc(self.calcs_spec, 'sum_employee_age', 'sum(former_employees.age)') self.schema.add_field(self.schema.root, 'calcs', 'collection', 'calcs') # add root resources employee_id_1 = self.schema.insert_resource('employee', {'name': 'Bob', 'age': 10}, 'current_employees') employee_id_2 = self.schema.insert_resource('employee', {'name': 'Ned', 'age': 14}, 'current_employees') calcs_id_1 = self.schema.insert_resource('calcs', {}, 'calcs') # create move update self.move_resource = MoveResourceUpdate( self.updater, self.schema, None, 'root', 'former_employees', 'former_employees', 'current_employees') from_path_agg, from_path_spec, from_path_is_coll = self.move_resource.from_path_agg() self.assertEqual([ {'$match': { '$and': [ {'_parent_field_name': 'current_employees'}, {'_parent_canonical_url': '/'} ] }}, ], from_path_agg) self.assertEqual(self.schema.specs['employee'], from_path_spec) self.assertTrue(from_path_is_coll) affected_ids_agg_before = self.move_resource.affected_aggs() self.assertEqual([ ], affected_ids_agg_before) # check affected agg to path affected_ids_agg_after = self.move_resource.affected_aggs_to_path() self.assertEqual([ ('calcs', 'sum_employee_age', [{'$lookup': {'as': '_field_sum_employee_age', 'from': 'resource_calcs', 'pipeline': []}}, {'$group': {'_id': '$_field_sum_employee_age'}}, {'$unwind': '$_id'}, {'$replaceRoot': {'newRoot': '$_id'}}])], affected_ids_agg_after) # check affected ids self.assertEqual(set([ ]), self.move_resource.affected_ids()) # need to perform the move before the after ids will show up self.move_resource.perform_move() # check affected ids for to path self.assertEqual(set([ ('calcs', 'sum_employee_age', self.schema.decodeid(calcs_id_1)), ]), self.move_resource.affected_ids_to_path()) # check canonical_url employee = self.db['resource_employee'].find_one({'_id': self.schema.decodeid(employee_id_1 )}) self.assertEqual('/', employee['_parent_canonical_url']) self.assertEqual('former_employees', employee['_parent_field_name']) def test_move_from_root_more_resources(self): self.schema.add_calc(self.calcs_spec, 'sum_employee_age', 'sum(current_employees.age)') self.schema.add_field(self.schema.root, 'calcs', 'collection', 'calcs') # add root resources employee_id_1 = self.schema.insert_resource('employee', {'name': 'Bob', 'age': 10}, 'current_employees') employee_id_2 = self.schema.insert_resource('employee', {'name': 'Ned', 'age': 14}, 'current_employees') calcs_id_1 = self.schema.insert_resource('calcs', {}, 'calcs') calcs_id_2 = self.schema.insert_resource('calcs', {}, 'calcs') # create move update self.move_resource = MoveResourceUpdate( self.updater, self.schema, None, 'root', 'root', 'former_employees', 'current_employees') # check affected ids self.assertEqual(set([ ('calcs', 'sum_employee_age', self.schema.decodeid(calcs_id_1)), ('calcs', 'sum_employee_age', self.schema.decodeid(calcs_id_2)), ]), self.move_resource.affected_ids()) def test_move_from_child_collection(self): self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.division_spec, 'name', 'str') self.schema.add_field(self.schema.root, 'divisions', 'collection', 'division') self.schema.add_field(self.schema.root, 'calcs', 'collection', 'calcs') self.schema.add_field(self.division_spec, 'employees', 'collection', 'employee') self.schema.add_field(self.calcs_spec, 'division', 'link', 'division') self.schema.add_calc(self.calcs_spec, 'sum_division_employee_age', 'sum(self.division.employees.age)') # add root resources 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': 10}, 'employees', 'division', division_id_1) employee_id_2 = self.schema.insert_resource('employee', {'name': 'Ned', 'age': 14}, 'employees', 'division', division_id_1) employee_id_3 = self.schema.insert_resource('employee', {'name': 'Fred', 'age': 16}, 'employees', 'division', division_id_2) calcs_id_1 = self.schema.insert_resource('calcs', {"division": division_id_1}, 'calcs') calcs_id_2 = self.schema.insert_resource('calcs', {"division": division_id_2}, 'calcs') # create move update self.move_resource = MoveResourceUpdate( self.updater, self.schema, division_id_2, 'division', 'employees', 'divisions/%s/employees' % division_id_2, 'divisions/%s/employees' % division_id_1) # check from agg from_path_agg, from_path_spec, from_path_is_coll = self.move_resource.from_path_agg() self.assertEqual([ {'$match': {'$and': [{'_parent_field_name': 'divisions'}, {'_parent_canonical_url': '/'}]}}, {'$match': {'_id': self.schema.decodeid(division_id_1)}}, {'$lookup': {'as': '_field_employees', 'foreignField': '_parent_id', 'from': 'resource_employee', 'localField': '_id'}}, {'$group': {'_id': '$_field_employees'}}, {'$unwind': '$_id'}, {'$replaceRoot': {'newRoot': '$_id'}}], from_path_agg) self.assertEqual(self.schema.specs['employee'], from_path_spec) self.assertTrue(from_path_is_coll) # check affected ids agg affected_ids_agg_before = self.move_resource.affected_aggs() self.assertEqual([ ('calcs', 'sum_division_employee_age', [ # lookup to division {'$lookup': {'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id'}}, {'$group': {'_id': '$_field_employees'}}, {'$unwind': '$_id'}, {'$replaceRoot': {'newRoot': '$_id'}}, # lookup to calcs {'$lookup': {'as': '_field_division', 'foreignField': 'division', 'from': 'resource_calcs', 'localField': '_id'}}, {'$group': {'_id': '$_field_division'}}, {'$unwind': '$_id'}, {'$replaceRoot': {'newRoot': '$_id'}}])], affected_ids_agg_before) # check affected ids self.assertEqual(set([ ('calcs', 'sum_division_employee_age', self.schema.decodeid(calcs_id_1)), ]), self.move_resource.affected_ids())
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.updater = Updater(self.schema) self.schema.create_initial_schema() self.company_spec = self.schema.add_spec('company') self.employee_spec = self.schema.add_spec('employee') self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.company_spec, 'divisions', 'collection', 'division') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.division_spec, 'employees', 'collection', 'employee') self.schema.add_field(self.schema.root, 'companies', 'collection', 'company') def test_delete_group(self): self.admin_group_id = self.updater.create_resource('group', 'root', 'groups', None, {'name': 'admin'}, self.schema.read_root_grants('groups')) self.grant_id = self.updater.create_resource('grant', 'group', 'grants', self.admin_group_id, {'type': 'read', 'url': '/companies'}) self.user_id = self.updater.create_resource('user', 'root', 'users', None, {'username': '******', 'password': '******', 'admin': True}, self.schema.read_root_grants('users')) self.updater.create_linkcollection_entry('user', self.user_id, 'groups', self.admin_group_id) user_db = self.db['resource_user'].find_one() self.assertEqual(1, len(user_db['read_grants'])) self.updater.delete_linkcollection_entry('user', self.schema.decodeid(self.user_id), 'groups', self.admin_group_id) user_db = self.db['resource_user'].find_one() self.assertEqual(0, len(user_db['read_grants'])) def test_root_grants(self): group_id = self.updater.create_resource('group', 'root', 'groups', None, {'name': 'readall'}, self.schema.read_root_grants('groups')) grant_id = self.updater.create_resource('grant', 'group', 'grants', group_id, {'type': 'read', 'url': '/'}) company_id = self.updater.create_resource('company', 'root', 'companies', None, {}, self.schema.read_root_grants('companies')) company_data = self.db['resource_company'].find_one({}) self.assertEqual([self.schema.decodeid(grant_id)], company_data['_grants']) def test_nested_grants(self): group_id = self.updater.create_resource('group', 'root', 'groups', None, {'name': 'readall'}, self.schema.read_root_grants('groups')) grant_id = self.updater.create_resource('grant', 'group', 'grants', group_id, {'type': 'read', 'url': '/'}) company_id = self.updater.create_resource('company', 'root', 'companies', None, {}, self.schema.read_root_grants('companies')) company_path = "companies/%s" % company_id division_id_1 = self.updater.create_resource('division', 'company', 'divisions', company_id, {}, self.schema.read_root_grants(company_path)) division_id_2 = self.updater.create_resource('division', 'company', 'divisions', company_id, {}, self.schema.read_root_grants(company_path)) division_1_path = "companies/%s/divisions/%s" % (company_id, division_id_1) employee_id_1 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, {}, self.schema.read_root_grants(division_1_path)) employee_id_2 = self.updater.create_resource('employee', 'division', 'employees', division_id_1, {}, self.schema.read_root_grants(division_1_path)) division_2_path = "companies/%s/divisions/%s" % (company_id, division_id_2) employee_id_3 = self.updater.create_resource('employee', 'division', 'employees', division_id_2, {}, self.schema.read_root_grants(division_2_path)) employee_id_4 = self.updater.create_resource('employee', 'division', 'employees', division_id_2, {}, self.schema.read_root_grants(division_2_path)) # check grants company_data = self.db['resource_company'].find_one({}) self.assertEqual([self.schema.decodeid(grant_id)], company_data['_grants']) division_1_data = self.db['resource_division'].find_one({"_id": self.schema.decodeid(division_id_1)}) self.assertEqual([self.schema.decodeid(grant_id)], division_1_data['_grants']) division_2_data = self.db['resource_division'].find_one({"_id": self.schema.decodeid(division_id_2)}) self.assertEqual([self.schema.decodeid(grant_id)], division_2_data['_grants']) employee_1_data = self.db['resource_employee'].find_one({"_id": self.schema.decodeid(employee_id_1)}) self.assertEqual([self.schema.decodeid(grant_id)], employee_1_data['_grants']) employee_2_data = self.db['resource_employee'].find_one({"_id": self.schema.decodeid(employee_id_2)}) self.assertEqual([self.schema.decodeid(grant_id)], employee_2_data['_grants']) employee_3_data = self.db['resource_employee'].find_one({"_id": self.schema.decodeid(employee_id_3)}) self.assertEqual([self.schema.decodeid(grant_id)], employee_3_data['_grants']) employee_4_data = self.db['resource_employee'].find_one({"_id": self.schema.decodeid(employee_id_4)}) self.assertEqual([self.schema.decodeid(grant_id)], employee_4_data['_grants']) def test_deleting_grant_removes_grant_id(self): group_id = self.updater.create_resource('group', 'root', 'groups', None, {'name': 'readall'}, self.schema.read_root_grants('groups')) grant_id = self.updater.create_resource('grant', 'group', 'grants', group_id, {'type': 'read', 'url': '/'}) company_id = self.updater.create_resource('company', 'root', 'companies', None, {}, self.schema.read_root_grants('companies')) self.updater.delete_resource('grant', grant_id, 'group', 'grants') company_data = self.db['resource_company'].find_one({}) self.assertEqual([], company_data['_grants'])
class UpdaterBackgroundTest(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.api = Api(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.employee_spec, 'age', 'int') self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.division_spec, 'name', 'str') self.schema.add_field(self.division_spec, 'employees', 'collection', 'employee') self.schema.add_field(self.division_spec, 'managers', 'linkcollection', 'employee') self.schema.add_calc(self.division_spec, 'older_managers', 'self.managers[age>30]') self.company_spec = self.schema.add_spec('company') self.schema.add_field(self.company_spec, 'name', 'str') self.schema.add_field(self.company_spec, 'division', 'link', 'division') self.schema.add_calc(self.company_spec, 'max_age', 'max(self.division.older_managers.age)') self.schema.add_field(self.schema.root, 'companies', 'collection', 'company') self.schema.add_field(self.schema.root, 'divisions', 'collection', 'division') def test_update(self): company_1_id = self.api.post('/companies', {'name': 'Bobs Burgers'}) division_1_id = self.api.post('/divisions', {'name': 'Kitchen'}) employee_1_id = self.api.post( '/divisions/%s/employees' % division_1_id, { 'name': 'Bob', 'age': 38 }) employee_2_id = self.api.post( '/divisions/%s/employees' % division_1_id, { 'name': 'Linda', 'age': 36 }) self.api.post('/divisions/%s/managers' % division_1_id, {'id': employee_1_id}) self.api.post('/divisions/%s/managers' % division_1_id, {'id': employee_2_id}) self.assertEquals( None, self.api.get('/companies/%s' % company_1_id)['max_age']) self.api.patch('/companies/%s' % company_1_id, {'division': division_1_id}) self.assertEquals( 38, self.api.get('/companies/%s' % company_1_id)['max_age'])
class AggregatorTest(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.updater = Updater(self.schema) self.employee_spec = self.schema.add_spec('employee') self.schema.add_field(self.employee_spec, 'name', 'str') self.schema.add_field(self.employee_spec, 'age', 'int') self.section_spec = self.schema.add_spec('section') self.schema.add_field(self.section_spec, 'name', 'str') self.schema.add_field(self.section_spec, 'members', 'linkcollection', 'employee') self.division_spec = self.schema.add_spec('division') self.schema.add_field(self.division_spec, 'name', 'str') self.schema.add_field(self.division_spec, 'employees', 'collection', 'employee') self.schema.add_field(self.division_spec, 'sections', 'collection', 'section') self.schema.add_field(self.schema.root, 'divisions', 'collection', 'division') self.aggregator = ReverseAggregator(self.schema) def test_simple(self): tree = parse('self.employees.age', self.schema.specs['division']) employee_id = ObjectId() aggregations = self.aggregator.get_for_resource( tree, 'employee', employee_id, 'division', 'all_employees_age') self.assertEqual([[{ '$match': { '_id': employee_id } }, { '$lookup': { 'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }]], aggregations) def test_middle_of_calc(self): tree = parse('divisions.sections.members.age', self.schema.specs['division']) section_id = ObjectId() aggregations = self.aggregator.get_for_resource( tree, 'section', section_id, 'division', 'all_ages') self.assertEqual([[{ '$match': { '_id': section_id } }, { '$lookup': { 'as': '_field_sections', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_sections' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }], [{ '$lookup': { 'as': '_field_all_ages', 'from': 'resource_division', 'pipeline': [] } }, { '$group': { '_id': '$_field_all_ages' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }]], aggregations) def test_double(self): tree = parse( 'self.employees.parent_division_employees.sections.members.age', self.schema.specs['division']) employee_id = ObjectId() aggregations = self.aggregator.get_for_resource( tree, 'employee', employee_id) self.assertEqual([ [ { '$match': { '_id': employee_id } }, { '$lookup': { 'as': '_field_members', 'foreignField': 'members._id', 'from': 'resource_section', 'localField': '_id' } }, { '$group': { '_id': '$_field_members' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, { '$lookup': { 'as': '_field_sections', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_sections' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, { '$lookup': { 'as': '_field_parent_division_employees', 'foreignField': '_parent_id', 'from': 'resource_employee', 'localField': '_id' } }, { '$group': { '_id': '$_field_parent_division_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, { '$lookup': { 'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, ], [ { '$match': { '_id': employee_id } }, { '$lookup': { 'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, ], ], aggregations) def test_calc(self): tree = parse( 'max(self.employees.age) + min(divisions.employees.age) + 10', self.schema.specs['division']) employee_id = ObjectId() aggregations = self.aggregator.get_for_resource( tree, 'employee', employee_id, 'section', 'age_calc') self.assertEqual( [ [ { '$match': { '_id': employee_id } }, { '$lookup': { 'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, ], # TODO: if two of the aggregations are the same, remove the second one: [ { '$match': { '_id': employee_id } }, { '$lookup': { 'as': '_field_employees', 'foreignField': '_id', 'from': 'resource_division', 'localField': '_parent_id' } }, { '$group': { '_id': '$_field_employees' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, ], [ { '$lookup': { 'as': '_field_age_calc', 'from': 'resource_section', 'pipeline': [] } }, { '$group': { '_id': '$_field_age_calc' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }, ] ], aggregations) def test_double_aggregate(self): division_id_1 = self.schema.insert_resource('division', {'name': 'Sales'}, 'divisions') section_id_1 = self.schema.insert_resource('section', {'name': 'Sales'}, 'sections', 'division', division_id_1) employee_id_1 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 10 }, 'employees', 'division', division_id_1) employee_id_2 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 10 }, 'employees', 'division', division_id_1) self.schema.create_linkcollection_entry('section', section_id_1, 'members', employee_id_1) tree = parse( 'self.employees.parent_division_employees.sections.members.age', self.schema.specs['division']) aggregations = self.aggregator.get_for_resource( tree, 'employee', self.schema.decodeid(employee_id_1)) result = self.schema.db['resource_employee'].aggregate(aggregations[0]) self.assertEqual( { '_canonical_url': '/divisions/%s' % division_id_1, '_grants': [], '_id': self.schema.decodeid(division_id_1), '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'name': 'Sales' }, next(result)) def test_ternary_aggregate(self): division_id_1 = self.schema.insert_resource('division', {'name': 'Sales'}, 'divisions') section_id_1 = self.schema.insert_resource('section', {'name': 'Sales'}, 'sections', 'division', division_id_1) employee_id_1 = self.schema.insert_resource('employee', { 'name': 'ned', 'age': 10 }, 'employees', 'division', division_id_1) employee_id_2 = self.schema.insert_resource('employee', { 'name': 'bob', 'age': 10 }, 'employees', 'division', division_id_1) self.schema.create_linkcollection_entry('section', section_id_1, 'members', employee_id_1) tree = parse( 'max(self.employees.age) > 20 -> max(self.employees.age) : min(divisions.sections.members.age)', self.schema.specs['division']) aggregations = self.aggregator.get_for_resource( tree, 'employee', self.schema.decodeid(employee_id_1)) result = self.schema.db['resource_employee'].aggregate(aggregations[0]) self.assertEqual( { '_canonical_url': '/divisions/%s' % division_id_1, '_grants': [], '_id': self.schema.decodeid(division_id_1), '_parent_canonical_url': '/', '_parent_field_name': 'divisions', '_parent_id': None, '_parent_type': 'root', 'name': 'Sales' }, next(result)) def test_simple_root(self): # uncertain what case this is testing exactly - section has no link to division division_id_1 = self.schema.insert_resource('division', {'name': 'Sales'}, 'divisions') tree = parse('max(divisions.name)', self.schema.root) aggregations = self.aggregator.get_for_resource( tree, 'division', self.schema.decodeid(division_id_1), 'division', 'max_divisions_name') self.assertEqual([[{ '$lookup': { 'as': '_field_max_divisions_name', 'from': 'resource_division', 'pipeline': [] } }, { '$group': { '_id': '$_field_max_divisions_name' } }, { '$unwind': '$_id' }, { '$replaceRoot': { 'newRoot': '$_id' } }]], aggregations)