org.obm.push.calendar.CalendarBackendTest.java Source code

Java tutorial

Introduction

Here is the source code for org.obm.push.calendar.CalendarBackendTest.java

Source

/* ***** BEGIN LICENSE BLOCK *****
 * 
 * Copyright (C) 2011-2014  Linagora
 *
 * This program is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Affero General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version, provided you comply 
 * with the Additional Terms applicable for OBM connector by Linagora 
 * pursuant to Section 7 of the GNU Affero General Public License, 
 * subsections (b), (c), and (e), pursuant to which you must notably (i) retain 
 * the Message sent thanks to OBM, Free Communication by Linagora? 
 * signature notice appended to any and all outbound messages 
 * (notably e-mail and meeting requests), (ii) retain all hypertext links between 
 * OBM and obm.org, as well as between Linagora and linagora.com, and (iii) refrain 
 * from infringing Linagora intellectual property rights over its trademarks 
 * and commercial brands. Other Additional Terms apply, 
 * see <http://www.linagora.com/licenses/> for more details. 
 *
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
 * for more details. 
 *
 * You should have received a copy of the GNU Affero General Public License 
 * and its applicable Additional Terms for OBM along with this program. If not, 
 * see <http://www.gnu.org/licenses/> for the GNU Affero General Public License version 3 
 * and <http://www.linagora.com/licenses/> for the Additional Terms applicable to 
 * OBM connectors. 
 * 
 * ***** END LICENSE BLOCK ***** */
package org.obm.push.calendar;

import static org.assertj.core.api.Assertions.assertThat;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createControl;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;

import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.util.Strings;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.easymock.IMocksControl;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.obm.icalendar.ICalendar;
import org.obm.icalendar.Ical4jHelper;
import org.obm.icalendar.Ical4jUser;
import org.obm.icalendar.ical4jwrapper.ICalendarEvent;
import org.obm.push.backend.WindowingEvent;
import org.obm.push.backend.WindowingEventChanges;
import org.obm.push.bean.AttendeeStatus;
import org.obm.push.bean.BodyPreference;
import org.obm.push.bean.Credentials;
import org.obm.push.bean.Device;
import org.obm.push.bean.DeviceId;
import org.obm.push.bean.FilterType;
import org.obm.push.bean.FolderType;
import org.obm.push.bean.ItemSyncState;
import org.obm.push.bean.MSEvent;
import org.obm.push.bean.MSEventUid;
import org.obm.push.bean.PIMDataType;
import org.obm.push.bean.ServerId;
import org.obm.push.bean.SyncCollectionOptions;
import org.obm.push.bean.SyncKey;
import org.obm.push.bean.User;
import org.obm.push.bean.User.Factory;
import org.obm.push.bean.UserDataRequest;
import org.obm.push.bean.change.hierarchy.BackendFolder;
import org.obm.push.bean.change.hierarchy.BackendFolder.BackendId;
import org.obm.push.bean.change.hierarchy.BackendFolders;
import org.obm.push.bean.change.hierarchy.CalendarPath;
import org.obm.push.bean.change.hierarchy.Folder;
import org.obm.push.bean.change.item.ItemChange;
import org.obm.push.exception.ConversionException;
import org.obm.push.exception.DaoException;
import org.obm.push.exception.ICalendarConverterException;
import org.obm.push.exception.activesync.ItemNotFoundException;
import org.obm.push.impl.ObmSyncBackend.WindowingChangesDelta;
import org.obm.push.protocol.bean.CollectionId;
import org.obm.push.resource.OpushResourcesHolder;
import org.obm.push.service.ClientIdService;
import org.obm.push.service.DateService;
import org.obm.push.service.EventService;
import org.obm.push.service.FolderSnapshotDao;
import org.obm.push.service.impl.MappingService;
import org.obm.push.store.WindowingDao;
import org.obm.push.utils.DateUtils;
import org.obm.sync.NotAllowedException;
import org.obm.sync.auth.AccessToken;
import org.obm.sync.auth.EventAlreadyExistException;
import org.obm.sync.auth.EventNotFoundException;
import org.obm.sync.auth.ServerFault;
import org.obm.sync.calendar.Attendee;
import org.obm.sync.calendar.ContactAttendee;
import org.obm.sync.calendar.DeletedEvent;
import org.obm.sync.calendar.Event;
import org.obm.sync.calendar.EventExtId;
import org.obm.sync.calendar.EventObmId;
import org.obm.sync.calendar.Participation;
import org.obm.sync.calendar.UserAttendee;
import org.obm.sync.client.calendar.CalendarClient;
import org.obm.sync.items.EventChanges;
import org.obm.sync.items.ParticipationChanges;
import org.slf4j.Logger;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;

public class CalendarBackendTest {

    private User user;
    private Device device;
    private UserDataRequest userDataRequest;
    private AccessToken token;
    private CollectionId collectionId;
    private Folder calendarFolder;

    private MappingService mappingService;
    private CalendarClient calendarClient;
    private CalendarClient.Factory calendarClientFactory;
    private EventConverter eventConverter;
    private EventService eventService;
    private ConsistencyEventChangesLogger consistencyLogger;
    private EventExtId.Factory eventExtIdFactory;
    private WindowingDao windowingDao;
    private ClientIdService clientIdService;
    private Ical4jHelper ical4jHelper;
    private Ical4jUser.Factory ical4jUserFactory;
    private DateService dateService;
    private OpushResourcesHolder opushResourcesHolder;
    private FolderSnapshotDao folderSnapshotDao;

    private CalendarBackend calendarBackend;
    private IMocksControl mockControl;
    private CloseableHttpClient httpClient;

    @Before
    public void setUp() {
        this.user = Factory.create().createUser("test@test", "test@domain", "displayName");
        this.device = new Device.Factory().create(null, "iPhone", "iOs 5", new DeviceId("my phone"), null);
        this.userDataRequest = new UserDataRequest(new Credentials(user, "password".toCharArray()), "noCommand",
                device);
        this.token = new AccessToken(0, "OBM");
        this.httpClient = HttpClientBuilder.create().build();
        collectionId = CollectionId.of(1);
        calendarFolder = Folder.builder().collectionId(collectionId).backendId(CalendarPath.of("test"))
                .displayName("test calendar").folderType(FolderType.DEFAULT_CALENDAR_FOLDER)
                .parentBackendIdOpt(Optional.<BackendId>absent()).build();

        mockControl = createControl();

        this.mappingService = mockControl.createMock(MappingService.class);
        this.calendarClient = mockControl.createMock(CalendarClient.class);
        this.calendarClientFactory = mockControl.createMock(CalendarClient.Factory.class);
        this.eventConverter = mockControl.createMock(EventConverter.class);
        this.eventService = mockControl.createMock(EventService.class);
        this.consistencyLogger = mockControl.createMock(ConsistencyEventChangesLogger.class);
        this.eventExtIdFactory = mockControl.createMock(EventExtId.Factory.class);
        this.windowingDao = mockControl.createMock(WindowingDao.class);
        this.clientIdService = mockControl.createMock(ClientIdService.class);
        this.ical4jHelper = mockControl.createMock(Ical4jHelper.class);
        this.ical4jUserFactory = mockControl.createMock(Ical4jUser.Factory.class);
        this.dateService = mockControl.createMock(DateService.class);
        this.opushResourcesHolder = mockControl.createMock(OpushResourcesHolder.class);
        this.folderSnapshotDao = mockControl.createMock(FolderSnapshotDao.class);
        expect(opushResourcesHolder.getAccessToken()).andReturn(token).anyTimes();
        expect(opushResourcesHolder.getHttpClient()).andReturn(httpClient).anyTimes();

        consistencyLogger.log(anyObject(Logger.class), anyObject(EventChanges.class));
        expectLastCall().anyTimes();

        expect(calendarClientFactory.create(anyObject(HttpClient.class))).andReturn(calendarClient).anyTimes();

        this.calendarBackend = new CalendarBackend(mappingService, calendarClientFactory, eventConverter,
                eventService, consistencyLogger, eventExtIdFactory, windowingDao, clientIdService, ical4jHelper,
                ical4jUserFactory, dateService, opushResourcesHolder, folderSnapshotDao);
    }

    @After
    public void teardown() throws IOException {
        httpClient.close();
    }

    @Test
    public void testGetPIMDataType() {
        assertThat(calendarBackend.getPIMDataType()).isEqualTo(PIMDataType.CALENDAR);
    }

    @Test
    public void testGetEstimateSize() throws Exception {
        Date currentDate = DateUtils.getCurrentDate();
        ItemSyncState lastKnownKey = ItemSyncState.builder().syncDate(currentDate)
                .syncKey(new SyncKey("1234567890a")).build();

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        EventChanges eventChanges = expectTwoDeletedAndTwoUpdatedEventChanges(currentDate, 11, 12, 21, 22);

        expect(calendarClient.getSync(token, "test", currentDate)).andReturn(eventChanges).once();

        expect(calendarClient.getUserEmail(token)).andReturn("test").anyTimes();

        expectConvertUpdatedEventsToMSEvents(eventChanges);

        mockControl.replay();

        BodyPreference.Builder bodyPreferenceBuilder = BodyPreference.builder();
        BodyPreference bodyPreference = bodyPreferenceBuilder.build();
        SyncCollectionOptions syncCollectionOptions = SyncCollectionOptions.builder()
                .filterType(FilterType.ALL_ITEMS).bodyPreferences(ImmutableList.of(bodyPreference)).build();

        int itemEstimateSize = calendarBackend.getItemEstimateSize(userDataRequest, lastKnownKey, collectionId,
                syncCollectionOptions);

        mockControl.verify();

        assertThat(itemEstimateSize).isEqualTo(4);
    }

    @Test
    public void testGetChanged() throws Exception {
        Date currentDate = DateUtils.getCurrentDate();
        SyncKey syncKey = new SyncKey("1234567890a");
        ItemSyncState lastKnownKey = ItemSyncState.builder().syncDate(currentDate).syncKey(syncKey).build();

        CollectionId collectionId = CollectionId.of(1);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        EventChanges eventChanges = expectTwoDeletedAndTwoUpdatedEventChanges(currentDate, 11, 12, 21, 22);
        expect(calendarClient.getSync(token, "test", currentDate)).andReturn(eventChanges).once();
        expect(calendarClient.getUserEmail(token)).andReturn("test@test").anyTimes();
        expectConvertUpdatedEventsToMSEvents(eventChanges);

        mockControl.replay();

        BodyPreference.Builder bodyPreferenceBuilder = BodyPreference.builder();
        BodyPreference bodyPreference = bodyPreferenceBuilder.build();
        SyncCollectionOptions syncCollectionOptions = SyncCollectionOptions.builder()
                .filterType(FilterType.ALL_ITEMS).bodyPreferences(ImmutableList.of(bodyPreference)).build();

        WindowingChangesDelta<WindowingEvent> allChanges = calendarBackend.getAllChanges(userDataRequest,
                lastKnownKey, collectionId, syncCollectionOptions);

        mockControl.verify();

        assertThat(allChanges.getDeltaDate()).isEqualTo(currentDate);
        assertThat(allChanges.getWindowingChanges()).isEqualTo(WindowingEventChanges.builder()
                .changes(ImmutableList.of(WindowingEvent.builder().uid(21).build(),
                        WindowingEvent.builder().uid(22).build()))
                .deletions(ImmutableList.of(WindowingEvent.builder().uid(11).build(),
                        WindowingEvent.builder().uid(12).build()))
                .build());
    }

    @Test
    public void testGetAllChangesOnFirstSync() throws Exception {
        Date currentDate = DateUtils.getEpochPlusOneSecondCalendar().getTime();
        SyncKey syncKey = new SyncKey("1234567890a");
        ItemSyncState lastKnownKey = ItemSyncState.builder().syncDate(currentDate).syncKey(syncKey).build();

        CollectionId collectionId = CollectionId.of(1);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        EventChanges eventChanges = expectTwoUpdatedEventChanges(currentDate, 21, 22);
        expect(calendarClient.getFirstSync(token, "test", currentDate)).andReturn(eventChanges).once();
        expect(calendarClient.getUserEmail(token)).andReturn("test@test").anyTimes();
        expectConvertUpdatedEventsToMSEvents(eventChanges);

        mockControl.replay();

        BodyPreference.Builder bodyPreferenceBuilder = BodyPreference.builder();
        BodyPreference bodyPreference = bodyPreferenceBuilder.build();
        SyncCollectionOptions syncCollectionOptions = SyncCollectionOptions.builder()
                .filterType(FilterType.ALL_ITEMS).bodyPreferences(ImmutableList.of(bodyPreference)).build();

        WindowingChangesDelta<WindowingEvent> allChanges = calendarBackend.getAllChanges(userDataRequest,
                lastKnownKey, collectionId, syncCollectionOptions);

        mockControl.verify();

        assertThat(allChanges.getDeltaDate()).isEqualTo(currentDate);
        assertThat(allChanges.getWindowingChanges()).isEqualTo(WindowingEventChanges.builder()
                .changes(ImmutableList.of(WindowingEvent.builder().uid(21).build(),
                        WindowingEvent.builder().uid(22).build()))
                .deletions(ImmutableList.<WindowingEvent>of()).build());
    }

    private EventChanges expectTwoDeletedAndTwoUpdatedEventChanges(Date currentDate, int deletedObmId,
            int deletedObmId2, int createdObmId, int createdObmId2) {
        Set<DeletedEvent> deletedEvents = ImmutableSet.of(
                createDeletedEvent(new EventObmId(deletedObmId), new EventExtId(Strings.valueOf(deletedObmId))),
                createDeletedEvent(new EventObmId(deletedObmId2), new EventExtId(Strings.valueOf(deletedObmId2))));
        List<Event> updated = new ArrayList<Event>();
        updated.add(createEvent(createdObmId));
        updated.add(createEvent(createdObmId2));

        return EventChanges.builder().lastSync(currentDate).deletes(deletedEvents).updates(updated)
                .participationChanges(ImmutableList.<ParticipationChanges>of()).build();
    }

    private EventChanges expectTwoUpdatedEventChanges(Date currentDate, int createdObmId, int createdObmId2) {
        List<Event> updated = new ArrayList<Event>();
        updated.add(createEvent(createdObmId));
        updated.add(createEvent(createdObmId2));

        return EventChanges.builder().lastSync(currentDate).deletes(ImmutableList.<DeletedEvent>of())
                .updates(updated).participationChanges(ImmutableList.<ParticipationChanges>of()).build();
    }

    private Event createEvent(int obmId) {
        Event event = new Event();
        event.setUid(new EventObmId(obmId));
        return event;
    }

    private DeletedEvent createDeletedEvent(EventObmId eventObmId, EventExtId eventExtId) {
        return DeletedEvent.builder().eventObmId(eventObmId.getObmId()).eventExtId(eventExtId.getExtId()).build();
    }

    private void expectConvertUpdatedEventsToMSEvents(EventChanges eventChanges)
            throws DaoException, ConversionException {
        for (Event event : eventChanges.getUpdated()) {
            expect(eventService.convertEventToMSEvent(userDataRequest, event)).andReturn(null).once();
        }
    }

    @Test
    public void testCreateExternalEventIsAccept() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        ServerId serverId = null;
        String clientId = "3";
        String clientIdHash = "54661110";

        Event oldEvent = null;
        Event creatingEvent = new Event();
        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);
        creatingEvent.setExtId(eventExtId);
        boolean eventIsResolvedAsInternal = false;

        MSEvent creatingMSEvent = new MSEvent();
        creatingMSEvent.setUid(new MSEventUid("abc0123"));
        int createdObmId = 12315648;

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);
        expect(clientIdService.hash(userDataRequest, clientId)).andReturn(clientIdHash);

        expect(eventService.getEventExtIdFor(creatingMSEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(calendarClient.getEventFromExtId(token, "test", eventExtId)).andReturn(oldEvent).once();

        expect(eventConverter.isInternalEvent(oldEvent, eventExtId)).andReturn(eventIsResolvedAsInternal);
        expect(eventConverter.convert(user, oldEvent, creatingMSEvent, eventIsResolvedAsInternal))
                .andReturn(creatingEvent).once();

        expect(calendarClient.createEvent(eq(token), eq("test"), eq(creatingEvent), eq(true),
                anyObject(String.class))).andReturn(new EventObmId(createdObmId));

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                creatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(createdObmId));
    }

    @Test
    public void testCreateInternalEvent() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        String clientId = "13";
        String clientIdHash = "135660464";
        ServerId serverId = null;

        EventExtId eventExtId = new EventExtId(null);
        Event oldEvent = null;
        Event creatingEvent = new Event();
        String generatedEventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId generatedEventExtID = new EventExtId(generatedEventExtIdString);
        boolean eventIsResolvedAsInternal = true;

        MSEvent creatingMSEvent = new MSEvent();
        creatingMSEvent.setUid(new MSEventUid("abc0123"));
        creatingMSEvent.setObmSequence(4);
        int createdObmId = 12315648;

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);
        expect(clientIdService.hash(userDataRequest, clientId)).andReturn(clientIdHash);

        expect(eventService.getEventExtIdFor(creatingMSEvent.getUid(), device)).andReturn(null);
        expect(eventConverter.isInternalEvent(oldEvent, eventExtId)).andReturn(eventIsResolvedAsInternal);
        expect(eventConverter.convert(user, oldEvent, creatingMSEvent, eventIsResolvedAsInternal))
                .andReturn(creatingEvent).once();

        expect(eventExtIdFactory.generate()).andReturn(generatedEventExtID);
        eventService.trackEventExtIdMSEventUidTranslation(generatedEventExtIdString, creatingMSEvent.getUid(),
                device);
        expectLastCall();

        expect(calendarClient.createEvent(eq(token), eq("test"), eq(creatingEvent), eq(true),
                anyObject(String.class))).andReturn(new EventObmId(createdObmId));

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                creatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(createdObmId));
    }

    @Test
    public void testUpdateOwnInternalEvent() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        int itemId = 123;
        ServerId serverId = collectionId.serverId(itemId);
        String clientId = null;

        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);

        Event oldEvent = new Event();
        oldEvent.setUid(new EventObmId(itemId));
        oldEvent.setInternalEvent(true);
        oldEvent.setOwner("test");
        oldEvent.setOwnerEmail("test@test");
        Event updatingEvent = new Event();
        updatingEvent.setUid(new EventObmId(itemId));
        Event updatedEvent = new Event();
        updatedEvent.setUid(new EventObmId(itemId));
        boolean eventIsResolvedAsInternal = true;

        MSEvent updatingMSEvent = new MSEvent();
        updatingMSEvent.setUid(new MSEventUid("abc0123"));

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);
        expect(eventService.getEventExtIdFor(updatingMSEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(calendarClient.getEventFromId(token, "test", new EventObmId(itemId))).andReturn(oldEvent).once();

        expect(eventConverter.isInternalEvent(oldEvent, eventExtId)).andReturn(eventIsResolvedAsInternal);
        expect(eventConverter.convert(user, oldEvent, updatingMSEvent, eventIsResolvedAsInternal))
                .andReturn(updatingEvent).once();

        expect(calendarClient.modifyEvent(token, "test", updatingEvent, true, true)).andReturn(updatedEvent);

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                updatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(itemId));
    }

    @Test
    public void testUpdateNotOwnInternalEventWithServerId() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        int itemId = 123;
        ServerId serverId = collectionId.serverId(itemId);
        String clientId = null;

        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);

        Event oldEvent = new Event();
        oldEvent.setUid(new EventObmId(itemId));
        oldEvent.setExtId(eventExtId);
        oldEvent.setInternalEvent(true);
        oldEvent.setSequence(4);

        MSEvent updatingMSEvent = new MSEvent();
        updatingMSEvent.setUid(new MSEventUid("abc0123"));

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(updatingMSEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(calendarClient.getEventFromId(token, "test", new EventObmId(itemId))).andReturn(oldEvent).once();

        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(Participation.accepted());
        expect(calendarClient.changeParticipationState(token, "test", eventExtId, Participation.accepted(), 4,
                true)).andReturn(true);

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                updatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(itemId));
    }

    @Test
    public void testUpdateNotOwnInternalEventWithClientId() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        String clientId = "13";
        ServerId serverId = null;

        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);

        Event oldEvent = new Event();
        oldEvent.setUid(new EventObmId(123));
        oldEvent.setExtId(eventExtId);
        oldEvent.setInternalEvent(true);
        oldEvent.setSequence(4);

        MSEvent updatingMSEvent = new MSEvent();
        updatingMSEvent.setUid(new MSEventUid("abc0123"));

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(updatingMSEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(calendarClient.getEventFromExtId(token, "test", eventExtId)).andReturn(oldEvent).once();

        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(Participation.accepted());
        expect(calendarClient.changeParticipationState(token, "test", eventExtId, Participation.accepted(), 4,
                true)).andReturn(true);

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                updatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(oldEvent.getObmId().getObmId()));
    }

    @Test
    public void testUpdateNotOwnExternalEvent() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        int itemId = 123;
        ServerId serverId = collectionId.serverId(itemId);
        String clientId = null;

        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);

        Event oldEvent = new Event();
        oldEvent.setUid(new EventObmId(itemId));
        oldEvent.setInternalEvent(true);
        oldEvent.setOwner("test");
        oldEvent.setOwnerEmail("test@test");
        boolean eventIsResolvedAsInternal = false;

        Event updatingEvent = new Event();

        MSEvent updatingMSEvent = new MSEvent();
        updatingMSEvent.setUid(new MSEventUid("abc0123"));
        updatingMSEvent.setObmSequence(4);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(updatingMSEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(calendarClient.getEventFromId(token, "test", new EventObmId(itemId))).andReturn(oldEvent).once();

        expect(eventConverter.isInternalEvent(oldEvent, eventExtId)).andReturn(eventIsResolvedAsInternal);

        expect(eventConverter.convert(user, oldEvent, updatingMSEvent, eventIsResolvedAsInternal))
                .andReturn(updatingEvent);
        expect(calendarClient.modifyEvent(token, "test", updatingEvent, true, true)).andReturn(updatingEvent);

        mockControl.replay();
        ServerId serverIdFor = calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId,
                updatingMSEvent);
        mockControl.verify();

        assertThat(serverIdFor).isEqualTo(collectionId.serverId(itemId));
    }

    @Test
    public void testDelete() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        int itemId = 3;
        ServerId serverId = collectionId.serverId(itemId);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expectGetAndRemoveEventFromId(itemId);

        mockControl.replay();

        calendarBackend.delete(userDataRequest, collectionId, serverId, true);

        mockControl.verify();
    }

    private void expectGetAndRemoveEventFromId(int itemId)
            throws ServerFault, EventNotFoundException, NotAllowedException {

        EventObmId eventObmId = new EventObmId(itemId);
        Event event = new Event();
        event.setUid(eventObmId);
        expect(calendarClient.getEventFromId(token, "test", eventObmId)).andReturn(event).once();

        calendarClient.removeEventById(token, "test", event.getObmId(), event.getSequence(), true);
        expectLastCall();
    }

    @Test
    public void testHandleMettingResponseExternalCreation() throws Exception {
        String calendar = user.getLogin();
        expect(folderSnapshotDao.get(user, device, PIMDataType.CALENDAR, calendarFolder.getBackendId()))
                .andReturn(calendarFolder);

        boolean isInternal = false;
        String clientId = null;
        MSEventUid msEventUid = new MSEventUid("1");
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(msEventUid);

        EventExtId eventExtId = new EventExtId("1564");
        Event eventFromICS = new Event();
        eventFromICS.setUid(new EventObmId(150));
        eventFromICS.setInternalEvent(isInternal);
        eventFromICS.setExtId(eventExtId);
        eventFromICS.setSequence(12);

        ICalendar iCalendar = icalendar("simpleEvent.ics");
        expect(ical4jUserFactory.createIcal4jUser(user.getEmail(), token.getDomain())).andReturn(null).once();
        expect(ical4jHelper.parseICSEvent(iCalendar.getICalendar(), null, token.getObmId()))
                .andReturn(ImmutableList.of(eventFromICS)).once();

        EventObmId eventCreationDbId = new EventObmId(9);
        expect(calendarClient.getEventFromExtId(token, calendar, eventExtId))
                .andThrow(new EventNotFoundException("Replying to an external invitation"));

        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(Participation.accepted());
        expect(calendarClient.createEvent(token, calendar, eventFromICS, isInternal, clientId))
                .andReturn(eventCreationDbId).once();
        expect(calendarClient.getEventFromId(token, calendar, eventCreationDbId)).andReturn(eventFromICS);

        expect(calendarClient.changeParticipationState(token, calendar, eventExtId, Participation.accepted(),
                eventFromICS.getSequence(), true)).andReturn(true);

        mockControl.replay();
        ServerId serverIdResponse = calendarBackend.handleMeetingResponse(userDataRequest, iCalendar,
                AttendeeStatus.ACCEPT);
        mockControl.verify();

        assertThat(serverIdResponse).isEqualTo(CollectionId.of(1).serverId(150));
    }

    @Test
    public void testHandleMettingResponseExternalUpdate() throws Exception {
        String calendar = user.getLogin();
        expect(folderSnapshotDao.get(user, device, PIMDataType.CALENDAR, calendarFolder.getBackendId()))
                .andReturn(calendarFolder);

        boolean isInternal = false;
        MSEventUid msEventUid = new MSEventUid("1");
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(msEventUid);

        EventExtId eventExtId = new EventExtId("1564");
        Event eventFromDB = new Event();
        eventFromDB.setUid(new EventObmId(100));
        eventFromDB.setSequence(5);
        Event eventFromICS = new Event();
        eventFromICS.setInternalEvent(isInternal);
        eventFromICS.setExtId(eventExtId);
        Event eventWithMergedInfos = new Event();
        eventWithMergedInfos.setUid(eventFromDB.getUid());
        eventWithMergedInfos.setSequence(eventFromDB.getSequence());
        eventWithMergedInfos.setInternalEvent(eventFromICS.isInternalEvent());
        eventWithMergedInfos.setExtId(eventFromICS.getExtId());

        ICalendar iCalendar = icalendar("simpleEvent.ics");
        expect(ical4jUserFactory.createIcal4jUser(user.getEmail(), token.getDomain())).andReturn(null).once();
        expect(ical4jHelper.parseICSEvent(iCalendar.getICalendar(), null, token.getObmId()))
                .andReturn(ImmutableList.of(eventFromICS)).once();

        expect(calendarClient.getEventFromExtId(token, calendar, eventExtId)).andReturn(eventFromDB);
        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(Participation.accepted());
        expect(calendarClient.modifyEvent(token, calendar, eventFromICS, true, false))
                .andReturn(eventWithMergedInfos);

        expect(calendarClient.changeParticipationState(token, calendar, eventExtId, Participation.accepted(),
                eventFromDB.getSequence(), true)).andReturn(true);

        mockControl.replay();
        ServerId serverIdResponse = calendarBackend.handleMeetingResponse(userDataRequest, iCalendar,
                AttendeeStatus.ACCEPT);
        mockControl.verify();

        assertThat(serverIdResponse).isEqualTo(CollectionId.of(1).serverId(100));
    }

    @Test
    public void testHandleMettingResponseOnInternalEvent() throws Exception {
        String calendar = user.getLogin();
        expect(folderSnapshotDao.get(user, device, PIMDataType.CALENDAR, calendarFolder.getBackendId()))
                .andReturn(calendarFolder);

        EventExtId eventExtId = new EventExtId("145");
        Event iCSEvent = new Event();
        iCSEvent.setInternalEvent(true);
        iCSEvent.setExtId(eventExtId);

        ICalendar iCalendar = icalendar("simpleEvent.ics");

        expect(ical4jUserFactory.createIcal4jUser(user.getEmail(), token.getDomain())).andReturn(null).once();
        expect(ical4jHelper.parseICSEvent(iCalendar.getICalendar(), null, token.getObmId()))
                .andReturn(ImmutableList.of(iCSEvent)).once();

        Event oBMEvent = new Event();
        oBMEvent.setUid(new EventObmId(180));
        oBMEvent.setInternalEvent(true);
        oBMEvent.setExtId(eventExtId);

        expect(calendarClient.getEventFromExtId(token, calendar, eventExtId)).andReturn(oBMEvent).once();

        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(Participation.accepted());
        expect(calendarClient.changeParticipationState(token, calendar, eventExtId, Participation.accepted(), 0,
                true)).andReturn(true);

        mockControl.replay();

        ServerId serverIdResponse = calendarBackend.handleMeetingResponse(userDataRequest, iCalendar,
                AttendeeStatus.ACCEPT);

        mockControl.verify();
        assertThat(serverIdResponse).isEqualTo(CollectionId.of(1).serverId(180));
    }

    private void expectGetAndModifyEvent(EventExtId eventExtId, Event event)
            throws ServerFault, EventNotFoundException, NotAllowedException {

        expect(calendarClient.getEventFromExtId(token, user.getLogin(), eventExtId)).andReturn(event).once();

        expect(calendarClient.modifyEvent(token, user.getLogin(), event, true, false)).andReturn(event).once();
    }

    private void expectEventConvertion(Event event) throws ConversionException {
        expect(eventConverter.convert(eq(user), eq(event), anyObject(MSEvent.class), eq(false))).andReturn(event)
                .once();
    }

    @Test
    public void testFetch() throws Exception {
        SyncCollectionOptions options = null;
        ItemSyncState state = null;
        SyncKey newSyncKey = new SyncKey("132");
        CollectionId collectionId = CollectionId.of(1);
        Integer itemId1 = 1;
        Integer itemId2 = 2;
        ServerId serverId1 = collectionId.serverId(itemId1);
        ServerId serverId2 = collectionId.serverId(itemId2);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        Event event1 = expectGetEventFromId(itemId1);
        Event event2 = expectGetEventFromId(itemId2);

        expectConvertEventToMSEvent(serverId1, event1);
        expectConvertEventToMSEvent(serverId2, event2);

        mockControl.replay();

        List<ServerId> itemIds = ImmutableList.of(serverId1, serverId2);

        List<ItemChange> itemChanges = calendarBackend.fetch(userDataRequest, collectionId, itemIds, options, state,
                newSyncKey);

        mockControl.verify();

        assertThat(itemChanges).hasSize(2);
    }

    private Event expectGetEventFromId(Integer itemId)
            throws ServerFault, EventNotFoundException, NotAllowedException {

        EventObmId eventObmId = new EventObmId(itemId);
        Event event = new Event();
        event.setUid(eventObmId);
        expect(calendarClient.getEventFromId(token, "test", eventObmId)).andReturn(event).once();

        return event;
    }

    private void expectConvertEventToMSEvent(ServerId serverId1, Event event)
            throws DaoException, ConversionException {

        MSEvent msEvent = new MSEvent();
        msEvent.setUid(new MSEventUid(String.valueOf(serverId1.getItemId())));
        expect(eventService.convertEventToMSEvent(userDataRequest, event)).andReturn(msEvent).once();
    }

    @Test
    public void appendChangesToBuilderMustNotTransformDeclinedEventIntoRemoved()
            throws ServerFault, DaoException, ConversionException {
        EventObmId eventObmId = new EventObmId(132453);
        EventExtId eventExtId = new EventExtId("event-ext-id-bla-bla");
        Attendee attendee = UserAttendee.builder().email(user.getLoginAtDomain())
                .participation(Participation.declined()).build();
        Event event = new Event();

        event.setExtId(eventExtId);
        event.setUid(eventObmId);
        event.addAttendee(attendee);

        DeletedEvent deletedEvent = createDeletedEvent(eventObmId, eventExtId);
        EventChanges eventChanges = EventChanges.builder().deletes(Arrays.asList(deletedEvent))
                .updates(Arrays.asList(event)).lastSync(org.obm.DateUtils.date("2012-10-10")).build();

        expect(calendarClient.getUserEmail(token)).andReturn("test@test").anyTimes();

        mockControl.replay();

        WindowingEventChanges.Builder builder = WindowingEventChanges.builder();
        calendarBackend.appendChangesToBuilder(userDataRequest, token, eventChanges, builder);

        mockControl.verify();

        assertThat(builder.build().deletions()).hasSize(1)
                .containsOnly(WindowingEvent.builder().uid(132453).build());
    }

    @Test
    public void testGetAllChangesThrowsHierarchyChangedException() throws Exception {
        Date currentDate = DateUtils.getCurrentDate();
        SyncKey syncKey = new SyncKey("1234567890a");
        ItemSyncState lastKnownKey = ItemSyncState.builder().syncDate(currentDate).syncKey(syncKey).build();

        CollectionId collectionId = CollectionId.of(1);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(calendarClient.getSync(token, "test", currentDate)).andThrow(new NotAllowedException("Not Allowed"))
                .once();

        mockControl.replay();

        BodyPreference.Builder bodyPreferenceBuilder = BodyPreference.builder();
        BodyPreference bodyPreference = bodyPreferenceBuilder.build();
        SyncCollectionOptions syncCollectionOptions = SyncCollectionOptions.builder()
                .filterType(FilterType.ALL_ITEMS).bodyPreferences(ImmutableList.of(bodyPreference)).build();

        WindowingChangesDelta<WindowingEvent> allChanges = calendarBackend.getAllChanges(userDataRequest,
                lastKnownKey, collectionId, syncCollectionOptions);

        mockControl.verify();

        assertThat(allChanges.getDeltaDate()).isEqualTo(currentDate);
        assertThat(allChanges.getWindowingChanges()).isEqualTo(WindowingEventChanges.builder()
                .changes(ImmutableList.<WindowingEvent>of()).deletions(ImmutableList.<WindowingEvent>of()).build());
    }

    @Test(expected = ItemNotFoundException.class)
    public void testCreateOrUpdateThrowsHierarchyChangedException() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        String itemId = "2";
        ServerId serverId = collectionId.serverId(2);
        String clientId = "3";
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(new MSEventUid("abc0123"));
        String eventExtIdString = "00000123-0456-0789-0012-000000000345";

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(msEvent.getUid(), device)).andReturn(eventExtIdString);

        expect(mappingService.getServerIdFor(collectionId, itemId)).andReturn(serverId).once();

        expect(calendarClient.getEventFromId(token, user.getLogin(), new EventObmId(itemId)))
                .andThrow(new NotAllowedException("Not allowed")).once();

        mockControl.replay();

        calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId, msEvent);
    }

    @Test(expected = ItemNotFoundException.class)
    public void testCreateOrUpdateThrowsHierarchyChangedExceptionOnUpdateCalendarEntity() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        int itemId = 2;
        ServerId serverId = collectionId.serverId(itemId);
        String clientId = "3";
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(new MSEventUid("abc0123"));
        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(msEvent.getUid(), device)).andReturn(eventExtIdString);
        expect(mappingService.getServerIdFor(collectionId, String.valueOf(itemId))).andReturn(serverId).once();

        Event event = new Event();
        event.setUid(new EventObmId(itemId));
        event.setOwner("test");
        event.setOwnerEmail("test@test");
        expect(calendarClient.getEventFromId(token, user.getLogin(), new EventObmId(itemId))).andReturn(event)
                .once();

        expect(eventConverter.isInternalEvent(event, eventExtId)).andReturn(false);

        expect(calendarClient.modifyEvent(token, "test", event, true, true))
                .andThrow(new NotAllowedException("Not allowed")).once();

        expectEventConvertion(event);

        mockControl.replay();

        calendarBackend.createOrUpdate(userDataRequest, collectionId, serverId, clientId, msEvent);
    }

    @Test(expected = ItemNotFoundException.class)
    public void testCreateOrUpdateThrowsHierarchyChangedExceptionOnCreateCalendarEntity() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        String clientId = "3";
        String clientIdHash = "35466464106456405";
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(new MSEventUid("abc0123"));
        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);
        Event event = new Event();

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);
        expect(clientIdService.hash(userDataRequest, clientId)).andReturn(clientIdHash);

        expect(eventService.getEventExtIdFor(msEvent.getUid(), device)).andReturn(eventExtIdString).once();
        expect(calendarClient.getEventFromExtId(token, user.getLogin(), eventExtId)).andReturn(null).once();

        expect(calendarClient.createEvent(eq(token), eq("test"), eq(event), eq(true), anyObject(String.class)))
                .andThrow(new NotAllowedException("Not allowed")).once();

        expect(eventConverter.isInternalEvent(null, eventExtId)).andReturn(false).once();

        expect(eventConverter.convert(user, null, msEvent, false)).andReturn(event).once();

        eventService.trackEventExtIdMSEventUidTranslation(eventExtIdString, msEvent.getUid(), device);
        expectLastCall().once();

        mockControl.replay();

        calendarBackend.createOrUpdate(userDataRequest, collectionId, null, clientId, msEvent);
    }

    @Test(expected = ItemNotFoundException.class)
    public void testCreateOrUpdateThrowsHierarchyChangedExceptionOnEventAlreadyExist() throws Exception {
        CollectionId collectionId = CollectionId.of(1);
        String clientId = "3";
        String clientIdHash = "35466464106456405";
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(new MSEventUid("abc0123"));
        String eventExtIdString = "00000123-0456-0789-0012-000000000345";
        EventExtId eventExtId = new EventExtId(eventExtIdString);
        Event event = new Event();

        expect(eventService.getEventExtIdFor(msEvent.getUid(), device)).andReturn(eventExtIdString).once();
        expect(clientIdService.hash(userDataRequest, clientId)).andReturn(clientIdHash);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        expect(eventService.getEventExtIdFor(msEvent.getUid(), device)).andReturn(eventExtIdString).once();

        expect(calendarClient.getEventFromExtId(token, user.getLogin(), eventExtId)).andReturn(null).once();

        expect(calendarClient.createEvent(eq(token), eq("test"), eq(event), eq(true), anyObject(String.class)))
                .andThrow(new EventAlreadyExistException("Already exist")).once();

        expect(calendarClient.getEventObmIdFromExtId(token, "test", eventExtId))
                .andThrow(new NotAllowedException("Not allowed")).once();

        expect(eventConverter.isInternalEvent(null, eventExtId)).andReturn(false).once();

        expect(eventConverter.convert(user, null, msEvent, false)).andReturn(event).once();

        eventService.trackEventExtIdMSEventUidTranslation(eventExtIdString, msEvent.getUid(), device);
        expectLastCall().once();

        mockControl.replay();

        calendarBackend.createOrUpdate(userDataRequest, collectionId, null, clientId, msEvent);
    }

    @Test(expected = ItemNotFoundException.class)
    public void testHandleMettingResponseThrowsHierarchyChangedException() throws Exception {
        String calendar = user.getLogin();

        String eventExtIdString = "1";
        MSEventUid msEventUid = new MSEventUid(eventExtIdString);
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(msEventUid);

        EventExtId eventExtId = new EventExtId(eventExtIdString);
        expect(eventService.getEventExtIdFor(msEventUid, device)).andReturn(eventExtIdString).once();

        Event event = new Event();
        event.setUid(new EventObmId(1));
        event.setExtId(eventExtId);

        ICalendar iCalendar = icalendar("simpleEvent.ics");

        expect(ical4jUserFactory.createIcal4jUser(user.getEmail(), token.getDomain())).andReturn(null).once();
        expect(ical4jHelper.parseICSEvent(iCalendar.getICalendar(), null, token.getObmId()))
                .andReturn(ImmutableList.of(event)).once();

        expect(calendarClient.getEventFromExtId(token, calendar, eventExtId)).andReturn(event).once();

        expect(eventConverter.isInternalEvent(event, false)).andReturn(false).once();
        expectGetAndModifyEvent(eventExtId, event);
        expect(calendarClient.changeParticipationState(token, calendar, eventExtId, null, 0, true))
                .andThrow(new NotAllowedException("Not allowed")).once();

        expectEventConvertion(event);
        expect(eventConverter.getParticipation(AttendeeStatus.ACCEPT)).andReturn(null).once();

        ServerId serverId = CollectionId.of(1).serverId(123);
        expect(folderSnapshotDao.get(user, device, PIMDataType.CALENDAR, calendarFolder.getBackendId()))
                .andReturn(calendarFolder);
        expect(mappingService.getServerIdFor(CollectionId.of(1), eventExtIdString)).andReturn(serverId);

        mockControl.replay();

        calendarBackend.handleMeetingResponse(userDataRequest, iCalendar, AttendeeStatus.ACCEPT);
    }

    @Test(expected = ICalendarConverterException.class)
    public void testHandleMettingResponseErrorWhenParsingICS() throws Exception {
        MSEventUid msEventUid = new MSEventUid("1");
        MSEvent msEvent = new MSEvent();
        msEvent.setUid(msEventUid);

        EventExtId eventExtId = new EventExtId("1");

        Event event = new Event();
        event.setUid(new EventObmId(1));
        event.setExtId(eventExtId);

        ICalendar iCalendar = icalendar("simpleEvent.ics");

        expect(ical4jUserFactory.createIcal4jUser(user.getEmail(), token.getDomain())).andReturn(null).once();
        expect(ical4jHelper.parseICSEvent(iCalendar.getICalendar(), null, token.getObmId()))
                .andThrow(new ParserException(1));

        expect(folderSnapshotDao.get(user, device, PIMDataType.CALENDAR, calendarFolder.getBackendId()))
                .andReturn(calendarFolder);

        mockControl.replay();

        try {
            calendarBackend.handleMeetingResponse(userDataRequest, iCalendar, AttendeeStatus.ACCEPT);
        } catch (ICalendarConverterException e) {
            mockControl.verify();
            throw e;
        }
    }

    @Test
    public void testFetchThrowsHierarchyChangedException() throws Exception {
        SyncCollectionOptions options = null;
        ItemSyncState state = null;
        SyncKey newSyncKey = new SyncKey("132");
        CollectionId collectionId = CollectionId.of(1);
        Integer itemId = 1;
        ServerId serverId = collectionId.serverId(itemId);

        expect(folderSnapshotDao.get(user, device, collectionId)).andReturn(calendarFolder);

        EventObmId eventObmId = new EventObmId(itemId);
        Event event = new Event();
        event.setUid(eventObmId);
        expect(calendarClient.getEventFromId(token, "test", eventObmId))
                .andThrow(new NotAllowedException("Not allowed")).once();

        mockControl.replay();

        List<ServerId> itemIds = ImmutableList.of(serverId);

        calendarBackend.fetch(userDataRequest, collectionId, itemIds, options, state, newSyncKey);
        mockControl.verify();
    }

    @Test
    public void testIsParticipationChangeUpdateWhenOldEventIsNull() {
        Event oldEvent = null;
        CalendarPath calendarPath = CalendarPath.of("calendarName");
        assertThat(calendarBackend.isParticipationChangeUpdate(calendarPath, oldEvent)).isFalse();
    }

    @Test
    public void testIsParticipationChangeUpdateWhenOldEventNotBelongsToCalendar() {
        CalendarPath calendarPath = CalendarPath.of("calendarName");
        Event oldEvent = new Event();
        oldEvent.setOwner("otherCalendarName");

        assertThat(calendarBackend.isParticipationChangeUpdate(calendarPath, oldEvent)).isTrue();
    }

    @Test
    public void testIsParticipationChangeUpdateWhenOldEventBelongsToCalendar() {
        CalendarPath calendarPath = CalendarPath.of("calendarName");
        Event oldEvent = new Event();
        oldEvent.setOwner("calendarName");

        assertThat(calendarBackend.isParticipationChangeUpdate(calendarPath, oldEvent)).isFalse();
    }

    private ICalendar icalendar(String filename) throws IOException, ParserException {
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("icsFiles/" + filename);
        if (in == null) {
            Assert.fail("Cannot load " + filename);
        }
        return ICalendar.builder().inputStream(in).build();
    }

    @Test(expected = NullPointerException.class)
    public void testAppendOrganizerIfNoneOnNullList() {
        Iterable<Event> events = null;
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        try {
            calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        } catch (Exception e) {
            mockControl.verify();
            throw e;
        }
    }

    @Test
    public void testAppendOrganizerIfNoneOnEmptyList() {
        Iterable<Event> events = ImmutableList.of();
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        Iterable<Event> changedEvents = calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        mockControl.verify();

        assertThat(changedEvents).isEmpty();
    }

    @Test(expected = NullPointerException.class)
    public void testAppendOrganizerIfNoneOnNullICalendarEvent() {
        Iterable<Event> events = ImmutableList.of();
        ICalendarEvent iCalendarEvent = null;

        mockControl.replay();
        try {
            calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        } catch (Exception e) {
            mockControl.verify();
            throw e;
        }
    }

    @Test
    public void testAppendOrganizerIfNoneOnOneEventWithoutAttendee() {
        Event event = new Event();
        Iterable<Event> events = ImmutableList.of(event);
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        Iterable<Event> changedEvents = calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        mockControl.verify();

        assertThat(changedEvents).hasSize(1);
        assertThat(Iterables.getOnlyElement(changedEvents).getAttendees())
                .containsOnly(ContactAttendee.builder().asOrganizer().email("organizer@domain").build());
    }

    @Test
    public void testAppendOrganizerIfNoneOnOneEventWithoutOrganizer() {
        Event event = new Event();
        event.addAttendee(ContactAttendee.builder().asAttendee().email("attendee@domain").build());
        Iterable<Event> events = ImmutableList.of(event);
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        Iterable<Event> changedEvents = calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        mockControl.verify();

        assertThat(changedEvents).hasSize(1);
        assertThat(Iterables.getOnlyElement(changedEvents).getAttendees()).containsOnly(
                ContactAttendee.builder().asAttendee().email("attendee@domain").build(),
                ContactAttendee.builder().asOrganizer().email("organizer@domain").build());
    }

    @Test
    public void testAppendOrganizerIfNoneOnOneEventWithOrganizer() {
        Event event = new Event();
        event.addAttendee(ContactAttendee.builder().asAttendee().email("attendee@domain").build());
        event.addAttendee(ContactAttendee.builder().asOrganizer().email("initialorganizer@domain").build());
        Iterable<Event> events = ImmutableList.of(event);
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        Iterable<Event> changedEvents = calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        mockControl.verify();

        assertThat(changedEvents).hasSize(1);
        assertThat(Iterables.getOnlyElement(changedEvents).getAttendees()).containsOnly(
                ContactAttendee.builder().asAttendee().email("attendee@domain").build(),
                ContactAttendee.builder().asOrganizer().email("initialorganizer@domain").build());
    }

    @Test
    public void testAppendOrganizerIfNoneOnTwoDifferentEvent() {
        Event event = new Event();
        event.setTitle("one");
        event.addAttendee(ContactAttendee.builder().asAttendee().email("attendee@domain").build());
        Event event2 = new Event();
        event2.setTitle("two");
        event2.addAttendee(ContactAttendee.builder().asOrganizer().email("initialorganizer@domain").build());

        Iterable<Event> events = ImmutableList.of(event, event2);
        ICalendarEvent iCalendarEvent = mockControl.createMock(ICalendarEvent.class);
        expect(iCalendarEvent.organizer()).andReturn("organizer@domain");

        mockControl.replay();
        Iterable<Event> changedEvents = calendarBackend.appendOrganizerIfNone(events, iCalendarEvent);
        mockControl.verify();

        assertThat(changedEvents).hasSize(2);
        assertThat(findEventByTitle(changedEvents, "one").getAttendees()).containsOnly(
                ContactAttendee.builder().asAttendee().email("attendee@domain").build(),
                ContactAttendee.builder().asOrganizer().email("organizer@domain").build());
        assertThat(findEventByTitle(changedEvents, "two").getAttendees())
                .containsOnly(ContactAttendee.builder().asOrganizer().email("initialorganizer@domain").build());
    }

    private Event findEventByTitle(Iterable<Event> changedEvents, final String title) {
        return Iterables.find(changedEvents, new Predicate<Event>() {
            @Override
            public boolean apply(Event input) {
                return input.getTitle().equals(title);
            }
        });
    }

    @Test(expected = IllegalArgumentException.class)
    public void testBelongsToCalendarWhenNullCalendar() {
        Event event = new Event();
        calendarBackend.belongsToCalendar(event, null);
    }

    @Test(expected = IllegalArgumentException.class)
    public void testBelongsToCalendarWhenEmptyCalendar() {
        Event event = new Event();
        calendarBackend.belongsToCalendar(event, "");
    }

    @Test
    public void testBelongsToCalendarWhenOwnerIsNull() {
        Event event = new Event();
        event.setOwner(null);

        assertThat(calendarBackend.belongsToCalendar(event, "owner")).isFalse();
    }

    @Test
    public void testBelongsToCalendarWhenOwnerIsEmpty() {
        Event event = new Event();
        event.setOwner("");

        assertThat(calendarBackend.belongsToCalendar(event, "owner")).isFalse();
    }

    @Test
    public void testBelongsToCalendarWhenOwnerIsDifferent() {
        Event event = new Event();
        event.setOwner("owner");

        assertThat(calendarBackend.belongsToCalendar(event, "user@email.com")).isFalse();
    }

    @Test
    public void testBelongsToCalendarWhenCalendarEqualsOwner() {
        Event event = new Event();
        event.setOwner("owner");

        assertThat(calendarBackend.belongsToCalendar(event, "owner")).isTrue();
    }

    @Test
    public void testBelongsToCalendarWhenCalendarEqualsOwnerDifferentCase() {
        Event event = new Event();
        event.setOwner("OWNER");

        assertThat(calendarBackend.belongsToCalendar(event, "owner")).isTrue();
    }

    @Test
    public void testBelongsToCalendarWhenCalendarNotEqualsOwnerButCreator() {
        Event event = new Event();
        event.setOwner("owner");
        event.setCreatorEmail("creator@email.com");

        assertThat(calendarBackend.belongsToCalendar(event, "creator@email.com")).isFalse();
    }

    @Test
    public void currentFoldersShouldReturnOnlyDefaultCalendar() {
        mockControl.replay();
        BackendFolders currentFolders = calendarBackend.getBackendFolders(userDataRequest);
        mockControl.verify();

        assertThat(currentFolders).hasSize(1);
        assertThat(currentFolders.iterator().next()).isEqualTo(BackendFolder.builder()
                .backendId(CalendarPath.of("test")).displayName("test calendar")
                .folderType(FolderType.DEFAULT_CALENDAR_FOLDER).parentId(Optional.<BackendId>absent()).build());
    }
}