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.
     """
     self.test_subject = KnowledgeTracingPlugin(
         123, 456, None,
         shlex.quote(
             json.dumps({
                 "shared_messages": {
                     "get_student_model_fragment":
                     ["88bb246d-7347-4f57-8cbe-95944a4e0027"]
                 },
                 "transaction_management": "999",
             })))
     self.test_subject.send_response = 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.
     """
     self.test_subject = KnowledgeTracingPlugin(123,456,None,shlex.quote(json.dumps({
             "shared_messages": {
                 "get_student_model_fragment": [
                     "88bb246d-7347-4f57-8cbe-95944a4e0027"
                 ]
             },
             "transaction_management":"999",
         })))
     self.test_subject.send_response = MagicMock()
 def test_constructor(self):
     """
     KnowledgeTracingPlugin.__init__() Test plan:
         - make sure logger set right
         - make sure self.mongo is a mongo client
         - make sure db is a collection
         -check full name
     """
     ds = KnowledgeTracingPlugin(
         1234, 5678, None,
         shlex.quote(
             json.dumps({
                 "shared_messages": {
                     "get_student_model_fragment":
                     ["88bb246d-7347-4f57-8cbe-95944a4e0027"]
                 },
                 "transaction_management": "999"
             })))
     ds.logger.should.equal(None)
     isinstance(ds.mongo, MongoClient).should.equal(True)
     isinstance(ds.db, Collection).should.equal(True)
     ds.db.full_name.should.equal(
         "hpit_unit_test_db.hpit_knowledge_tracing")
class TestKnowledgeTracingPlugin(unittest.TestCase):

    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.
        """
        self.test_subject = KnowledgeTracingPlugin(123,456,None,shlex.quote(json.dumps({
                "shared_messages": {
                    "get_student_model_fragment": [
                        "88bb246d-7347-4f57-8cbe-95944a4e0027"
                    ]
                },
                "transaction_management":"999",
            })))
        self.test_subject.send_response = 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):
        """
        KnowledgeTracingPlugin.__init__() Test plan:
            - make sure logger set right
            - make sure self.mongo is a mongo client
            - make sure db is a collection
            -check full name
        """
        ds = KnowledgeTracingPlugin(1234,5678,None,shlex.quote(json.dumps({
                "shared_messages": {
                    "get_student_model_fragment": [
                        "88bb246d-7347-4f57-8cbe-95944a4e0027"
                    ]
                },
                "transaction_management":"999"
            })))
        ds.logger.should.equal(None)
        isinstance(ds.mongo,MongoClient).should.equal(True)
        isinstance(ds.db,Collection).should.equal(True)
        ds.db.full_name.should.equal("hpit_unit_test_db.hpit_knowledge_tracing")
    
    def test_kt_batch_trace(self):
        """
        KnowledgeTracingPlugin.kt_batch_trace() Test plan:
            - pass without skill_list, student id, should respond error
            - pass with bogus skill_list, should repond error
            - mock _kt_trace should be called with each skill
            - response should be called with skill_name : response dict
        """
        #no params
        msg = {"message_id":"2","sender_entity_id":"3"}
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with("2",{"error":"kt_batch_trace requires 'skill_list', and 'student_id'"})
        self.test_subject.send_response.reset_mock()
        
        #just student id
        msg["student_id"] = "123"
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with("2",{"error":"kt_batch_trace requires 'skill_list', and 'student_id'"})
        self.test_subject.send_response.reset_mock()
        
        #bad skill_list
        msg["skill_list"] = "skill 1 skill 2"
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with("2",{"error":"kt_batch_trace requires 'skill_list' to be dict"})
        self.test_subject.send_response.reset_mock()
        
        #good skills, not init
        def side_effect_trace(kt_config,correct):
            return {
                'skill_id': kt_config["skill_id"],
                'student_id': kt_config['student_id'],
                'probability_known': 5,
                'probability_learned': 5,
                'probability_guess': 5,
                'probability_mistake': 5,     
            }
            
        self.test_subject._kt_trace = MagicMock(side_effect=side_effect_trace)      
        
        msg["skill_list"] = {"444":True,"555":False}
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with("2",{
                "traced_skills":{
                    "444" : {
                        'skill_id': "444",
                        'probability_known': .75,
                        'probability_learned': .33,
                        'probability_guess': .33,
                        'probability_mistake': .33,
                        'student_id':"123",   
                        
                    },
                    "555" : {
                        'skill_id': "555",
                        'probability_known': .75,
                        'probability_learned': .33,
                        'probability_guess': .33,
                        'probability_mistake': .33,
                        'student_id':"123",   
                        
                    },
                    
                }
            })
        self.test_subject.send_response.reset_mock()
        
        #skills exist
        self.test_subject.kt_batch_trace(msg)
        self.test_subject._kt_trace.call_count.should.equal(2)
        self.test_subject.send_response.assert_called_with("2",{
                "traced_skills":{
                    "444" : {
                        'skill_id': "444",
                        'probability_known': 5,
                        'probability_learned': 5,
                        'probability_guess': 5,
                        'probability_mistake': 5,
                        'student_id':"123",   
                    },
                    "555" : {
                        'skill_id': "555",
                        'probability_known': 5,
                        'probability_learned': 5,
                        'probability_guess': 5,
                        'probability_mistake': 5,
                        'student_id':"123",   
                    },
                }
            })
        self.test_subject.send_response.reset_mock()
    
    def test_kt_trace_no_params(self):
        """
        KnowledgeTracingPlugin.kt_trace() Test plan:
            - mock out send response
            - send without each of the required parameters, response should be error
            - send in bogus kt_config values, should send error response
            - set a kt_config with values, send in new message
            - make sure new p_known is updated in db
            - make sure new p_known is returned in response
        """
        msg = {"message_id":"2","sender_entity_id":"3"}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"})
    
    def test_kt_trace_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_trace() Invalid Skill Id:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add"}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace 'skill_id' is not a valid skill id"})
        
    def test_kt_trace_no_correct(self):
        """
        KnowledgeTracingPlugin.kt_trace() No Correct Parameter:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":str(ObjectId())}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"})
    
    def test_kt_trace_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_trace() Student Id Missing:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":str(ObjectId()),"correct":True}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"})
        
    def test_kt_trace_no_initial_settings(self):
        """
        KnowledgeTracingPlugin.kt_trace() No settings in db:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":skill_id,"correct":True,"student_id":"4"}
        msg["probability_known"] = .7
        msg["probability_learned"] = .2
        msg["probability_guess"] = .3
        msg["probability_mistake"] = .4
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with("2", {
            'probability_guess': 0.33, 
            'probability_known': 0.75, 
            'probability_learned': 0.33, 
            'probability_mistake': 0.33, 
            'skill_id': skill_id, 
            'student_id': '4'
        })


    def test_kt_trace_correct_true(self): 
        """
        KnowledgeTracingPlugin.kt_trace() Correct true:
        """
        insert_doc = {
            "skill_id":"444",
            "student_id":"123",
            "probability_known": .7,
            "probability_learned": .2,
            "probability_guess": .3,
            "probability_mistake": .4,
        }
        
        value = self.test_subject._kt_trace(insert_doc,True)
        expected_value = (.42 / .51) + ( (1 - (.42 / .51)) * .2)
        nose.tools.assert_almost_equal(value["probability_known"],expected_value,places=5)
        nose.tools.assert_equal(value["student_id"],"123")
        nose.tools.assert_equal(value["skill_id"],"444")
     
    def test_kt_trace_correct_false(self):
        """
        KnowledgeTracingPlugin.kt_trace() Correct false:
        """
        insert_doc = {
            "skill_id":"444",
            "student_id":"123",
            "probability_known": .7,
            "probability_learned": .2,
            "probability_guess": .3,
            "probability_mistake": .4,
        }
        
        value = self.test_subject._kt_trace(insert_doc,False)
        expected_value = (.28 / .49) + ( (1 - (.28 / .49)) * .2)
        nose.tools.assert_almost_equal(value["probability_known"],expected_value,places=5)
        nose.tools.assert_equal(value["student_id"],"123")
        nose.tools.assert_equal(value["skill_id"],"444")
        
    def test_kt_set_initial_no_sender_entity_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Test plan:
            - mock out send response
            - send without each of the required parameters, response should be error
            - send message for non existant kt_config, should insert a new one
            - send message for existing kt_config, should update that one
            - response should always echo parameters sent in
        """
        msg = {"message_id":"2"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_initial_no_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Skill ID:
        """
        msg = {"message_id":"2","sender_entity_id":"3"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_initial_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Invalid Skill ID:
        """        
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace 'skill_id' is not a valid skill id"})
    
    def test_kt_set_initial_no_probability_known(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Skill ID:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_initial_no_probability_leraned(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Probability Learned:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        msg["probability_known"] = "1.1"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_initial_no_probability_guess(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Probabilty Guess:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_intiial_no_probablity_mistake(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No probability Mistake:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
    
    def test_kt_set_intitial_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Student ID:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"})
        self.test_subject.send_response.reset_mock()
    
    def test_kt_set_initial_bad_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Bad Skill ID:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":skill_id,"student_id":"4"}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"
        
        def send_mock_error(message_name,payload,callback):
            callback({"error":"there was an error"})
            
        self.test_subject.send = MagicMock(side_effect=send_mock_error)
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send.call_count.should.equal(1)
        self.test_subject.send_response.assert_called_once_with("2",{
            "error":"skill_id " + str(skill_id) + " is invalid.",
            "skill_manager_error":"there was an error",
            })


    def test_kt_set_initial_good_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Good Skill ID:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":skill_id,"student_id":"4"}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"
        
        def send_mock_clean(message_name,payload,callback):
            callback({})
        
        self.test_subject.send = MagicMock(side_effect=send_mock_clean)
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send.call_count.should.equal(1)
        self.test_subject.send_response.assert_called_once_with("2",{
            'skill_id': str(skill_id),
            'probability_known': "1.1",
            'probability_learned': "1.2",
            'probability_guess': "1.3",
            'probability_mistake':"1.4",
            'student_id':"4"
            })
        self.test_subject.db.find().count().should.equal(1)
        

    def test_kt_set_initial_overwrite_data(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Overwrite data:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":skill_id,"student_id":"4"}
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"
        
        def send_mock_clean(message_name,payload,callback):
            callback({})
        
        self.test_subject.send = MagicMock(side_effect=send_mock_clean)
        
        self.test_subject.db.insert(dict(msg))
        
        msg["probability_known"] = "2.1"
        msg["probability_learned"] = "2.2"
        msg["probability_guess"] = "2.3"
        msg["probability_mistake"] = "2.4"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with("2",{
            'skill_id': str(skill_id),
            'probability_known': "2.1",
            'probability_learned': "2.2",
            'probability_guess': "2.3",
            'probability_mistake':"2.4",
            'student_id':"4"
            })
        thing = self.test_subject.db.find_one({"sender_entity_id":"3","skill_id":str(skill_id),"student_id":"4"})
        thing["probability_known"].should.equal("2.1")
        thing["probability_learned"].should.equal("2.2")
        thing["probability_guess"].should.equal("2.3")
        thing["probability_mistake"].should.equal("2.4")
    
    def test_kt_reset(self):
        """
        KnowledgeTracingPlugin.kt_reset() Test plan:
            - mock out send response
            - send without entity_id, skill, or student_id. should have error response
            - send with bogus info, should still return response with everything 0
            - put a kt_config in the db, with non zero values
            - response should be sent, db should have zeroed out values
        """
    def test_kt_reset_no_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() No Skill Id:
        """
        msg = {"message_id":"2","sender_entity_id":"3"}
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_reset requires 'sender_entity_id', 'skill_id', and 'student_id'"})
       
    def test_kt_reset_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() Invalid Id:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add"}
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_trace 'skill_id' is not a valid skill id"})
    
    def test_kt_reset_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() No Student Id:
        """
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":str(ObjectId())}
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with("2",{"error":"kt_reset requires 'sender_entity_id', 'skill_id', and 'student_id'"})
        
    def test_kt_reset_no_data(self):
        """
        KnowledgeTracingPlugin.kt_reset() No kt_config:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":skill_id,"student_id":"4"}
        
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with("2", {
            'error': 'No configuration found in knowledge tracer for skill/student combination.'
        })
        
    def test_kt_reset_data_present(self):
        """
        KnowledgeTracingPlugin.kt_reset() kt_config Exists:
        """
        skill_id = str(ObjectId())
        msg = {"message_id":"2","sender_entity_id":"3","skill_id":"add","skill_id":skill_id,"student_id":"4"}

        oid = self.test_subject.db.insert({"sender_entity_id":"3","skill_id":str(skill_id),"student_id":"4"})
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with("2",{
            'skill_id': str(skill_id),
            'probability_known': 0.75,
            'probability_learned': 0.33,
            'probability_guess': 0.33,
            'probability_mistake': 0.33,
            'student_id':"4"
        })
        
        ob = self.test_subject.db.find_one({"_id":oid})
        ob["probability_known"].should.equal(0.75)
        ob["probability_learned"].should.equal(0.33)
        ob["probability_guess"].should.equal(0.33)
        ob["probability_mistake"].should.equal(0.33)
        
        
    def test_get_student_model_fragment_no_student_id(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Test plan:
            - if nothing in db, response should have empty list
            - add some skills to db, make sure exist in list.
        """
        
        msg = {"message_id":"1","sender_entity_id":"2"}
        
        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with("1",{
                "error":"knowledge tracing get_student_model_fragment requires 'student_id'",
            })
        
        
    def test_get_student_model_fragment_empty_db(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Empty DB:
        """ 
        msg = {"message_id":"1","sender_entity_id":"2","student_id":"3"}
        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with("1",{
            "name":"knowledge_tracing",
            "fragment":[],
        })
        
    def test_get_student_model_fragment_full_db(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Non Empty DB:
        """ 
        msg = {"message_id":"1","sender_entity_id":"2","student_id":"123"}
        self.test_subject.db.insert([
            {
                'sender_entity_id': "2",
                'skill_id': "567",
                'probability_known': 1,
                'probability_learned': 1,
                'probability_guess': 0,
                'probability_mistake': 0,
                'student_id': "123",
            },
            {
                'sender_entity_id': "2",
                'skill_id': "8910",
                'probability_known': 0,
                'probability_learned': 0,
                'probability_guess': 1,
                'probability_mistake': 1,
                'student_id': "123",
            },
            {
                'sender_entity_id': "2",
                'skill_id': "8910",
                'probability_known': 0,
                'probability_learned': 0,
                'probability_guess': 1,
                'probability_mistake': 1,
                'student_id': "333",
            },
        ])
        
        #should return values 1 and 2 from above
        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with("1",{
            "name":"knowledge_tracing",
            "fragment":[{
                'skill_id': "567",
                'probability_known': 1,
                'probability_learned': 1,
                'probability_guess': 0,
                'probability_mistake': 0,
                'student_id': "123",
                },
                {
                'skill_id': "8910",
                'probability_known': 0,
                'probability_learned': 0,
                'probability_guess': 1,
                'probability_mistake': 1,
                'student_id': "123",
                }
            ],
        })
        
    def test_transaction_callback_method(self):
        """
        KnowledgeTracingPlugin.transaction_callback_method() Test plan:
            - try without skill_ids, student_id, and correct, should reply with error
            - try with skill_ids not dict, should reply with error
            - first pass, all skills should be set to .75, ensure called with those values
            - next pass, all skill should be close to the expected values
        """
        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":"2","sender_entity_id":"888"}
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{"error" : "Access denied","responder":"kt",})
        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":"knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
             "responder":"kt",   
        })
        self.test_subject.send_response.reset_mock()
               
        #just outcome
        msg["outcome"] = "correct"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{
             "error":"knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
             "responder":"kt"  
        })
        self.test_subject.send_response.reset_mock()
        
        #no skill_ids
        msg["student_id"] = "123"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{
             "error":"knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
             "responder":"kt"    
        })
        self.test_subject.send_response.reset_mock()
        
        #skill ids not valid
        msg["skill_ids"] = "4"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{
             "error":"knowledge tracing not done because supplied 'skill_ids' is not valid; must be dict.",
             "responder":"kt"    
        })
        self.test_subject.send_response.reset_mock()
        
        #outcome is not string
        msg["skill_ids"] = {"skill1":"skill1_id","skill2":"skill2_id"}
        msg["outcome"] = True
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{
             "error": "knowledge tracing not done because outcome was neither 'correct' or 'incorrect'",
             "responder":"kt"  
        })
        self.test_subject.send_response.reset_mock()
        
        #first run, default values
        msg["outcome"] = "correct"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with("1",{
             "traced_skills" : {
                 "skill1":{
                    'skill_id': "skill1_id",
                    'student_id': "123",
                    'probability_known': 0.75,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
                },
                "skill2":{
                    'skill_id': "skill2_id",
                    'student_id': "123",
                    'probability_known': 0.75,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
             }},
             "responder":"kt",  
        })
        self.test_subject.send_response.reset_mock()
        
        #second run, values should change
        
        self.test_subject.transaction_callback_method(msg)
        """
        self.test_subject.send_response.assert_called_with("1",{
             "traced_skills" : {
                 "skill1":{
                    'skill_id': "skill1_id",
                    'student_id': "123",
                    'probability_known': 0.875,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
                },
                "skill2":{
                    'skill_id': "skill2_id",
                    'student_id': "123",
                    'probability_known': 0.875,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
             }},
             "responder":"kt",  
        })
        """
        self.test_subject.send_response.called.should.equal(True) #can't check params because of float precision
        expected_value = (.5025 / .585) + ( (1 - (.5025 / .585)) * .33)
        thing  = self.test_subject.db.find_one({'sender_entity_id':"2",'student_id':"123","skill_id":"skill1_id"})
        nose.tools.assert_almost_equal(thing["probability_known"],expected_value,places=5)
        thing  = self.test_subject.db.find_one({'sender_entity_id':"2",'student_id':"123","skill_id":"skill2_id"})
        nose.tools.assert_almost_equal(thing["probability_known"],expected_value,places=5)
        
        self.test_subject.send_response.reset_mock()
class TestKnowledgeTracingPlugin(unittest.TestCase):
    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.
        """
        self.test_subject = KnowledgeTracingPlugin(
            123, 456, None,
            shlex.quote(
                json.dumps({
                    "shared_messages": {
                        "get_student_model_fragment":
                        ["88bb246d-7347-4f57-8cbe-95944a4e0027"]
                    },
                    "transaction_management": "999",
                })))
        self.test_subject.send_response = 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):
        """
        KnowledgeTracingPlugin.__init__() Test plan:
            - make sure logger set right
            - make sure self.mongo is a mongo client
            - make sure db is a collection
            -check full name
        """
        ds = KnowledgeTracingPlugin(
            1234, 5678, None,
            shlex.quote(
                json.dumps({
                    "shared_messages": {
                        "get_student_model_fragment":
                        ["88bb246d-7347-4f57-8cbe-95944a4e0027"]
                    },
                    "transaction_management": "999"
                })))
        ds.logger.should.equal(None)
        isinstance(ds.mongo, MongoClient).should.equal(True)
        isinstance(ds.db, Collection).should.equal(True)
        ds.db.full_name.should.equal(
            "hpit_unit_test_db.hpit_knowledge_tracing")

    def test_kt_batch_trace(self):
        """
        KnowledgeTracingPlugin.kt_batch_trace() Test plan:
            - pass without skill_list, student id, should respond error
            - pass with bogus skill_list, should repond error
            - mock _kt_trace should be called with each skill
            - response should be called with skill_name : response dict
        """
        #no params
        msg = {"message_id": "2", "sender_entity_id": "3"}
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with(
            "2", {
                "error":
                "kt_batch_trace requires 'skill_list', and 'student_id'"
            })
        self.test_subject.send_response.reset_mock()

        #just student id
        msg["student_id"] = "123"
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with(
            "2", {
                "error":
                "kt_batch_trace requires 'skill_list', and 'student_id'"
            })
        self.test_subject.send_response.reset_mock()

        #bad skill_list
        msg["skill_list"] = "skill 1 skill 2"
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with(
            "2", {"error": "kt_batch_trace requires 'skill_list' to be dict"})
        self.test_subject.send_response.reset_mock()

        #good skills, not init
        def side_effect_trace(kt_config, correct):
            return {
                'skill_id': kt_config["skill_id"],
                'student_id': kt_config['student_id'],
                'probability_known': 5,
                'probability_learned': 5,
                'probability_guess': 5,
                'probability_mistake': 5,
            }

        self.test_subject._kt_trace = MagicMock(side_effect=side_effect_trace)

        msg["skill_list"] = {"444": True, "555": False}
        self.test_subject.kt_batch_trace(msg)
        self.test_subject.send_response.assert_called_with(
            "2", {
                "traced_skills": {
                    "444": {
                        'skill_id': "444",
                        'probability_known': .75,
                        'probability_learned': .33,
                        'probability_guess': .33,
                        'probability_mistake': .33,
                        'student_id': "123",
                    },
                    "555": {
                        'skill_id': "555",
                        'probability_known': .75,
                        'probability_learned': .33,
                        'probability_guess': .33,
                        'probability_mistake': .33,
                        'student_id': "123",
                    },
                }
            })
        self.test_subject.send_response.reset_mock()

        #skills exist
        self.test_subject.kt_batch_trace(msg)
        self.test_subject._kt_trace.call_count.should.equal(2)
        self.test_subject.send_response.assert_called_with(
            "2", {
                "traced_skills": {
                    "444": {
                        'skill_id': "444",
                        'probability_known': 5,
                        'probability_learned': 5,
                        'probability_guess': 5,
                        'probability_mistake': 5,
                        'student_id': "123",
                    },
                    "555": {
                        'skill_id': "555",
                        'probability_known': 5,
                        'probability_learned': 5,
                        'probability_guess': 5,
                        'probability_mistake': 5,
                        'student_id': "123",
                    },
                }
            })
        self.test_subject.send_response.reset_mock()

    def test_kt_trace_no_params(self):
        """
        KnowledgeTracingPlugin.kt_trace() Test plan:
            - mock out send response
            - send without each of the required parameters, response should be error
            - send in bogus kt_config values, should send error response
            - set a kt_config with values, send in new message
            - make sure new p_known is updated in db
            - make sure new p_known is returned in response
        """
        msg = {"message_id": "2", "sender_entity_id": "3"}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"
            })

    def test_kt_trace_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_trace() Invalid Skill Id:
        """
        msg = {"message_id": "2", "sender_entity_id": "3", "skill_id": "add"}
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {"error": "kt_trace 'skill_id' is not a valid skill id"})

    def test_kt_trace_no_correct(self):
        """
        KnowledgeTracingPlugin.kt_trace() No Correct Parameter:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": str(ObjectId())
        }
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"
            })

    def test_kt_trace_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_trace() Student Id Missing:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": str(ObjectId()),
            "correct": True
        }
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_trace requires 'sender_entity_id', 'skill_id', 'student_id' and 'correct'"
            })

    def test_kt_trace_no_initial_settings(self):
        """
        KnowledgeTracingPlugin.kt_trace() No settings in db:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": skill_id,
            "correct": True,
            "student_id": "4"
        }
        msg["probability_known"] = .7
        msg["probability_learned"] = .2
        msg["probability_guess"] = .3
        msg["probability_mistake"] = .4
        self.test_subject.kt_trace(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                'probability_guess': 0.33,
                'probability_known': 0.75,
                'probability_learned': 0.33,
                'probability_mistake': 0.33,
                'skill_id': skill_id,
                'student_id': '4'
            })

    def test_kt_trace_correct_true(self):
        """
        KnowledgeTracingPlugin.kt_trace() Correct true:
        """
        insert_doc = {
            "skill_id": "444",
            "student_id": "123",
            "probability_known": .7,
            "probability_learned": .2,
            "probability_guess": .3,
            "probability_mistake": .4,
        }

        value = self.test_subject._kt_trace(insert_doc, True)
        expected_value = (.42 / .51) + ((1 - (.42 / .51)) * .2)
        nose.tools.assert_almost_equal(value["probability_known"],
                                       expected_value,
                                       places=5)
        nose.tools.assert_equal(value["student_id"], "123")
        nose.tools.assert_equal(value["skill_id"], "444")

    def test_kt_trace_correct_false(self):
        """
        KnowledgeTracingPlugin.kt_trace() Correct false:
        """
        insert_doc = {
            "skill_id": "444",
            "student_id": "123",
            "probability_known": .7,
            "probability_learned": .2,
            "probability_guess": .3,
            "probability_mistake": .4,
        }

        value = self.test_subject._kt_trace(insert_doc, False)
        expected_value = (.28 / .49) + ((1 - (.28 / .49)) * .2)
        nose.tools.assert_almost_equal(value["probability_known"],
                                       expected_value,
                                       places=5)
        nose.tools.assert_equal(value["student_id"], "123")
        nose.tools.assert_equal(value["skill_id"], "444")

    def test_kt_set_initial_no_sender_entity_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Test plan:
            - mock out send response
            - send without each of the required parameters, response should be error
            - send message for non existant kt_config, should insert a new one
            - send message for existing kt_config, should update that one
            - response should always echo parameters sent in
        """
        msg = {"message_id": "2"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_initial_no_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Skill ID:
        """
        msg = {"message_id": "2", "sender_entity_id": "3"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_initial_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Invalid Skill ID:
        """
        msg = {"message_id": "2", "sender_entity_id": "3", "skill_id": "add"}
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {"error": "kt_trace 'skill_id' is not a valid skill id"})

    def test_kt_set_initial_no_probability_known(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Skill ID:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_initial_no_probability_leraned(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Probability Learned:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        msg["probability_known"] = "1.1"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_initial_no_probability_guess(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Probabilty Guess:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_intiial_no_probablity_mistake(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No probability Mistake:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })

    def test_kt_set_intitial_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() No Student ID:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_set_initial requires 'sender_entity_id', 'skill_id', 'probability_known', 'probability_learned', 'probability_guess', 'probability_mistake', and 'student_id'"
            })
        self.test_subject.send_response.reset_mock()

    def test_kt_set_initial_bad_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Bad Skill ID:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": skill_id,
            "student_id": "4"
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"

        def send_mock_error(message_name, payload, callback):
            callback({"error": "there was an error"})

        self.test_subject.send = MagicMock(side_effect=send_mock_error)
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send.call_count.should.equal(1)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error": "skill_id " + str(skill_id) + " is invalid.",
                "skill_manager_error": "there was an error",
            })

    def test_kt_set_initial_good_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Good Skill ID:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": skill_id,
            "student_id": "4"
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"

        def send_mock_clean(message_name, payload, callback):
            callback({})

        self.test_subject.send = MagicMock(side_effect=send_mock_clean)
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send.call_count.should.equal(1)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                'skill_id': str(skill_id),
                'probability_known': "1.1",
                'probability_learned': "1.2",
                'probability_guess': "1.3",
                'probability_mistake': "1.4",
                'student_id': "4"
            })
        self.test_subject.db.find().count().should.equal(1)

    def test_kt_set_initial_overwrite_data(self):
        """
        KnowledgeTracingPlugin.kt_set_initial_callback() Overwrite data:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": skill_id,
            "student_id": "4"
        }
        msg["probability_known"] = "1.1"
        msg["probability_learned"] = "1.2"
        msg["probability_guess"] = "1.3"
        msg["probability_mistake"] = "1.4"

        def send_mock_clean(message_name, payload, callback):
            callback({})

        self.test_subject.send = MagicMock(side_effect=send_mock_clean)

        self.test_subject.db.insert(dict(msg))

        msg["probability_known"] = "2.1"
        msg["probability_learned"] = "2.2"
        msg["probability_guess"] = "2.3"
        msg["probability_mistake"] = "2.4"
        self.test_subject.kt_set_initial_callback(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                'skill_id': str(skill_id),
                'probability_known': "2.1",
                'probability_learned': "2.2",
                'probability_guess': "2.3",
                'probability_mistake': "2.4",
                'student_id': "4"
            })
        thing = self.test_subject.db.find_one({
            "sender_entity_id": "3",
            "skill_id": str(skill_id),
            "student_id": "4"
        })
        thing["probability_known"].should.equal("2.1")
        thing["probability_learned"].should.equal("2.2")
        thing["probability_guess"].should.equal("2.3")
        thing["probability_mistake"].should.equal("2.4")

    def test_kt_reset(self):
        """
        KnowledgeTracingPlugin.kt_reset() Test plan:
            - mock out send response
            - send without entity_id, skill, or student_id. should have error response
            - send with bogus info, should still return response with everything 0
            - put a kt_config in the db, with non zero values
            - response should be sent, db should have zeroed out values
        """

    def test_kt_reset_no_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() No Skill Id:
        """
        msg = {"message_id": "2", "sender_entity_id": "3"}
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_reset requires 'sender_entity_id', 'skill_id', and 'student_id'"
            })

    def test_kt_reset_invalid_skill_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() Invalid Id:
        """
        msg = {"message_id": "2", "sender_entity_id": "3", "skill_id": "add"}
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {"error": "kt_trace 'skill_id' is not a valid skill id"})

    def test_kt_reset_no_student_id(self):
        """
        KnowledgeTracingPlugin.kt_reset() No Student Id:
        """
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": str(ObjectId())
        }
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                "error":
                "kt_reset requires 'sender_entity_id', 'skill_id', and 'student_id'"
            })

    def test_kt_reset_no_data(self):
        """
        KnowledgeTracingPlugin.kt_reset() No kt_config:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": skill_id,
            "student_id": "4"
        }

        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                'error':
                'No configuration found in knowledge tracer for skill/student combination.'
            })

    def test_kt_reset_data_present(self):
        """
        KnowledgeTracingPlugin.kt_reset() kt_config Exists:
        """
        skill_id = str(ObjectId())
        msg = {
            "message_id": "2",
            "sender_entity_id": "3",
            "skill_id": "add",
            "skill_id": skill_id,
            "student_id": "4"
        }

        oid = self.test_subject.db.insert({
            "sender_entity_id": "3",
            "skill_id": str(skill_id),
            "student_id": "4"
        })
        self.test_subject.kt_reset(msg)
        self.test_subject.send_response.assert_called_once_with(
            "2", {
                'skill_id': str(skill_id),
                'probability_known': 0.75,
                'probability_learned': 0.33,
                'probability_guess': 0.33,
                'probability_mistake': 0.33,
                'student_id': "4"
            })

        ob = self.test_subject.db.find_one({"_id": oid})
        ob["probability_known"].should.equal(0.75)
        ob["probability_learned"].should.equal(0.33)
        ob["probability_guess"].should.equal(0.33)
        ob["probability_mistake"].should.equal(0.33)

    def test_get_student_model_fragment_no_student_id(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Test plan:
            - if nothing in db, response should have empty list
            - add some skills to db, make sure exist in list.
        """

        msg = {"message_id": "1", "sender_entity_id": "2"}

        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error":
                "knowledge tracing get_student_model_fragment requires 'student_id'",
            })

    def test_get_student_model_fragment_empty_db(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Empty DB:
        """
        msg = {"message_id": "1", "sender_entity_id": "2", "student_id": "3"}
        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "name": "knowledge_tracing",
                "fragment": [],
            })

    def test_get_student_model_fragment_full_db(self):
        """
        KnowledgeTracingPlugin.get_student_model_fragment() Non Empty DB:
        """
        msg = {"message_id": "1", "sender_entity_id": "2", "student_id": "123"}
        self.test_subject.db.insert([
            {
                'sender_entity_id': "2",
                'skill_id': "567",
                'probability_known': 1,
                'probability_learned': 1,
                'probability_guess': 0,
                'probability_mistake': 0,
                'student_id': "123",
            },
            {
                'sender_entity_id': "2",
                'skill_id': "8910",
                'probability_known': 0,
                'probability_learned': 0,
                'probability_guess': 1,
                'probability_mistake': 1,
                'student_id': "123",
            },
            {
                'sender_entity_id': "2",
                'skill_id': "8910",
                'probability_known': 0,
                'probability_learned': 0,
                'probability_guess': 1,
                'probability_mistake': 1,
                'student_id': "333",
            },
        ])

        #should return values 1 and 2 from above
        self.test_subject.get_student_model_fragment(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "name":
                "knowledge_tracing",
                "fragment": [{
                    'skill_id': "567",
                    'probability_known': 1,
                    'probability_learned': 1,
                    'probability_guess': 0,
                    'probability_mistake': 0,
                    'student_id': "123",
                }, {
                    'skill_id': "8910",
                    'probability_known': 0,
                    'probability_learned': 0,
                    'probability_guess': 1,
                    'probability_mistake': 1,
                    'student_id': "123",
                }],
            })

    def test_transaction_callback_method(self):
        """
        KnowledgeTracingPlugin.transaction_callback_method() Test plan:
            - try without skill_ids, student_id, and correct, should reply with error
            - try with skill_ids not dict, should reply with error
            - first pass, all skills should be set to .75, ensure called with those values
            - next pass, all skill should be close to the expected values
        """
        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": "2",
            "sender_entity_id": "888"
        }
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error": "Access denied",
                "responder": "kt",
            })
        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":
                "knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
                "responder": "kt",
            })
        self.test_subject.send_response.reset_mock()

        #just outcome
        msg["outcome"] = "correct"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error":
                "knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
                "responder": "kt"
            })
        self.test_subject.send_response.reset_mock()

        #no skill_ids
        msg["student_id"] = "123"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error":
                "knowledge tracing not done because 'skill_ids', 'student_id', or 'outcome' not found.",
                "responder": "kt"
            })
        self.test_subject.send_response.reset_mock()

        #skill ids not valid
        msg["skill_ids"] = "4"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error":
                "knowledge tracing not done because supplied 'skill_ids' is not valid; must be dict.",
                "responder": "kt"
            })
        self.test_subject.send_response.reset_mock()

        #outcome is not string
        msg["skill_ids"] = {"skill1": "skill1_id", "skill2": "skill2_id"}
        msg["outcome"] = True
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "error":
                "knowledge tracing not done because outcome was neither 'correct' or 'incorrect'",
                "responder": "kt"
            })
        self.test_subject.send_response.reset_mock()

        #first run, default values
        msg["outcome"] = "correct"
        self.test_subject.transaction_callback_method(msg)
        self.test_subject.send_response.assert_called_with(
            "1", {
                "traced_skills": {
                    "skill1": {
                        'skill_id': "skill1_id",
                        'student_id': "123",
                        'probability_known': 0.75,
                        'probability_learned': 0.33,
                        'probability_guess': 0.33,
                        'probability_mistake': 0.33,
                    },
                    "skill2": {
                        'skill_id': "skill2_id",
                        'student_id': "123",
                        'probability_known': 0.75,
                        'probability_learned': 0.33,
                        'probability_guess': 0.33,
                        'probability_mistake': 0.33,
                    }
                },
                "responder": "kt",
            })
        self.test_subject.send_response.reset_mock()

        #second run, values should change

        self.test_subject.transaction_callback_method(msg)
        """
        self.test_subject.send_response.assert_called_with("1",{
             "traced_skills" : {
                 "skill1":{
                    'skill_id': "skill1_id",
                    'student_id': "123",
                    'probability_known': 0.875,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
                },
                "skill2":{
                    'skill_id': "skill2_id",
                    'student_id': "123",
                    'probability_known': 0.875,
                    'probability_learned': 0.33,
                    'probability_guess': 0.33,
                    'probability_mistake': 0.33,
             }},
             "responder":"kt",  
        })
        """
        self.test_subject.send_response.called.should.equal(
            True)  #can't check params because of float precision
        expected_value = (.5025 / .585) + ((1 - (.5025 / .585)) * .33)
        thing = self.test_subject.db.find_one({
            'sender_entity_id': "2",
            'student_id': "123",
            "skill_id": "skill1_id"
        })
        nose.tools.assert_almost_equal(thing["probability_known"],
                                       expected_value,
                                       places=5)
        thing = self.test_subject.db.find_one({
            'sender_entity_id': "2",
            'student_id': "123",
            "skill_id": "skill2_id"
        })
        nose.tools.assert_almost_equal(thing["probability_known"],
                                       expected_value,
                                       places=5)

        self.test_subject.send_response.reset_mock()