diff --git a/test/remote_lrs_test.py b/test/remote_lrs_test.py index ec7c042..ea944b2 100644 --- a/test/remote_lrs_test.py +++ b/test/remote_lrs_test.py @@ -18,6 +18,8 @@ from tincan.context import Context from tincan.context_activities import ContextActivities from tincan.score import Score +from tincan.group import Group +from tincan.base import Base from tincan.result import Result from tincan.substatement import SubStatement from tincan.statement_ref import StatementRef @@ -33,69 +35,69 @@ class RemoteLRSTest(unittest.TestCase): def setUp(self): - if not hasattr(self, "set"): - self.endpoint = lrs_properties.endpoint - self.version = lrs_properties.version - self.username = lrs_properties.username - self.password = lrs_properties.password - self.lrs = RemoteLRS( - version=self.version, - endpoint=self.endpoint, - username=self.username, - password=self.password, - ) - - self.agent = Agent(mbox="mailto:tincanpython@tincanapi.com") - self.agent2 = Agent(mbox="Agent2.mailto:tincanpython@tincanapi.com") - self.verb = Verb( - id="http://adlnet.gov/expapi/verbs/experienced", - display=LanguageMap({"en-US": "experienced"}) - ) - - self.activity = Activity( - id="http://tincanapi.com/TinCanPython/Test/Unit/0", - definition=ActivityDefinition() - ) - self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test" - self.activity.definition.name = LanguageMap({"en-US": "Python Tests"}) - self.activity.definition.description = LanguageMap( - {"en-US": "Unit test in the test suite for the Python library"} - ) - - self.parent = Activity( - id="http://tincanapi.com/TinCanPython/Test", - definition=ActivityDefinition()) - self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test-suite" - self.parent.definition.name = LanguageMap({"en-US": "Python Tests"}) - self.parent.definition.description = LanguageMap( - {"en-US": "Unit test in the test suite for the Python library"} - ) - - self.statement_ref = StatementRef(id=uuid.uuid4()) - - self.context = Context(registration=uuid.uuid4(), statement=self.statement_ref) - #self.context.context_activities = ContextActivities(parent=[self.parent]) - - self.score = Score( - raw=97, - scaled=0.97, - max=100, - min=0 - ) - - self.result = Result( - score=self.score, - success=True, - completion=True, - duration="PT120S" - ) - - self.substatement = SubStatement( - actor=self.agent, - verb=self.verb, - object=self.activity, - ) - self.set = True + self.endpoint = lrs_properties.endpoint + self.version = lrs_properties.version + self.username = lrs_properties.username + self.password = lrs_properties.password + self.lrs = RemoteLRS( + version=self.version, + endpoint=self.endpoint, + username=self.username, + password=self.password, + ) + + self.agent = Agent(mbox="mailto:tincanpython@tincanapi.com") + self.agent2 = Agent(mbox="Agent2.mailto:tincanpython@tincanapi.com") + self.verb = Verb( + id="http://adlnet.gov/expapi/verbs/experienced", + display=LanguageMap({"en-US": "experienced"}) + ) + + self.group = Group(member=[self.agent, self.agent2]) + + self.activity = Activity( + id="http://tincanapi.com/TinCanPython/Test/Unit/0", + definition=ActivityDefinition() + ) + self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test" + self.activity.definition.name = LanguageMap({"en-US": "Python Tests"}) + self.activity.definition.description = LanguageMap( + {"en-US": "Unit test in the test suite for the Python library"} + ) + + self.parent = Activity( + id="http://tincanapi.com/TinCanPython/Test", + definition=ActivityDefinition()) + self.activity.definition.type = "http://id.tincanapi.com/activitytype/unit-test-suite" + self.parent.definition.name = LanguageMap({"en-US": "Python Tests"}) + self.parent.definition.description = LanguageMap( + {"en-US": "Unit test in the test suite for the Python library"} + ) + + self.statement_ref = StatementRef(id=uuid.uuid4()) + + self.context = Context(registration=uuid.uuid4(), statement=self.statement_ref) + #self.context.context_activities = ContextActivities(parent=[self.parent]) + + self.score = Score( + raw=97, + scaled=0.97, + max=100, + min=0 + ) + + self.result = Result( + score=self.score, + success=True, + completion=True, + duration="PT120S" + ) + + self.substatement = SubStatement( + actor=self.agent, + verb=self.verb, + object=self.activity, + ) def tearDown(self): pass @@ -130,8 +132,8 @@ def test_save_statement(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) self.assertIsNotNone(response.content.id) + self.shallow_compare(statement, response.content) def test_save_statement_with_id(self): statement = Statement( @@ -144,21 +146,21 @@ def test_save_statement_with_id(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) def test_save_statement_conflict(self): - test_id = str(uuid.uuid4()) + test_id = unicode(uuid.uuid4()) statement1 = Statement( actor=self.agent, verb=self.verb, - object=self.statement_ref, + object=self.substatement, id=test_id ) statement2 = Statement( actor=self.agent2, verb=self.verb, - object=self.statement_ref, + object=self.substatement, id=test_id ) response = self.lrs.save_statement(statement1) @@ -183,7 +185,20 @@ def test_save_statement_ref(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) + + def test_save_statement_group(self): + statement = Statement( + actor=self.agent, + verb=self.verb, + object=self.group, + id=str(uuid.uuid4()) + ) + response = self.lrs.save_statement(statement) + + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.shallow_compare(statement, response.content, True) def test_save_statement_substatement(self): statement = Statement( @@ -196,7 +211,7 @@ def test_save_statement_substatement(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) - self.assertEqual(statement, response.content) + self.shallow_compare(statement, response.content, True) def test_save_statements(self): statement1 = Statement( @@ -216,8 +231,8 @@ def test_save_statements(self): self.assertTrue(response.success) self.assertIsNotNone(response.content[0].id) self.assertIsNotNone(response.content[1].id) - self.assertEquals(statement1, response.content[0]) - self.assertEquals(statement2, response.content[1]) + self.shallow_compare(statement1, response.content[0]) + self.shallow_compare(statement2, response.content[1]) def test_retrieve_statement(self): statement = Statement( @@ -230,15 +245,41 @@ def test_retrieve_statement(self): ) save_resp = self.lrs.save_statement(statement) - if(save_resp.success): - response = self.lrs.retrieve_statement(save_resp.content.id) - self.assertIsInstance(response, LRSResponse) - self.assertTrue(response.success) - self.assertEquals(statement, response.content) - else: - print "test_retrieve_statement: save_statement failed" + self.assertTrue(save_resp.success) + + response = self.lrs.retrieve_statement(save_resp.content.id) + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.shallow_compare(statement, response.content) def test_query_statements(self): + s1 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s1) + + s2 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s2) + + s3 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s3) + query = { "agent": self.agent, "verb": self.verb, @@ -246,28 +287,84 @@ def test_query_statements(self): "related_activities": True, "related_agents": True, "format": "ids", - "limit": 10 + "limit": 2 } response = self.lrs.query_statements(query) self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) self.assertIsInstance(response.content, StatementsResult) + self.assertTrue(hasattr(response.content, 'more')) + self.assertIsNotNone(response.content.more) + self.shallow_compare(s1, response.content.statements[0]) + self.shallow_compare(s2, response.content.statements[1]) + + def test_query_none(self): + query = { + "agent": Agent(mbox="unique@tincanapi.com"), + "verb": self.verb, + "activity": self.parent, + "related_activities": True, + "related_agents": True, + "format": "ids", + "limit": 2 + } + response = self.lrs.query_statements(query) + + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.assertIsInstance(response.content, StatementsResult) + self.assertTrue(hasattr(response.content, 'more')) + self.assertTrue(hasattr(response.content, 'statements')) + self.assertEquals(response.content.statements, []) def test_more_statements(self): + s1 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s1) + + s2 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s2) + + s3 = Statement( + actor=self.agent, + verb=self.verb, + object=self.parent, + result=self.result, + id=str(uuid.uuid4()) + ) + self.lrs.save_statement(s3) + query = { + "agent": self.agent, + "verb": self.verb, + "activity": self.parent, + "related_activities": True, + "related_agents": True, "format": "ids", "limit": 2 } query_resp = self.lrs.query_statements(query) - if query_resp.success and query_resp.content.more is not None: - response = self.lrs.more_statements(query_resp.content) - self.assertIsInstance(response, LRSResponse) - self.assertTrue(response.success) - self.assertIsInstance(response.content, StatementsResult) - else: - print "test_more_statements: query_statements failed or did not return a more url" + self.assertIsInstance(query_resp, LRSResponse) + self.assertTrue(query_resp.success) + self.assertIsNotNone(query_resp.content.more) + + response = self.lrs.more_statements(query_resp.content) + self.assertIsInstance(response, LRSResponse) + self.assertTrue(response.success) + self.assertIsInstance(response.content, StatementsResult) def test_retrieve_state_ids(self): response = self.lrs.retrieve_state_ids(activity=self.activity, agent=self.agent) @@ -369,6 +466,15 @@ def test_delete_agent_profile(self): self.assertIsInstance(response, LRSResponse) self.assertTrue(response.success) + def shallow_compare(self, s1, s2, compare_ids=False): + for k, v in vars(s1).iteritems(): + if not k == '_id' or compare_ids: + self.assertTrue(hasattr(s2, k)) + if isinstance(v, Base): + self.shallow_compare(v, getattr(s2, k), True) + else: + self.assertEqual(v, getattr(s2, k)) + if __name__ == "__main__": suite = unittest.TestLoader().loadTestsFromTestCase(RemoteLRSTest) unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tincan/remote_lrs.py b/tincan/remote_lrs.py index ed60d75..c26289b 100644 --- a/tincan/remote_lrs.py +++ b/tincan/remote_lrs.py @@ -16,10 +16,11 @@ import urllib import json import base64 - from urlparse import urlparse + from tincan.lrs_response import LRSResponse from tincan.http_request import HTTPRequest +from tincan.statement_list import StatementList from tincan.agent import Agent from tincan.statement import Statement from tincan.activity import Activity @@ -114,8 +115,12 @@ def _send_request(self, request): web_req = httplib.HTTPConnection(parsed.hostname, parsed.port) path = parsed.path - if params: - path += "?" + params + if parsed.query or parsed.path: + path += "?" + if parsed.query: + path += parsed.query + if params: + path += params if hasattr(request, "content") and request.content is not None: web_req.request( @@ -204,14 +209,12 @@ def save_statements(self, statements): """Save statements to LRS and update their statement id's :param statements: A list of statement objects to be saved - :type statements: list + :type statements: :class:`StatementList` :return: LRS Response object with the saved list of statements as content :rtype: :class:`tincan.lrs_response.LRSResponse` """ - def make_statement(st): - return st if isinstance(st, Statement) else Statement(st) - - statements = [make_statement(s) for s in statements] + if not isinstance(statements, StatementList): + statements = StatementList(statements) request = HTTPRequest( endpoint=self.endpoint, @@ -220,7 +223,7 @@ def make_statement(st): ) request.headers["Content-Type"] = "application/json" - request.content = json.dumps([s.to_json(self.version) for s in statements]) + request.content = statements.to_json() lrs_response = self._send_request(request) @@ -344,7 +347,7 @@ def more_statements(self, more_url): lrs_response = self._send_request(request) if lrs_response.success: - lrs_response.content = StatementsResult(json.loads(lrs_response.data)) + lrs_response.content = StatementsResult.from_json(lrs_response.data) return lrs_response diff --git a/tincan/statement_list.py b/tincan/statement_list.py new file mode 100644 index 0000000..e0a501c --- /dev/null +++ b/tincan/statement_list.py @@ -0,0 +1,27 @@ +# Copyright 2014 Rustici Software +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tincan.statement import Statement +from tincan.typed_list import TypedList + +""" +.. module:: statement_list + :synopsis: A type checked Statement list + +""" + + +class StatementList(TypedList): + + _cls = Statement \ No newline at end of file