def testWithoutLogging(self):
     self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr,
                                            logErrors=True,
                                            enableCollectLog=False)
     self.sruRecordUpdate.addObserver(self.observer)
     requestBody = self.createRequestBody(
         recordData='<my:data xmlns:my="mine">data</my:data>      ')
     headers, result = self.performRequest(requestBody)
     self.assertTrue(
         '<ucp:operationStatus>success</ucp:operationStatus>' in result)
     self.assertEquals(dict(), self.logCollector)
 def testWithoutLogging(self):
     self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr, logErrors=True, enableCollectLog=False)
     self.sruRecordUpdate.addObserver(self.observer)
     requestBody = self.createRequestBody(recordData='<my:data xmlns:my="mine">data</my:data>      ')
     headers, result = self.performRequest(requestBody)
     self.assertTrue('<ucp:operationStatus>success</ucp:operationStatus>' in result)
     self.assertEquals(dict(), self.logCollector)
    def setUp(self):
        SeecrTestCase.setUp(self)
        self.stderr = StringIO()
        self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr,
                                               logErrors=True,
                                               enableCollectLog=True)

        @asyncnoreturnvalue
        def addOrDelete(*args, **kwargs):
            pass

        self.observer = CallTrace("Observer",
                                  methods={
                                      'add': addOrDelete,
                                      'delete': addOrDelete
                                  })
        self.sruRecordUpdate.addObserver(self.observer)
Example #4
0
 def setUp(self):
     SeecrTestCase.setUp(self)
     self.stderr = StringIO()
     self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr, logErrors=True, enableCollectLog=True)
     @asyncnoreturnvalue
     def addOrDelete(*args, **kwargs):
         pass
     self.observer = CallTrace("Observer", methods={'add': addOrDelete, 'delete': addOrDelete})
     self.sruRecordUpdate.addObserver(self.observer)
class SruRecordUpdateTest(SeecrTestCase):
    """http://www.loc.gov/standards/sru/record-update/"""
    def setUp(self):
        SeecrTestCase.setUp(self)
        self.stderr = StringIO()
        self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr,
                                               logErrors=True,
                                               enableCollectLog=True)

        @asyncnoreturnvalue
        def addOrDelete(*args, **kwargs):
            pass

        self.observer = CallTrace("Observer",
                                  methods={
                                      'add': addOrDelete,
                                      'delete': addOrDelete
                                  })
        self.sruRecordUpdate.addObserver(self.observer)

    def createRequestBody(self,
                          action=CREATE,
                          recordIdentifier="123",
                          recordData="<dc>empty</dc>"):
        return UPDATE_REQUEST % {
            "action": action,
            "recordIdentifier": recordIdentifier,
            "recordPacking": "text/xml",
            "recordSchema": "irrelevantXML",
            "recordData": recordData,
        }

    def performRequest(self, requestBody):
        __callstack_var_logCollector__ = self.logCollector = dict()
        result = ''.join(
            compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        return result.split('\r\n\r\n')

    def testAddXML(self):
        requestBody = self.createRequestBody(
            recordData='<my:data xmlns:my="mine">data</my:data>      ')
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)

        self.assertEquals(1, len(self.observer.calledMethods))
        method = self.observer.calledMethods[0]
        self.assertEquals(3, len(method.kwargs))
        self.assertEquals("add", method.name)
        self.assertEquals("123", method.kwargs['identifier'])
        self.assertEquals(str, type(method.kwargs['identifier']))
        self.assertEquals("irrelevantXML", method.kwargs['partname'])
        resultNode = method.kwargs['lxmlNode']
        self.assertEqualsLxml(XML('<my:data xmlns:my="mine">data</my:data>'),
                              resultNode)
        self.assertEquals(['data'],
                          resultNode.xpath('/my:data/text()',
                                           namespaces={'my': 'mine'}))
        self.assertEquals(ElementTreeType, type(resultNode))
        self.assertEquals(None, resultNode.getroot().tail)

    def testWithoutLogging(self):
        self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr,
                                               logErrors=True,
                                               enableCollectLog=False)
        self.sruRecordUpdate.addObserver(self.observer)
        requestBody = self.createRequestBody(
            recordData='<my:data xmlns:my="mine">data</my:data>      ')
        headers, result = self.performRequest(requestBody)
        self.assertTrue(
            '<ucp:operationStatus>success</ucp:operationStatus>' in result)
        self.assertEquals(dict(), self.logCollector)

    def testDelete(self):
        requestBody = self.createRequestBody(action=DELETE)
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)
        self.assertEquals(1, len(self.observer.calledMethods))

        method = self.observer.calledMethods[0]
        self.assertEquals("delete", method.name)

    def testReplaceIsAdd(self):
        requestBody = self.createRequestBody(action=REPLACE)
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)
        self.assertEquals(1, len(self.observer.calledMethods))

        method = self.observer.calledMethods[0]
        self.assertEquals("add", method.name)

    def testAddXMLWithUcpUpdateRequest(self):
        """It is not entirely sure if updateRequest is in the 'srw' or 'ucp' namespace.
        We now assume it is in 'srw', but versions of meresco-harvester use 'ucp'.
        We will accept both.
        """
        requestBody = self.createRequestBody()
        requestBody = requestBody.replace('srw:updateRequest',
                                          'ucp:updateRequest')
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)

        self.assertEquals(['add'], self.observer.calledMethodNames())

    def testPassCallableObjectForAdd(self):
        def callable():
            pass

        self.observer.returnValues['add'] = (f for f in [callable])
        requestBody = self.createRequestBody(action=REPLACE)
        __callstack_var_logCollector__ = defaultdict(list)
        result = list(
            compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        self.assertTrue(callable in result)
        result.remove(callable)
        header, body = (''.join(result)).split('\r\n\r\n')
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", body)

    def testPassCallableObjectForDelete(self):
        def callable():
            pass

        self.observer.returnValues['delete'] = (f for f in [callable])
        requestBody = self.createRequestBody(action=DELETE)
        __callstack_var_logCollector__ = defaultdict(list)
        result = list(
            compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        self.assertTrue(callable in result)
        result.remove(callable)
        header, body = (''.join(result)).split('\r\n\r\n')
        self.assertEqualsWS(
            """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", body)

    def testNotCorrectXml(self):
        headers, result = self.performRequest("not_xml")
        self.assertTrue(
            '<ucp:operationStatus>fail</ucp:operationStatus>' in result,
            result)
        self.assertEquals(0, len(self.observer.calledMethods))
        self.assertTrue('XMLSyntaxError' in self.stderr.getvalue(),
                        self.stderr.getvalue())

    def testErrorsAreNotPassed(self):
        self.observer.exceptions['add'] = Exception('Some <Exception>')
        headers, result = self.performRequest(self.createRequestBody())
        self.assertTrue(
            """<ucp:operationStatus>fail</ucp:operationStatus>""" in result,
            result)
        diag = parse(StringIO(result))
        self.assertTrue(
            "Some <Exception>" in xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'
            ), result)

    def testValidationErrors(self):
        self.observer.exceptions['add'] = ValidateException('Some <Exception>')
        headers, result = self.performRequest(self.createRequestBody())
        self.assertTrue(
            """<ucp:operationStatus>fail</ucp:operationStatus>""" in result,
            result)
        diag = parse(StringIO(result))
        self.assertEquals(
            "info:srw/diagnostic/12/12",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'
            ))
        self.assertEquals(
            "Some <Exception>",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'
            ))
        self.assertEquals(
            "Invalid data:  record rejected",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'
            ))

    def testEmptyIdentifierNotAccepted(self):
        requestBody = self.createRequestBody(recordIdentifier="")
        headers, result = self.performRequest(requestBody)
        self.assertTrue(
            """<ucp:operationStatus>fail</ucp:operationStatus>""" in result,
            result)
        diag = parse(StringIO(result))
        self.assertEquals(
            "info:srw/diagnostic/12/1",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'
            ))
        self.assertTrue(
            "recordIdentifier is mandatory." in xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'
            ), result)
        self.assertTrue(
            "Invalid component:  record rejected" in xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'
            ), result)

    def testNoIdentifierNotAccepted(self):
        requestBody = """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateRequest xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:action>info:srw/action/1/%(action)s</ucp:action>
    <srw:record>
        <srw:recordPacking>xml</srw:recordPacking>
        <srw:recordSchema>ascheme</srw:recordSchema>
        <srw:recordData>some data</srw:recordData>
    </srw:record>
</srw:updateRequest>"""
        headers, result = self.performRequest(requestBody)
        self.assertTrue(
            """<ucp:operationStatus>fail</ucp:operationStatus>""" in result,
            result)
        diag = parse(StringIO(result))
        self.assertEquals(
            "info:srw/diagnostic/12/1",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'
            ))
        self.assertTrue(
            "recordIdentifier is mandatory." in xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'
            ), result)
        self.assertEquals(
            "Invalid component:  record rejected",
            xpathFirst(
                diag,
                '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'
            ))

    def testCollectLog(self):
        requestBody = self.createRequestBody(action=DELETE,
                                             recordIdentifier='idDelete')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(sruRecordUpdate=dict(delete=['idDelete'])),
                          self.logCollector)
        requestBody = self.createRequestBody(action=CREATE,
                                             recordIdentifier='idAdd')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(sruRecordUpdate=dict(add=['idAdd'])),
                          self.logCollector)

    def testCollectLogWithErrors(self):
        self.observer.exceptions['delete'] = Exception('Some <Exception>')
        requestBody = self.createRequestBody(action=DELETE,
                                             recordIdentifier='idDelete')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(
            dict(sruRecordUpdate=dict(delete=['idDelete'],
                                      errorType=['Exception'],
                                      errorMessage=["Some <Exception>"])),
            self.logCollector)

        self.observer.exceptions['add'] = ValidateException('Nee')
        requestBody = self.createRequestBody(action=CREATE,
                                             recordIdentifier='idAdd')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(
            dict(sruRecordUpdate=dict(add=['idAdd'],
                                      invalid=['idAdd'],
                                      errorType=['ValidateException'],
                                      errorMessage=["Nee"])),
            self.logCollector)

        headers, result = self.performRequest(
            '<srw:updateRequest>Will raise XMLSyntaxError')
        sru_error = self.logCollector['sruRecordUpdate']
        self.assertEqual(['XMLSyntaxError'], sru_error['errorType'])
        self.assertTrue(sru_error['errorMessage'][0].startswith(
            'Namespace prefix srw on updateRequest is not defined, line 1, column 19'
        ))
 def initSruRecordUpdate(self, **kwargs):
     self.sruRecordUpdate = SruRecordUpdate(**kwargs)
     self.observer = CallTrace(
         "Observer",
         emptyGeneratorMethods=['add', 'delete', 'deleteRecord'])
     self.sruRecordUpdate.addObserver(self.observer)
 def initSruRecordUpdate(self, **kwargs):
     self.sruRecordUpdate = SruRecordUpdate(**kwargs)
     self.observer = CallTrace("Observer", emptyGeneratorMethods=['add', 'delete', 'deleteRecord'])
     self.sruRecordUpdate.addObserver(self.observer)
class SruRecordUpdateTest(SeecrTestCase):
    """http://www.loc.gov/standards/sru/record-update/"""

    def setUp(self):
        SeecrTestCase.setUp(self)
        self.stderr = StringIO()
        self.initSruRecordUpdate(stderr=self.stderr, logErrors=True, enableCollectLog=True)

    def initSruRecordUpdate(self, **kwargs):
        self.sruRecordUpdate = SruRecordUpdate(**kwargs)
        self.observer = CallTrace("Observer", emptyGeneratorMethods=['add', 'delete', 'deleteRecord'])
        self.sruRecordUpdate.addObserver(self.observer)

    def createRequestBody(self, action=CREATE, recordIdentifier="123", recordData="<dc>empty</dc>"):
        return UPDATE_REQUEST% {
            "action": action,
            "recordIdentifier": recordIdentifier,
            "recordPacking": "text/xml",
            "recordSchema": "irrelevantXML",
            "recordData": recordData,
        }

    def performRequest(self, requestBody):
        __callstack_var_logCollector__ = self.logCollector = dict()
        result = ''.join(compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        return result.split('\r\n\r\n')

    def testAddXML(self):
        requestBody = self.createRequestBody(recordData='<my:data xmlns:my="mine">data</my:data>      ')
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)

        self.assertEquals(1, len(self.observer.calledMethods))
        method = self.observer.calledMethods[0]
        self.assertEquals(3, len(method.kwargs))
        self.assertEquals("add", method.name)
        self.assertEquals("123", method.kwargs['identifier'])
        self.assertEquals(str, type(method.kwargs['identifier']))
        self.assertEquals("irrelevantXML", method.kwargs['partname'])
        resultNode = method.kwargs['lxmlNode']
        self.assertEqualsLxml(XML('<my:data xmlns:my="mine">data</my:data>'), resultNode)
        self.assertEquals(['data'], resultNode.xpath('/my:data/text()', namespaces={'my':'mine'}))
        self.assertEquals(ElementTreeType, type(resultNode))
        self.assertEquals(None, resultNode.getroot().tail)

    def testWithoutLogging(self):
        self.sruRecordUpdate = SruRecordUpdate(stderr=self.stderr, logErrors=True, enableCollectLog=False)
        self.sruRecordUpdate.addObserver(self.observer)
        requestBody = self.createRequestBody(recordData='<my:data xmlns:my="mine">data</my:data>      ')
        headers, result = self.performRequest(requestBody)
        self.assertTrue('<ucp:operationStatus>success</ucp:operationStatus>' in result)
        self.assertEquals(dict(), self.logCollector)

    def testDelete(self):
        requestBody = self.createRequestBody(action=DELETE)
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)
        self.assertEquals(1, len(self.observer.calledMethods))

        method = self.observer.calledMethods[0]
        self.assertEquals("delete", method.name)

    def testReplaceIsAdd(self):
        requestBody = self.createRequestBody(action=REPLACE)
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)
        self.assertEquals(1, len(self.observer.calledMethods))

        method = self.observer.calledMethods[0]
        self.assertEquals("add", method.name)

    def testDeleteRecord(self):
        self.initSruRecordUpdate(stderr=self.stderr, logErrors=True, enableCollectLog=True, supportDeleteRecord=True)
        requestBody = self.createRequestBody(action=DELETE)
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)
        self.assertEquals(1, len(self.observer.calledMethods))

        method = self.observer.calledMethods[0]
        self.assertEquals("deleteRecord", method.name)
        self.assertEqual('123', method.kwargs['identifier'])
        self.assertEqualsWS('''<srw:record xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
        <srw:recordPacking>text/xml</srw:recordPacking>
        <srw:recordSchema>irrelevantXML</srw:recordSchema>
        <srw:recordData><dc>empty</dc></srw:recordData>
</srw:record>''', lxmltostring(method.kwargs['record']))

    def testAddXMLWithUcpUpdateRequest(self):
        """It is not entirely sure if updateRequest is in the 'srw' or 'ucp' namespace.
        We now assume it is in 'srw', but versions of meresco-harvester use 'ucp'.
        We will accept both.
        """
        requestBody = self.createRequestBody()
        requestBody = requestBody.replace('srw:updateRequest', 'ucp:updateRequest')
        headers, result = self.performRequest(requestBody)
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", result)

        self.assertEquals(['add'], self.observer.calledMethodNames())

    def testPassCallableObjectForAdd(self):
        def callable():
            pass
        self.observer.returnValues['add'] = (f for f in [callable])
        requestBody = self.createRequestBody(action=REPLACE)
        __callstack_var_logCollector__ = defaultdict(list)
        result = list(compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        self.assertTrue(callable in result)
        result.remove(callable)
        header,body = (''.join(result)).split('\r\n\r\n')
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", body)

    def testPassCallableObjectForDelete(self):
        def callable():
            pass
        self.observer.returnValues['delete'] = (f for f in [callable])
        requestBody = self.createRequestBody(action=DELETE)
        __callstack_var_logCollector__ = defaultdict(list)
        result = list(compose(self.sruRecordUpdate.handleRequest(Body=requestBody)))
        self.assertTrue(callable in result)
        result.remove(callable)
        header,body = (''.join(result)).split('\r\n\r\n')
        self.assertEqualsWS("""<?xml version="1.0" encoding="UTF-8"?>
<srw:updateResponse xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:operationStatus>success</ucp:operationStatus>
</srw:updateResponse>""", body)


    def testNotCorrectXml(self):
        headers, result = self.performRequest("not_xml")
        self.assertTrue('<ucp:operationStatus>fail</ucp:operationStatus>' in result, result)
        self.assertEquals(0, len(self.observer.calledMethods))
        self.assertTrue('XMLSyntaxError' in self.stderr.getvalue(), self.stderr.getvalue())

    def testErrorsAreNotPassed(self):
        self.observer.exceptions['add'] = Exception('Some <Exception>')
        headers, result = self.performRequest(self.createRequestBody())
        self.assertTrue("""<ucp:operationStatus>fail</ucp:operationStatus>""" in result, result)
        diag = parse(StringIO(result))
        self.assertTrue("Some <Exception>" in xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'), result)

    def testValidationErrors(self):
        self.observer.exceptions['add'] = ValidateException('Some <Exception>')
        headers, result = self.performRequest(self.createRequestBody())
        self.assertTrue("""<ucp:operationStatus>fail</ucp:operationStatus>""" in result, result)
        diag = parse(StringIO(result))
        self.assertEquals("info:srw/diagnostic/12/12", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'))
        self.assertEquals("Some <Exception>", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'))
        self.assertEquals("Invalid data:  record rejected", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'))

    def testEmptyIdentifierNotAccepted(self):
        requestBody = self.createRequestBody(recordIdentifier="")
        headers, result = self.performRequest(requestBody)
        self.assertTrue("""<ucp:operationStatus>fail</ucp:operationStatus>""" in result, result)
        diag = parse(StringIO(result))
        self.assertEquals("info:srw/diagnostic/12/1", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'))
        self.assertTrue("recordIdentifier is mandatory." in xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'), result)
        self.assertTrue("Invalid component:  record rejected" in xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'), result)

    def testNoIdentifierNotAccepted(self):
        requestBody = """<?xml version="1.0" encoding="UTF-8"?>
<srw:updateRequest xmlns:srw="http://www.loc.gov/zing/srw/" xmlns:ucp="info:lc/xmlns/update-v1">
    <srw:version>1.0</srw:version>
    <ucp:action>info:srw/action/1/%(action)s</ucp:action>
    <srw:record>
        <srw:recordPacking>xml</srw:recordPacking>
        <srw:recordSchema>ascheme</srw:recordSchema>
        <srw:recordData>some data</srw:recordData>
    </srw:record>
</srw:updateRequest>"""
        headers, result = self.performRequest(requestBody)
        self.assertTrue("""<ucp:operationStatus>fail</ucp:operationStatus>""" in result, result)
        diag = parse(StringIO(result))
        self.assertEquals("info:srw/diagnostic/12/1", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:uri/text()'))
        self.assertTrue("recordIdentifier is mandatory." in xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:details/text()'), result)
        self.assertEquals("Invalid component:  record rejected", xpathFirst(diag, '/srw:updateResponse/srw:diagnostics/diag:diagnostic/diag:message/text()'))

    def testCollectLog(self):
        requestBody = self.createRequestBody(action=DELETE, recordIdentifier='idDelete')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(sruRecordUpdate=dict(delete=['idDelete'])), self.logCollector)
        requestBody = self.createRequestBody(action=CREATE, recordIdentifier='idAdd')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(sruRecordUpdate=dict(add=['idAdd'])), self.logCollector)

    def testCollectLogWithErrors(self):
        self.observer.exceptions['delete'] = Exception('Some <Exception>')
        requestBody = self.createRequestBody(action=DELETE, recordIdentifier='idDelete')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(
            sruRecordUpdate=dict(
                delete=['idDelete'],
                errorType=['Exception'],
                errorMessage=["Some <Exception>"]
            )), self.logCollector)

        self.observer.exceptions['add'] = ValidateException('Nee')
        requestBody = self.createRequestBody(action=CREATE, recordIdentifier='idAdd')
        headers, result = self.performRequest(requestBody)
        self.assertEquals(dict(
            sruRecordUpdate=dict(
                add=['idAdd'],
                invalid=['idAdd'],
                errorType=['ValidateException'],
                errorMessage=["Nee"]
            )), self.logCollector)

        headers, result = self.performRequest('<srw:updateRequest>Will raise XMLSyntaxError')
        sru_error = self.logCollector['sruRecordUpdate']
        self.assertEqual(['XMLSyntaxError'], sru_error['errorType'])
        self.assertTrue(sru_error['errorMessage'][0].startswith('Namespace prefix srw on updateRequest is not defined, line 1, column 19'))