def setUp(self): """ setup any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) self.test_subject = StudentManagementPlugin(123,456,None,args_string) self.test_subject.logger = MagicMock() self.test_subject.send_log_entry = MagicMock()
def setUp(self): """ setup any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) self.test_subject = StudentManagementPlugin(123,456,None,args_string) #self.test_subject.cache = Couchbase.connect(bucket = "test_student_model_cache", host = settings.COUCHBASE_HOSTNAME) self.test_subject.logger = MagicMock() self.test_subject.send_log_entry = MagicMock()
def test_constructor(self): """ StudentManagementPlugin.__init__() Test plan: -ensure name, logger set as parameters -ensure that mongo is an instance of MongoClient -ensure that db is an instance of Collection -ensure that the full name is hpit.hpit_students """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) smp = StudentManagementPlugin(123,456,None,args_string) isinstance(smp.mongo,MongoClient).should.equal(True) isinstance(smp.db,Collection).should.equal(True) smp.db.full_name.should.equal("hpit_unit_test_db.hpit_students")
class TestStudentManagementPlugin(unittest.TestCase): @classmethod def setUpClass(cls): """ options = { "authType":"sasl", "saslPassword":"", "bucketType":"memcached", "flushEnabled":1, "name":"test_student_model_cache", "ramQuotaMB":100, } req = requests.post(settings.COUCHBASE_BUCKET_URI,auth=settings.COUCHBASE_AUTH, data = options) """ @classmethod def tearDownClass(cls): """ res = requests.delete(settings.COUCHBASE_BUCKET_URI + "/test_student_model_cache",auth=settings.COUCHBASE_AUTH) if res.status_code != 200 and res.status_code != 404: if '_' not in res.json() or res.json()['_'] != 'Bucket deletion not yet complete, but will continue.\r\n': raise Exception("Failure to delete bucket") """ def setUp(self): """ setup any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) self.test_subject = StudentManagementPlugin(123,456,None,args_string) #self.test_subject.cache = Couchbase.connect(bucket = "test_student_model_cache", host = settings.COUCHBASE_HOSTNAME) self.test_subject.logger = MagicMock() self.test_subject.send_log_entry = MagicMock() def tearDown(self): """ teardown any state that was previously setup with a setup_method call. """ client = MongoClient() client.drop_database(settings.MONGO_DBNAME) self.test_subject = None def test_constructor(self): """ StudentManagementPlugin.__init__() Test plan: -ensure name, logger set as parameters -ensure that mongo is an instance of MongoClient -ensure that db is an instance of Collection -ensure that the full name is hpit.hpit_students """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) smp = StudentManagementPlugin(123,456,None,args_string) isinstance(smp.mongo,MongoClient).should.equal(True) isinstance(smp.db,Collection).should.equal(True) smp.db.full_name.should.equal("hpit_unit_test_db.hpit_students") def test_add_student_callback(self): """ StudentManagementPlugin.add_student_callback() Test plan: -Mock logger, ensure written to when called -Send message without attributes, attributes should be empty in db -Send in attributes, should be present in database -Should be two distinc messages now -mock response, should have a call with the message id and student id """ test_message = {"message_id":"2","sender_entity_id":"3"} calls = [call("ADD_STUDENT"),call(test_message)] self.test_subject.send_response = MagicMock() self.test_subject._post_data = MagicMock(return_value=requests.Response()) requests.Response.json = MagicMock(return_value={"resource_id":"456"}) self.test_subject.add_student_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) client = MongoClient() result = client[settings.MONGO_DBNAME].hpit_students.find({}) result.count().should.equal(1) result[0]["attributes"].should.equal({}) session = client[settings.MONGO_DBNAME].hpit_sessions.find_one({"student_id":str(result[0]["_id"])}) self.test_subject.send_response.assert_called_with("2",{"student_id":str(result[0]["_id"]),"attributes":{},"resource_id":"456","session_id":str(session["_id"])}) self.test_subject.send_response.reset_mock() test_message = {"message_id":"2","attributes":{"attr":"value"},"sender_entity_id":"3"} self.test_subject.add_student_callback(test_message) result = client[settings.MONGO_DBNAME].hpit_students.find({}) result.count().should.equal(2) result[1]["attributes"].should.equal({"attr":"value"}) session = client[settings.MONGO_DBNAME].hpit_sessions.find_one({"student_id":str(result[1]["_id"])}) self.test_subject.send_response.assert_called_with("2",{"student_id":str(result[1]["_id"]),"attributes":{"attr":"value"},"resource_id":"456","session_id":str(session["_id"])}) def test_get_student_callback(self): """ StudentManagementPlugin.get_student_callback() Test plan: - Mock logger, ensure written to when called - mock response - send message without student id, response should contain error - with no student, should return an error - add a student - response should have call with student_id, and attributes """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("GET_STUDENT"),call(test_message)] self.test_subject.send_response = MagicMock() self.test_subject.get_student_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a valid 'student_id' to get a student"}) self.test_subject.send_response.reset_mock() #get non existing student bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"sender_entity_id":"123"} self.test_subject.get_student_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #try with a good student sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"4"}) test_message = {"message_id":"2","student_id":sid,"sender_entity_id":"123"} self.test_subject.get_student_callback(test_message) session = self.test_subject.session_db.find_one({"student_id":str(sid)}) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"resource_id":"4","attributes":{"key":"value"},"session_id":str(session["_id"])}) self.test_subject.send_response.reset_mock() def test_set_attribute_callback(self): """ StudentManagementPlugin.set_attribute_callback() Test plan: - Mock logger, ensure written to when called - Try lacking id, name, and value; response should have an error - Get an attribute with a bum ID; should send back error - Send in a real OID, should have real response. Attribute should be changed in response and in db """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("SET_ATTRIBUTE"),call(test_message)] self.test_subject.send_response = MagicMock() #no id self.test_subject.set_attribute_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no attr test_message = {"message_id":"2","student_id":ObjectId(),"sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no val test_message = {"message_id":"2","student_id":ObjectId(),"attribute_name":"attr","sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #bogus id bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"attribute_name":"attr","attribute_value":"val","sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #good id sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123"}) test_message = {"message_id":"2","student_id":sid,"attribute_name":"key","attribute_value":"new_value","sender_entity_id":"123"} #override previous val self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"attributes":{"key":"new_value"}}) self.test_subject.db.find_one({"_id":sid})["attributes"]["key"].should.equal("new_value") self.test_subject.send_response.reset_mock() #not owner #test_message["sender_entity_id"]="456" #self.test_subject.set_attribute_callback(test_message) #self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(sid)+" not found."}) def test_get_attribute_callback(self): """ StudentManagementPlugin.get_attribute_callback() Test plan: - Mock logger, ensure written to when called - Try missing id and attribute name, should respond with error - Try with bogus student, should show error - Try with valid attribute, should respond with value - Try with bogus attribute, should reply empty """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("GET_ATTRIBUTE"),call(test_message)] self.test_subject.send_response = MagicMock() #no student id self.test_subject.get_attribute_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id' and 'attribute_name'"}) self.test_subject.send_response.reset_mock() #no attribute name test_message = {"message_id":"2","student_id":ObjectId(),"sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id' and 'attribute_name'"}) self.test_subject.send_response.reset_mock() #bogus student id bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"attribute_name":"attr","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #get good key sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) test_message = {"message_id":"2","student_id":sid,"attribute_name":"key","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"key":"value","resource_id":"456"}) self.test_subject.send_response.reset_mock() #get bogus key test_message = {"message_id":"2","student_id":sid,"attribute_name":"bogus_key","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"bogus_key":"","resource_id":"456"}) self.test_subject.send_response.reset_mock() #not owner #test_message["sender_entity_id"] = "890" #self.test_subject.get_attribute_callback(test_message) #self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(sid)+" not found."}) def test_get_student_model_callback(self): """ StudentManagementPlugin.get_student_model_callback() Test plan: - pass it something without a student id, should respond with error - student_models, timeout_threads should be set - mock out Timer.start, ensure called - mock send, ensure sent with proper parameters """ self.test_subject.send_response = MagicMock() #no id msg = {"message_id":"1","sender_entity_id":"123"} self.test_subject.get_student_model_callback(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"get_student_model requires a 'student_id'", }) self.test_subject.send_response.reset_mock() #bogus id bogus = ObjectId() msg["student_id"] = bogus self.test_subject.get_student_model_callback(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"student with id " + str(bogus) + " does not exist.", }) #this should work sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg["student_id"] = sid msg["sender_entity_id"]="123" setattr(Timer,"start",MagicMock()) self.test_subject.send = MagicMock() self.test_subject.get_populate_student_model_callback_function = MagicMock(return_value="3") self.test_subject.get_student_model_callback(msg) self.test_subject.student_models["1"].should.equal({}) self.test_subject.timeout_threads["1"].start.assert_called_with() self.test_subject.send.assert_called_with("get_student_model_fragment",{ "student_id":str(sid), 'update': False },"3") def test_get_student_model_callback_cached(self): """ StudentManagementPlugin.get_student_model_callback() Cached Test plan: - put a student model in the cache - run get_student_model_callback with no update, and update false, should proceed as planned - run with update = true, should reply with what is in the cache. """ pass """ setattr(Timer,"start",MagicMock()) self.test_subject.send = MagicMock() self.test_subject.send_response = MagicMock() self.test_subject.get_populate_student_model_callback_function = MagicMock(return_value="3") #no update, should send message get_student_model_fragment sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"456","resource_id":"789"}) msg = {"message_id":"1","student_id":sid,"sender_entity_id":"456"} self.test_subject.get_student_model_callback(msg) self.test_subject.send.assert_called_with("get_student_model_fragment",{ "student_id":str(sid), "update": False },"3") #update set to true, same thing msg["update"]=True self.test_subject.get_student_model_callback(msg) self.test_subject.send.assert_called_with("get_student_model_fragment",{ "update": True, "student_id":str(sid), },"3") #update set to false, nothing exists, should do same thing msg["update"]=False self.test_subject.get_student_model_callback(msg) self.test_subject.send.assert_called_with("get_student_model_fragment",{ "student_id":str(sid), "update": False },"3") #update false, thing in cache, should return student model self.test_subject.cache.set(str(sid),{"knowledge_tracing":["1","2"]}) self.test_subject.get_student_model_callback(msg) self.test_subject.send_response.assert_called_with("1",{ "student_id": str(sid), "student_model" : {"knowledge_tracing":["1","2"]}, "cached": True, "resource_id":"789" }) """ def test_get_populate_student_model_callback_function(self): """ StudentManagementPlugin.get_populate_student_model_callback() Test plan: - call the method, get the function - call said function without response[name], message[student_id] and response[fragment], should exit cleanly, send_response not called - set some student_model_fragments to None, should break out, send_response not called - set some bogus fragments, raising key error, should break out, send_response not called - with empty self.timeout_threads, should not call send response - with self.timeout_threads[student_id], should call send response, cancel threads (mock out) and remove threads """ #init stuff self.test_subject.send_response = MagicMock() #self.test_subject.cache.set = MagicMock() msg = {"message_id":"1"} self.test_subject.timeout_threads["1"] = Timer(15,self.test_subject.kill_timeout,[msg, "123"]) self.test_subject.student_model_fragment_names = ["knowledge_tracing"] #missing student_id func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing","fragment":"some data"}) self.test_subject.send_response.call_count.should.equal(0) #missing name msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"fragment":"some data"}) self.test_subject.send_response.call_count.should.equal(0) #missing fragment msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing"}) self.test_subject.send_response.call_count.should.equal(0) #will still not be called, key error until 123 added to student models msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing","fragment":"some_data"}) self.test_subject.send_response.call_count.should.equal(0) self.test_subject.student_models["1"] = {} #bogus name should break for loop msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"bogus_name","fragment":"some_data"}) self.test_subject.send_response.call_count.should.equal(0) #this should work sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg = {"message_id":"1","student_id":sid,"sender_entity_id":"123"} func = self.test_subject.get_populate_student_model_callback_function(str(sid),msg) func({"name":"knowledge_tracing","fragment":"some_data"}) self.test_subject.send_response.assert_called_with("1",{ "student_id": str(sid), "student_model":{"knowledge_tracing":"some_data"}, "cached":False, "resource_id":"456", "message_id":"1", }) self.test_subject.timeout_threads.should_not.contain("1") self.test_subject.student_models.should_not.contain("1") self.test_subject.send_response.reset_mock() #self.test_subject.cache.set.assert_called_with(str(sid),{"knowledge_tracing":"some_data"}) #self.test_subject.cache.set.reset_mock() #simulate timeout (timeout_thread["1"] will be deleted in above test) msg = {"message_id":"1","student_id":sid,"sender_entity_id":"123"} func = self.test_subject.get_populate_student_model_callback_function(str(sid),msg) func({"name":"knowledge_tracing","fragment":"some_data","cached":False}) self.test_subject.send_response.call_count.should.equal(0) #self.test_subject.cache.set.call_count.should.equal(0) def test_kill_timeout(self): """ StudentManagementPlugin.kill_timeout() Test plan: - with nothing in threads or student_models, should exit cleanly, calling response - put something in student_models[student_id] and timeout_threads[student_id] - mock out send_response, ensured called with proper parameters - make sure keys get deleted in student_models and timeout_threads """ self.test_subject.send_response = MagicMock() sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg = {"message_id":"1","student_id":str(sid),"sender_entity_id":"123"} self.test_subject.kill_timeout(msg,str(sid)) self.test_subject.send_response.assert_called_with("1",{ "error":"Get student model timed out. Here is a partial student model.", "student_model":{}, "student_id": str(sid), "resource_id":"456", "message_id":"1", }) self.test_subject.send_response.reset_mock() self.test_subject.student_models = {"1":"value"} self.test_subject.timeout_threads = {"1":"value"} self.test_subject.kill_timeout(msg, str(sid)) self.test_subject.send_response.assert_called_with("1",{ "error":"Get student model timed out. Here is a partial student model.", "student_model":"value", "student_id": str(sid), "resource_id":"456", "message_id":"1", }) ("1" in self.test_subject.student_models).should.equal(False) ("1" in self.test_subject.timeout_threads).should.equal(False) self.test_subject.send_response.reset_mock() def test_transaction_callback_method(self): """ StudentManagementPlugin.transaction_callback_method() Test plan: - call without student and session id, should reply with error - call with invalid session_id, should reply with error - call with completely bogus student, should reply with error - call with ID in attributes with valid session, should call send, calls callback - callback should call send_response, which should contain all the responses - use random session, should send error. """ def mock_send(message_name,payload,callback): callback({"responder":["downstream"]}) self.test_subject.send = MagicMock(side_effect = mock_send) self.test_subject.send_response = MagicMock() #access denied msg = {"message_id":"1","orig_sender_id":"3","sender_entity_id":"888"} self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error" : "Access denied","responder":"student"}) self.test_subject.send_response.reset_mock() #no args msg = {"message_id":"1","orig_sender_id":"2","sender_entity_id":"999"} self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"transaction for Student Manager requires a 'student_id' and 'session_id'", "responder":"student" }) self.test_subject.send_response.reset_mock() #one arg msg["student_id"]="123" self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"transaction for Student Manager requires a 'student_id' and 'session_id'", "responder":"student" }) self.test_subject.send_response.reset_mock() #bad session id msg["session_id"] = "456" self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error" : "The supplied 'session_id' is not a valid ObjectId.", "responder":"student", "success":False }) self.test_subject.send_response.reset_mock() #student does not exist msg["session_id"] = ObjectId() bogus = ObjectId() msg["student_id"] = bogus self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{"error":"transaction failed; could not find student with id " + str(bogus) + ". Try using add_student first.","responder": "student"}) self.test_subject.send_response.reset_mock() #student exists in attributes, bad session student_id = self.test_subject.db.insert({"attributes":{"other_id":"123"},"owner_id":"2"}) session_id = self.test_subject.session_db.insert({"student_id":str(student_id),"date_created":"now"}) msg["student_id"] = "123" msg["session_id"] = bogus self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{"error":"transaction failed; could not find session with id " + str(bogus) +". Try adding/getting a student for a valid session id.","responder": "student"}) self.test_subject.send_response.reset_mock() #student exists, attributes good msg["student_id"] = student_id msg["session_id"] = session_id self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "student_id":str(student_id), "session_id":str(session_id), "responder":"student" }) self.test_subject.send_response.reset_mock()
class TestStudentManagementPlugin(unittest.TestCase): @classmethod def setUpClass(cls): pass @classmethod def tearDownClass(cls): pass def setUp(self): """ setup any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) self.test_subject = StudentManagementPlugin(123,456,None,args_string) self.test_subject.logger = MagicMock() self.test_subject.send_log_entry = MagicMock() def tearDown(self): """ teardown any state that was previously setup with a setup_method call. """ client = MongoClient() client.drop_database(settings.MONGO_DBNAME) self.test_subject = None def test_constructor(self): """ StudentManagementPlugin.__init__() Test plan: -ensure name, logger set as parameters -ensure that mongo is an instance of MongoClient -ensure that db is an instance of Collection -ensure that the full name is hpit.hpit_students """ args = {"transaction_management":"999"} args_string = shlex.quote(json.dumps(args)) smp = StudentManagementPlugin(123,456,None,args_string) isinstance(smp.mongo,MongoClient).should.equal(True) isinstance(smp.db,Collection).should.equal(True) smp.db.full_name.should.equal("hpit_unit_test_db.hpit_students") def test_add_student_callback(self): """ StudentManagementPlugin.add_student_callback() Test plan: -Mock logger, ensure written to when called -Send message without attributes, attributes should be empty in db -Send in attributes, should be present in database -Should be two distinc messages now -mock response, should have a call with the message id and student id """ test_message = {"message_id":"2","sender_entity_id":"3"} calls = [call("ADD_STUDENT"),call(test_message)] self.test_subject.send_response = MagicMock() self.test_subject._post_data = MagicMock(return_value=requests.Response()) requests.Response.json = MagicMock(return_value={"resource_id":"456"}) self.test_subject.add_student_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) client = MongoClient() result = client[settings.MONGO_DBNAME].hpit_students.find({}) result.count().should.equal(1) result[0]["attributes"].should.equal({}) session = client[settings.MONGO_DBNAME].hpit_sessions.find_one({"student_id":str(result[0]["_id"])}) self.test_subject.send_response.assert_called_with("2",{"student_id":str(result[0]["_id"]),"attributes":{},"resource_id":"456","session_id":str(session["_id"])}) self.test_subject.send_response.reset_mock() test_message = {"message_id":"2","attributes":{"attr":"value"},"sender_entity_id":"3"} self.test_subject.add_student_callback(test_message) result = client[settings.MONGO_DBNAME].hpit_students.find({}) result.count().should.equal(2) result[1]["attributes"].should.equal({"attr":"value"}) session = client[settings.MONGO_DBNAME].hpit_sessions.find_one({"student_id":str(result[1]["_id"])}) self.test_subject.send_response.assert_called_with("2",{"student_id":str(result[1]["_id"]),"attributes":{"attr":"value"},"resource_id":"456","session_id":str(session["_id"])}) def test_get_student_callback(self): """ StudentManagementPlugin.get_student_callback() Test plan: - Mock logger, ensure written to when called - mock response - send message without student id, response should contain error - with no student, should return an error - add a student - response should have call with student_id, and attributes """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("GET_STUDENT"),call(test_message)] self.test_subject.send_response = MagicMock() self.test_subject.get_student_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a valid 'student_id' to get a student"}) self.test_subject.send_response.reset_mock() #get non existing student bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"sender_entity_id":"123"} self.test_subject.get_student_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #try with a good student sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"4"}) test_message = {"message_id":"2","student_id":sid,"sender_entity_id":"123"} self.test_subject.get_student_callback(test_message) session = self.test_subject.session_db.find_one({"student_id":str(sid)}) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"resource_id":"4","attributes":{"key":"value"},"session_id":str(session["_id"])}) self.test_subject.send_response.reset_mock() def test_set_attribute_callback(self): """ StudentManagementPlugin.set_attribute_callback() Test plan: - Mock logger, ensure written to when called - Try lacking id, name, and value; response should have an error - Get an attribute with a bum ID; should send back error - Send in a real OID, should have real response. Attribute should be changed in response and in db """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("SET_ATTRIBUTE"),call(test_message)] self.test_subject.send_response = MagicMock() #no id self.test_subject.set_attribute_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no attr test_message = {"message_id":"2","student_id":ObjectId(),"sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no val test_message = {"message_id":"2","student_id":ObjectId(),"attribute_name":"attr","sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id', 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #bogus id bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"attribute_name":"attr","attribute_value":"val","sender_entity_id":"123"} self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #good id sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123"}) test_message = {"message_id":"2","student_id":sid,"attribute_name":"key","attribute_value":"new_value","sender_entity_id":"123"} #override previous val self.test_subject.set_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"attributes":{"key":"new_value"}}) self.test_subject.db.find_one({"_id":sid})["attributes"]["key"].should.equal("new_value") self.test_subject.send_response.reset_mock() #not owner #test_message["sender_entity_id"]="456" #self.test_subject.set_attribute_callback(test_message) #self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(sid)+" not found."}) def test_get_attribute_callback(self): """ StudentManagementPlugin.get_attribute_callback() Test plan: - Mock logger, ensure written to when called - Try missing id and attribute name, should respond with error - Try with bogus student, should show error - Try with valid attribute, should respond with value - Try with bogus attribute, should reply empty """ test_message = {"message_id":"2","sender_entity_id":"123"} calls = [call("GET_ATTRIBUTE"),call(test_message)] self.test_subject.send_response = MagicMock() #no student id self.test_subject.get_attribute_callback(test_message) self.test_subject.send_log_entry.assert_has_calls(calls) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id' and 'attribute_name'"}) self.test_subject.send_response.reset_mock() #no attribute name test_message = {"message_id":"2","student_id":ObjectId(),"sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Must provide a 'student_id' and 'attribute_name'"}) self.test_subject.send_response.reset_mock() #bogus student id bogus_id = ObjectId() test_message = {"message_id":"2","student_id":str(bogus_id),"attribute_name":"attr","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(bogus_id)+" not found."}) self.test_subject.send_response.reset_mock() #get good key sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) test_message = {"message_id":"2","student_id":sid,"attribute_name":"key","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"key":"value","resource_id":"456"}) self.test_subject.send_response.reset_mock() #get bogus key test_message = {"message_id":"2","student_id":sid,"attribute_name":"bogus_key","sender_entity_id":"123"} self.test_subject.get_attribute_callback(test_message) self.test_subject.send_response.assert_called_once_with("2",{"student_id":str(sid),"bogus_key":"","resource_id":"456"}) self.test_subject.send_response.reset_mock() #not owner #test_message["sender_entity_id"] = "890" #self.test_subject.get_attribute_callback(test_message) #self.test_subject.send_response.assert_called_once_with("2",{"error":"Student with id "+str(sid)+" not found."}) def test_get_students_by_attribute(self): """ StudentManagementPlugin.get_students_by_attribute() Test plan: - send without attribute name or value, should send error - with no students in db, should return empty list - add students to db, some with attribute, some without - should returns list with those students """ self.test_subject.send_response = MagicMock() #neither name or value msg = {"message_id":"1","sender_entity_id":"123"} self.test_subject.get_students_by_attribute_callback(msg) self.test_subject.send_response.assert_called_with("1",{"error":"Must provide a 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #just name msg["attribute_name"] = "attr" self.test_subject.get_students_by_attribute_callback(msg) self.test_subject.send_response.assert_called_with("1",{"error":"Must provide a 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no values msg["attribute_value"] = "val" self.test_subject.get_students_by_attribute_callback(msg) self.test_subject.send_response.assert_called_with("1",{"students":[]}) self.test_subject.send_response.reset_mock() #add some values insert_ids = self.test_subject.db.insert([ { "student_id":"555", "resource_id":"2", "attributes":{ "attr":"val" } }, { "student_id":"777", "resource_id":"3", "attributes":{ "attr":"val" } }, { "student_id":"888", "resource_id":"2", "attributes":{ "attr":"BADval" } }, ]) self.test_subject.get_students_by_attribute_callback(msg) session1 = self.test_subject.session_db.find_one({"student_id":str(insert_ids[0])}) session2 = self.test_subject.session_db.find_one({"student_id":str(insert_ids[1])}) self.test_subject.send_response.assert_called_with("1",{ "students":[ { "student_id":str(insert_ids[0]), "resource_id":"2", "attributes":{ "attr":"val" }, "session_id":str(session1["_id"]) }, { "student_id":str(insert_ids[1]), "resource_id":"3", "attributes":{ "attr":"val" }, "session_id":str(session2["_id"]) } ] }) def test_get_or_create_student_by_attribute(self,): """ StudentManagementPlugin.get_or_create_student_by_attribute_callback() Test plan: - without attribute name or value, should throw error - if student doesn't exist: a new one should be created, values returned - if a student does exist: make sure the returned one comes from the database """ self.test_subject.send_response = MagicMock() self.test_subject._post_data = MagicMock(return_value=requests.Response()) requests.Response.json = MagicMock(return_value={"resource_id":"456"}) #neither name or value msg = {"message_id":"1","sender_entity_id":"123"} self.test_subject.get_or_create_student_by_attribute_callback(msg) self.test_subject.send_response.assert_called_with("1",{"error":"Must provide a 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #just name msg["attribute_name"] = "attr" self.test_subject.get_or_create_student_by_attribute_callback(msg) self.test_subject.send_response.assert_called_with("1",{"error":"Must provide a 'attribute_name' and 'attribute_value'"}) self.test_subject.send_response.reset_mock() #no student msg["attribute_value"] = "val" self.test_subject.get_or_create_student_by_attribute_callback(msg) new_student = self.test_subject.db.find_one({}) new_session = self.test_subject.session_db.find_one({}) call_args = self.test_subject.send_response.call_args call_args[0][1]["student_id"].should.equal(str(new_student["_id"])) call_args[0][1]["session_id"].should.equal(str(new_session["_id"])) call_args[0][1]["attributes"].should.equal({"attr":"val"}) resource_id = call_args[0][1]["resource_id"] resource_id.should.equal("456") #student exists self.test_subject.get_or_create_student_by_attribute_callback(msg) call_args = self.test_subject.send_response.call_args call_args[0][1]["student_id"].should.equal(str(new_student["_id"])) call_args[0][1]["attributes"].should.equal({"attr":"val"}) call_args[0][1]["resource_id"].should.equal(resource_id) def test_get_student_model_callback(self): """ StudentManagementPlugin.get_student_model_callback() Test plan: - pass it something without a student id, should respond with error - student_models, timeout_threads should be set - mock out Timer.start, ensure called - mock send, ensure sent with proper parameters """ self.test_subject.send_response = MagicMock() #no id msg = {"message_id":"1","sender_entity_id":"123"} self.test_subject.get_student_model_callback(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"get_student_model requires a 'student_id'", }) self.test_subject.send_response.reset_mock() #bogus id bogus = ObjectId() msg["student_id"] = bogus self.test_subject.get_student_model_callback(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"student with id " + str(bogus) + " does not exist.", }) #this should work sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg["student_id"] = sid msg["sender_entity_id"]="123" setattr(Timer,"start",MagicMock()) self.test_subject.send = MagicMock() self.test_subject.get_populate_student_model_callback_function = MagicMock(return_value="3") self.test_subject.get_student_model_callback(msg) self.test_subject.student_models["1"].should.equal({}) self.test_subject.timeout_threads["1"].start.assert_called_with() self.test_subject.send.assert_called_with("get_student_model_fragment",{ "student_id":str(sid), 'update': False },"3") def test_get_populate_student_model_callback_function(self): """ StudentManagementPlugin.get_populate_student_model_callback() Test plan: - call the method, get the function - call said function without response[name], message[student_id] and response[fragment], should exit cleanly, send_response not called - set some student_model_fragments to None, should break out, send_response not called - set some bogus fragments, raising key error, should break out, send_response not called - with empty self.timeout_threads, should not call send response - with self.timeout_threads[student_id], should call send response, cancel threads (mock out) and remove threads """ #init stuff self.test_subject.send_response = MagicMock() msg = {"message_id":"1"} self.test_subject.timeout_threads["1"] = Timer(15,self.test_subject.kill_timeout,[msg, "123"]) self.test_subject.student_model_fragment_names = ["knowledge_tracing"] #missing student_id func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing","fragment":"some data"}) self.test_subject.send_response.call_count.should.equal(0) #missing name msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"fragment":"some data"}) self.test_subject.send_response.call_count.should.equal(0) #missing fragment msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing"}) self.test_subject.send_response.call_count.should.equal(0) #will still not be called, key error until 123 added to student models msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"knowledge_tracing","fragment":"some_data"}) self.test_subject.send_response.call_count.should.equal(0) self.test_subject.student_models["1"] = {} #bogus name should break for loop msg = {"message_id":"1","student_id":"123"} func = self.test_subject.get_populate_student_model_callback_function("123", msg) func({"name":"bogus_name","fragment":"some_data"}) self.test_subject.send_response.call_count.should.equal(0) #this should work sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg = {"message_id":"1","student_id":sid,"sender_entity_id":"123"} func = self.test_subject.get_populate_student_model_callback_function(str(sid),msg) func({"name":"knowledge_tracing","fragment":"some_data"}) self.test_subject.send_response.assert_called_with("1",{ "student_id": str(sid), "student_model":{"knowledge_tracing":"some_data"}, "cached":False, "resource_id":"456", "message_id":"1", }) self.test_subject.timeout_threads.should_not.contain("1") self.test_subject.student_models.should_not.contain("1") self.test_subject.send_response.reset_mock() #simulate timeout (timeout_thread["1"] will be deleted in above test) msg = {"message_id":"1","student_id":sid,"sender_entity_id":"123"} func = self.test_subject.get_populate_student_model_callback_function(str(sid),msg) func({"name":"knowledge_tracing","fragment":"some_data","cached":False}) self.test_subject.send_response.call_count.should.equal(0) def test_kill_timeout(self): """ StudentManagementPlugin.kill_timeout() Test plan: - with nothing in threads or student_models, should exit cleanly, calling response - put something in student_models[student_id] and timeout_threads[student_id] - mock out send_response, ensured called with proper parameters - make sure keys get deleted in student_models and timeout_threads """ self.test_subject.send_response = MagicMock() sid = self.test_subject.db.insert({"attributes":{"key":"value"},"owner_id":"123","resource_id":"456"}) msg = {"message_id":"1","student_id":str(sid),"sender_entity_id":"123"} self.test_subject.kill_timeout(msg,str(sid)) self.test_subject.send_response.assert_called_with("1",{ "error":"Get student model timed out. Here is a partial student model.", "student_model":{}, "student_id": str(sid), "resource_id":"456", "message_id":"1", }) self.test_subject.send_response.reset_mock() self.test_subject.student_models = {"1":"value"} self.test_subject.timeout_threads = {"1":"value"} self.test_subject.kill_timeout(msg, str(sid)) self.test_subject.send_response.assert_called_with("1",{ "error":"Get student model timed out. Here is a partial student model.", "student_model":"value", "student_id": str(sid), "resource_id":"456", "message_id":"1", }) ("1" in self.test_subject.student_models).should.equal(False) ("1" in self.test_subject.timeout_threads).should.equal(False) self.test_subject.send_response.reset_mock() def test_transaction_callback_method(self): """ StudentManagementPlugin.transaction_callback_method() Test plan: - call without student and session id, should reply with error - call with invalid session_id, should reply with error - call with completely bogus student, should reply with error - call with ID in attributes with valid session, should call send, calls callback - callback should call send_response, which should contain all the responses - use random session, should send error. """ def mock_send(message_name,payload,callback): callback({"responder":["downstream"]}) self.test_subject.send = MagicMock(side_effect = mock_send) self.test_subject.send_response = MagicMock() #access denied msg = {"message_id":"1","orig_sender_id":"3","sender_entity_id":"888"} self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error" : "Access denied","responder":"student"}) self.test_subject.send_response.reset_mock() #no args msg = {"message_id":"1","orig_sender_id":"2","sender_entity_id":"999"} self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"transaction for Student Manager requires a 'student_id' and 'session_id'", "responder":"student" }) self.test_subject.send_response.reset_mock() #one arg msg["student_id"]="123" self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error":"transaction for Student Manager requires a 'student_id' and 'session_id'", "responder":"student" }) self.test_subject.send_response.reset_mock() #bad session id msg["session_id"] = "456" self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "error" : "The supplied 'session_id' is not a valid ObjectId.", "responder":"student", "success":False }) self.test_subject.send_response.reset_mock() #student does not exist msg["session_id"] = ObjectId() bogus = ObjectId() msg["student_id"] = bogus self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{"error":"transaction failed; could not find student with id " + str(bogus) + ". Try using add_student first.","responder": "student"}) self.test_subject.send_response.reset_mock() #student exists in attributes, bad session student_id = self.test_subject.db.insert({"attributes":{"other_id":"123"},"owner_id":"2"}) session_id = self.test_subject.session_db.insert({"student_id":str(student_id),"date_created":"now"}) msg["student_id"] = "123" msg["session_id"] = bogus self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{"error":"transaction failed; could not find session with id " + str(bogus) +". Try adding/getting a student for a valid session id.","responder": "student"}) self.test_subject.send_response.reset_mock() #student exists, attributes good msg["student_id"] = student_id msg["session_id"] = session_id self.test_subject.transaction_callback_method(msg) self.test_subject.send_response.assert_called_with("1",{ "student_id":str(student_id), "session_id":str(session_id), "responder":"student" }) self.test_subject.send_response.reset_mock()