def test_lock_success(self):
        """
        Test the lockfile processor (happy path).
        """

        sut = LockingProcessor(key='test_lockpath')
        insert_proto = {
            'inserts': ['a'],
            'deletes': [],
            'data': {
                'a': {
                    'test_lockpath': '/path/to/some/file.txt.lck'
                }
            }
        }
        insert = copy.deepcopy(insert_proto)

        lock_mock = Mock(spec=_Lockfile)

        # Acquire lock
        expected = copy.deepcopy(insert_proto)
        matches = MatchesSendDeltaItemInvocation(expected, sut.out_locked)
        send = Mock(spec=Scheduler.send)

        with patch('spreadflow_delta.proc._Lockfile.open', return_value=lock_mock) as open_mock:
            sut(insert, send)

        self.assertEquals(send.call_count, 1)
        self.assertThat(send.call_args, matches)

        open_mock.assert_called_once_with('/path/to/some/file.txt.lck')
        self.assertEquals(lock_mock.close.call_count, 0)

        # Release lock
        expected = copy.deepcopy(insert_proto)
        matches = MatchesSendDeltaItemInvocation(expected, sut.out)
        send = Mock(spec=Scheduler.send)

        sut.release(insert, send)

        self.assertEquals(send.call_count, 1)
        self.assertThat(send.call_args, matches)

        lock_mock.close.assert_called_once_with()
    def test_lock_fail(self):
        """
        Test the lockfile processor (fail).
        """

        sut = LockingProcessor(key='test_lockpath')
        insert_proto = {
            'inserts': ['a'],
            'deletes': [],
            'data': {
                'a': {
                    'test_lockpath': '/path/to/some/file.txt.lck'
                }
            }
        }

        lock_mock = Mock(spec=_Lockfile)

        # Try-fail lock
        expected = {
            'inserts': [],
            'deletes': [],
            'data': {},
            'date': 'DATESTAMP'
        }
        insert = copy.deepcopy(insert_proto)
        update = copy.deepcopy(expected)
        matches = MatchesSendDeltaItemInvocation(expected, sut.out_retry)
        send = Mock(spec=Scheduler.send)

        sut._now = Mock(return_value='DATESTAMP')
        with patch('spreadflow_delta.proc._Lockfile.open', side_effect=LockError()) as open_mock:
            sut(insert, send)

        self.assertEquals(send.call_count, 1)
        self.assertThat(send.call_args, matches)

        open_mock.assert_called_once_with('/path/to/some/file.txt.lck')
        self.assertEquals(lock_mock.close.call_count, 0)

        # Retry-success lock
        lock_mock = Mock(spec=_Lockfile)
        expected = copy.deepcopy(insert_proto)
        expected['date'] = 'DATESTAMP'
        matches = MatchesSendDeltaItemInvocation(expected, sut.out_locked)
        send = Mock(spec=Scheduler.send)

        with patch('spreadflow_delta.proc._Lockfile.open', return_value=lock_mock) as open_mock:
            sut(update, send)

        self.assertEquals(send.call_count, 1)
        self.assertThat(send.call_args, matches)

        open_mock.assert_called_once_with('/path/to/some/file.txt.lck')
        self.assertEquals(lock_mock.close.call_count, 0)

        # Release lock
        expected = copy.deepcopy(insert_proto)
        expected['date'] = 'DATESTAMP'
        matches = MatchesSendDeltaItemInvocation(expected, sut.out)
        send = Mock(spec=Scheduler.send)

        sut.release(update, send)

        self.assertEquals(send.call_count, 1)
        self.assertThat(send.call_args, matches)

        lock_mock.close.assert_called_once_with()