mirror of
https://github.com/tutao/tutanota.git
synced 2025-12-07 13:49:47 +00:00
WIP: Figuring out tests for CalendarModel
This commit is contained in:
parent
808250f189
commit
ebee33883f
5 changed files with 171 additions and 200 deletions
|
|
@ -319,11 +319,6 @@ export class CalendarModel {
|
|||
newEvent.startTime.getTime() !== existingEvent.startTime.getTime() ||
|
||||
(await didLongStateChange(newEvent, existingEvent, zone))
|
||||
) {
|
||||
// FIXME come back when deciding about strategies and etc
|
||||
// if (this.isDefaultCalendar(groupRoot._id) && isSameId(groupRoot.pendingEvents?.list ?? null, listIdPart(existingEvent._id))) {
|
||||
// await this.createPendingEvent(newEvent, groupRoot, newAlarms, existingEvent)
|
||||
// }
|
||||
|
||||
await this.doCreate(newEvent, zone, groupRoot, newAlarms, existingEvent)
|
||||
|
||||
// We should reload the instance here because session key and permissions are updated when we recreate event.
|
||||
|
|
@ -687,7 +682,6 @@ export class CalendarModel {
|
|||
const calendarInfos = await this.loadCalendarInfos(progressMonitor)
|
||||
|
||||
const firstPrivateCalendar = findFirstPrivateCalendar(calendarInfos)
|
||||
const userSettingsGroupRoot = this.logins.getUserController().userSettingsGroupRoot
|
||||
const isInternalUser = this.logins.isInternalUserLoggedIn()
|
||||
|
||||
if (!isInternalUser || firstPrivateCalendar) {
|
||||
|
|
@ -746,29 +740,12 @@ export class CalendarModel {
|
|||
// Reset permissions because server will assign them
|
||||
downcast(event)._permissions = null
|
||||
event._ownerGroup = groupRoot._id
|
||||
|
||||
event.pendingInvitation = existingEvent ? existingEvent.pendingInvitation : null
|
||||
|
||||
return await this.calendarFacade.saveCalendarEvent(event, alarmInfos, existingEvent ?? null).then(this.requestWidgetRefresh)
|
||||
}
|
||||
|
||||
private async createPendingEvent(event: CalendarEvent, groupRoot: CalendarGroupRoot, existingEvent: CalendarEvent | null = null): Promise<void> {
|
||||
// If the event was copied it might still carry some fields for re-encryption. We can't reuse them.
|
||||
removeTechnicalFields(event)
|
||||
|
||||
const { assignEventId } = await import("../../../common/calendar/date/CalendarUtils")
|
||||
assignEventId(event, getTimeZone(), groupRoot)
|
||||
|
||||
// Reset ownerEncSessionKey because it cannot be set for new entity, it will be assigned by the CryptoFacade
|
||||
event._ownerEncSessionKey = null
|
||||
if (event.repeatRule != null) {
|
||||
event.repeatRule.excludedDates = event.repeatRule.excludedDates.map(({ date }) => createDateWrapper({ date }))
|
||||
}
|
||||
|
||||
// Reset permissions because server will assign them
|
||||
downcast(event)._permissions = null
|
||||
event._ownerGroup = groupRoot._id
|
||||
|
||||
return await this.calendarFacade.saveCalendarEvent(event, [], existingEvent)
|
||||
}
|
||||
|
||||
async deleteEvent(event: CalendarEvent): Promise<void> {
|
||||
return await this.entityClient.erase(event).then(this.requestWidgetRefresh)
|
||||
}
|
||||
|
|
@ -973,7 +950,7 @@ export class CalendarModel {
|
|||
|
||||
if (dbEvents == null) {
|
||||
// Create pending events when processing calendar invites.
|
||||
const calendarInfos = await this.loadOrCreateCalendarInfo(this.readProgressMonitor.next().value)
|
||||
const calendarInfos = await this.getCalendarInfos()
|
||||
const firstCalendar = findFirstPrivateCalendar(calendarInfos)
|
||||
if (firstCalendar == null) {
|
||||
throw new Error("Missing private calendar")
|
||||
|
|
@ -1004,9 +981,10 @@ export class CalendarModel {
|
|||
const calendarEvent: CalendarEvent = {
|
||||
...parsed.event,
|
||||
sender,
|
||||
pendingInvitation: true,
|
||||
}
|
||||
|
||||
return this.createPendingEvent(calendarEvent, destinationCalendarGroupRoot)
|
||||
return this.doCreate(calendarEvent, this.zone, destinationCalendarGroupRoot, [])
|
||||
})
|
||||
|
||||
await Promise.all(eventsPromises)
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ export class CalendarFacade {
|
|||
flags: {
|
||||
hasAlarms: hasAlarmsForTheUser(this.userFacade.getLoggedInUser(), e),
|
||||
isAlteredInstance: e.recurrenceId != null,
|
||||
isGhost: !!e.pendingInvitation,
|
||||
},
|
||||
color,
|
||||
}))
|
||||
|
|
@ -163,6 +164,7 @@ export class CalendarFacade {
|
|||
flags: {
|
||||
hasAlarms: hasAlarmsForTheUser(this.userFacade.getLoggedInUser(), e),
|
||||
isAlteredInstance: e.recurrenceId != null,
|
||||
isGhost: !!e.pendingInvitation,
|
||||
},
|
||||
color,
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -10,12 +10,7 @@ import Stream from "mithril/stream"
|
|||
import stream from "mithril/stream"
|
||||
import { EntityUpdateData } from "../../../src/common/api/common/utils/EntityUpdateUtils"
|
||||
import { EndType, OperationType, RepeatPeriod } from "../../../src/common/api/common/TutanotaConstants"
|
||||
import {
|
||||
CalendarEventsRefTypeRef,
|
||||
CalendarEventTypeRef,
|
||||
CalendarGroupRootTypeRef,
|
||||
CalendarRepeatRuleTypeRef,
|
||||
} from "../../../src/common/api/entities/tutanota/TypeRefs"
|
||||
import { CalendarEventTypeRef, CalendarGroupRootTypeRef, CalendarRepeatRuleTypeRef } from "../../../src/common/api/entities/tutanota/TypeRefs"
|
||||
import { EntityClient } from "../../../src/common/api/common/EntityClient"
|
||||
import { createTestEntity } from "../TestUtils"
|
||||
import { CalendarFacade } from "../../../src/common/api/worker/facades/lazy/CalendarFacade"
|
||||
|
|
@ -28,7 +23,6 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const initialCalendarGroupId = "initialCalendarGroupId"
|
||||
const userGroupId = "userGroupId"
|
||||
const shotEventsListId = "shotEventsListId"
|
||||
const pendingEventsListId = "pendingEventsListId"
|
||||
const timezone = getTimeZone()
|
||||
|
||||
const eventControllerMock: EventController = object()
|
||||
|
|
@ -39,18 +33,20 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
|
||||
o.spec("createOrUpdateCalendarEvent", function () {
|
||||
let userControllerMock: UserController
|
||||
let calendarFacade: CalendarFacade
|
||||
let calendarFacadeMock: CalendarFacade
|
||||
let loginControllerMock: LoginController
|
||||
let calendarModelMock: CalendarModel
|
||||
let entityClientMock: EntityClient
|
||||
let calendarInfosStreamMock: Stream<ReadonlyMap<Id, CalendarInfo>>
|
||||
let calendarEventsRepositoryMock: CalendarEventsRepository
|
||||
|
||||
let eventsRepository: CalendarEventsRepository
|
||||
|
||||
let initialCalendarInfos: Map<string, CalendarInfo>
|
||||
let initialCalendarMembership: GroupMembership
|
||||
|
||||
o.beforeEach(function () {
|
||||
userControllerMock = object()
|
||||
calendarFacade = object()
|
||||
calendarFacadeMock = object()
|
||||
loginControllerMock = object()
|
||||
calendarModelMock = object()
|
||||
entityClientMock = object()
|
||||
|
|
@ -72,14 +68,13 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const calendarInfo: CalendarInfo = object()
|
||||
calendarInfo.groupRoot = createTestEntity(CalendarGroupRootTypeRef, {
|
||||
shortEvents: shotEventsListId,
|
||||
pendingEvents: createTestEntity(CalendarEventsRefTypeRef, { list: pendingEventsListId }),
|
||||
})
|
||||
initialCalendarInfos = new Map([[initialCalendarGroupId, calendarInfo]])
|
||||
when(calendarModelMock.getCalendarInfos()).thenResolve(initialCalendarInfos)
|
||||
|
||||
calendarEventsRepositoryMock = new CalendarEventsRepository(
|
||||
eventsRepository = new CalendarEventsRepository(
|
||||
calendarModelMock,
|
||||
calendarFacade,
|
||||
calendarFacadeMock,
|
||||
timezone,
|
||||
entityClientMock,
|
||||
eventControllerMock,
|
||||
|
|
@ -104,11 +99,11 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const dateFarFromEvent = new Date(2025, 11, 13)
|
||||
const startOfDay = getStartOfDay(dateFarFromEvent).getTime()
|
||||
const daysToEventsMock: DaysToEvents = new Map([[startOfDay, []]])
|
||||
when(
|
||||
calendarFacade.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything()),
|
||||
).thenResolve(daysToEventsMock)
|
||||
when(calendarFacadeMock.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything())).thenResolve(
|
||||
daysToEventsMock,
|
||||
)
|
||||
// Making sure EventRepository.daysToEvents and EventRepository.loadedMonths is initialized
|
||||
await calendarEventsRepositoryMock.loadMonthsIfNeeded([dateFarFromEvent], stream(false), null)
|
||||
await eventsRepository.loadMonthsIfNeeded([dateFarFromEvent], stream(false), null)
|
||||
|
||||
// Act
|
||||
const calendarEventUpdate: EntityUpdateData = object()
|
||||
|
|
@ -119,7 +114,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
|
||||
// Assert
|
||||
const eventStartOfDay = getStartOfDay(eventStartDate).getTime()
|
||||
const daysToEvents = calendarEventsRepositoryMock.getEventsForMonths()()
|
||||
const daysToEvents = eventsRepository.getEventsForMonths()()
|
||||
// We expect only the initial dateFarFromEvent to be loaded
|
||||
o.check(daysToEvents.size).equals(1)
|
||||
// Calling entityEventsListener should not add the event since the previously loaded day is in another month
|
||||
|
|
@ -141,11 +136,11 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
|
||||
const startOfDay = getStartOfDay(eventStartDate).getTime()
|
||||
const daysToEventsMock: DaysToEvents = new Map([[startOfDay, []]])
|
||||
when(
|
||||
calendarFacade.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything()),
|
||||
).thenResolve(daysToEventsMock)
|
||||
when(calendarFacadeMock.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything())).thenResolve(
|
||||
daysToEventsMock,
|
||||
)
|
||||
// Making sure EventRepository.daysToEvents and EventRepository.loadedMonths is initialized
|
||||
await calendarEventsRepositoryMock.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
await eventsRepository.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
|
||||
// Act
|
||||
const calendarEventUpdate: EntityUpdateData = object()
|
||||
|
|
@ -155,7 +150,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
await entityEventsListener!(updates, initialCalendarGroupId)
|
||||
|
||||
// Assert
|
||||
const daysToEvents = calendarEventsRepositoryMock.getEventsForMonths()()
|
||||
const daysToEvents = eventsRepository.getEventsForMonths()()
|
||||
o(daysToEvents.size).equals(1)
|
||||
o.check(daysToEvents.get(startOfDay)?.length).equals(1)
|
||||
o.check(daysToEvents.get(startOfDay)?.[0].event).equals(event)
|
||||
|
|
@ -168,13 +163,11 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const eventStartDate = new Date(2025, 7, 26, 10, 0, 0)
|
||||
const startOfDay = getStartOfDay(eventStartDate).getTime()
|
||||
const daysToEventsMock: DaysToEvents = new Map([[startOfDay, []]])
|
||||
when(
|
||||
calendarFacade.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything()),
|
||||
).thenResolve(
|
||||
when(calendarFacadeMock.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything())).thenResolve(
|
||||
daysToEventsMock, // Provide a initialized Map with an empty day
|
||||
)
|
||||
// Making sure EventRepository.daysToEvents and EventRepository.loadedMonths is initialized
|
||||
await calendarEventsRepositoryMock.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
await eventsRepository.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
|
||||
const newCalendarGroupId = "newCalendarGroupId"
|
||||
const newCalendarInfo: CalendarInfo = object()
|
||||
|
|
@ -208,7 +201,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
await entityEventsListener!([calendarEventUpdate], newCalendarGroupId)
|
||||
|
||||
// Assert
|
||||
const daysToEvents = calendarEventsRepositoryMock.getEventsForMonths()()
|
||||
const daysToEvents = eventsRepository.getEventsForMonths()()
|
||||
o(daysToEvents.size).equals(1)
|
||||
o.check(daysToEvents.get(startOfDay)?.length).equals(1)
|
||||
o.check(daysToEvents.get(startOfDay)?.[0].event).equals(event)
|
||||
|
|
@ -221,19 +214,20 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const eventStartDate = new Date(2025, 7, 26, 10, 0, 0)
|
||||
const pendingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_ownerGroup: initialCalendarGroupId,
|
||||
_id: [pendingEventsListId, "event"],
|
||||
_id: ["listId", "event"],
|
||||
startTime: eventStartDate,
|
||||
endTime: new Date(2025, 7, 26, 23, 0, 0),
|
||||
pendingInvitation: true,
|
||||
})
|
||||
when(entityClientMock.load(CalendarEventTypeRef, matchers.anything())).thenResolve(pendingEvent)
|
||||
|
||||
const startOfDay = getStartOfDay(eventStartDate).getTime()
|
||||
const daysToEventsMock: DaysToEvents = new Map([[startOfDay, []]])
|
||||
when(
|
||||
calendarFacade.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything()),
|
||||
).thenResolve(daysToEventsMock)
|
||||
when(calendarFacadeMock.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything())).thenResolve(
|
||||
daysToEventsMock,
|
||||
)
|
||||
// Making sure EventRepository.daysToEvents and EventRepository.loadedMonths is initialized
|
||||
await calendarEventsRepositoryMock.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
await eventsRepository.loadMonthsIfNeeded([eventStartDate], stream(false), null)
|
||||
|
||||
// Act
|
||||
const calendarEventUpdate: EntityUpdateData = object()
|
||||
|
|
@ -244,7 +238,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
await entityEventsListener!(updates, initialCalendarGroupId)
|
||||
|
||||
// Assert
|
||||
const daysToEvents = calendarEventsRepositoryMock.getEventsForMonths()()
|
||||
const daysToEvents = eventsRepository.getEventsForMonths()()
|
||||
o(daysToEvents.size).equals(1)
|
||||
let eventsOfToday = daysToEvents.get(startOfDay) ?? []
|
||||
o.check(first(eventsOfToday)?.flags.isGhost).equals(true)
|
||||
|
|
@ -259,7 +253,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
const eventStartDate = new Date(2020, 7, 26, 10, 0, 0)
|
||||
const pendingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_ownerGroup: initialCalendarGroupId,
|
||||
_id: [pendingEventsListId, "event"],
|
||||
_id: ["listId", "event"],
|
||||
startTime: eventStartDate,
|
||||
endTime: new Date(2020, 7, 26, 23, 0, 0),
|
||||
repeatRule: createTestEntity(CalendarRepeatRuleTypeRef, {
|
||||
|
|
@ -268,17 +262,18 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
endValue: null,
|
||||
interval: "1",
|
||||
}),
|
||||
pendingInvitation: true,
|
||||
})
|
||||
when(entityClientMock.load(CalendarEventTypeRef, matchers.anything())).thenResolve(pendingEvent)
|
||||
|
||||
const currentDay = new Date(2025, 1, 10)
|
||||
const startOfDay = getStartOfDay(currentDay).getTime()
|
||||
const daysToEventsMock: DaysToEvents = new Map([[startOfDay, []]])
|
||||
when(
|
||||
calendarFacade.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything()),
|
||||
).thenResolve(daysToEventsMock)
|
||||
when(calendarFacadeMock.updateEventMap(matchers.anything(), matchers.anything(), matchers.anything(), matchers.anything())).thenResolve(
|
||||
daysToEventsMock,
|
||||
)
|
||||
// Making sure EventRepository.daysToEvents and EventRepository.loadedMonths is initialized
|
||||
await calendarEventsRepositoryMock.loadMonthsIfNeeded([new Date(startOfDay)], stream(false), null)
|
||||
await eventsRepository.loadMonthsIfNeeded([new Date(startOfDay)], stream(false), null)
|
||||
|
||||
// Act
|
||||
const calendarEventUpdate: EntityUpdateData = object()
|
||||
|
|
@ -289,7 +284,7 @@ o.spec("CalendarEventRepositoryTest", function () {
|
|||
await entityEventsListener!(updates, initialCalendarGroupId)
|
||||
|
||||
// Assert
|
||||
const daysToEvents = calendarEventsRepositoryMock.getEventsForMonths()()
|
||||
const daysToEvents = eventsRepository.getEventsForMonths()()
|
||||
const eventsOfToday = daysToEvents.get(startOfDay) ?? []
|
||||
o(daysToEvents.size).equals(28)
|
||||
o.check(first(eventsOfToday)?.flags.isGhost).equals(true)
|
||||
|
|
|
|||
|
|
@ -554,6 +554,7 @@ o.spec("CalendarImporter", function () {
|
|||
excludedDates: [],
|
||||
endValue: null,
|
||||
}),
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -632,6 +633,7 @@ o.spec("CalendarImporter", function () {
|
|||
status: CalendarAttendeeStatus.NEEDS_ACTION,
|
||||
}),
|
||||
],
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -711,6 +713,7 @@ o.spec("CalendarImporter", function () {
|
|||
status: CalendarAttendeeStatus.NEEDS_ACTION,
|
||||
}),
|
||||
],
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -792,6 +795,7 @@ o.spec("CalendarImporter", function () {
|
|||
status: CalendarAttendeeStatus.NEEDS_ACTION,
|
||||
}),
|
||||
],
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -861,6 +865,7 @@ o.spec("CalendarImporter", function () {
|
|||
hashedUid: null,
|
||||
description: "Some description",
|
||||
location: "Brazil",
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -884,6 +889,7 @@ o.spec("CalendarImporter", function () {
|
|||
sequence: "1",
|
||||
summary: "bkbkbkb",
|
||||
recurrenceId: null,
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
}
|
||||
|
|
@ -973,6 +979,7 @@ o.spec("CalendarImporter", function () {
|
|||
hashedUid: null,
|
||||
description: "Some description",
|
||||
location: "Brazil",
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -1040,6 +1047,7 @@ o.spec("CalendarImporter", function () {
|
|||
uid: "test@tuta.com",
|
||||
hashedUid: null,
|
||||
repeatRule: null,
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [
|
||||
{
|
||||
|
|
@ -1116,6 +1124,7 @@ o.spec("CalendarImporter", function () {
|
|||
uid: "test@tuta.com",
|
||||
hashedUid: null,
|
||||
repeatRule: null,
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [
|
||||
{
|
||||
|
|
@ -1190,6 +1199,7 @@ o.spec("CalendarImporter", function () {
|
|||
uid: "test@tuta.com",
|
||||
hashedUid: null,
|
||||
repeatRule: null,
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -1278,6 +1288,7 @@ END:VCALENDAR`
|
|||
uid: "test@tuta.com",
|
||||
hashedUid: null,
|
||||
sequence: "1",
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -1318,6 +1329,7 @@ END:VCALENDAR`
|
|||
sequence: "2",
|
||||
uid: "test@tuta.com",
|
||||
hashedUid: null,
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [alarmOne, alarmTwo],
|
||||
},
|
||||
|
|
@ -1375,6 +1387,7 @@ END:VCALENDAR`
|
|||
excludedDates: [],
|
||||
advancedRules: [],
|
||||
}),
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
@ -1429,6 +1442,7 @@ END:VCALENDAR`
|
|||
timeZone: "",
|
||||
excludedDates: [],
|
||||
}),
|
||||
pendingInvitation: null,
|
||||
}),
|
||||
alarms: [],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import o from "@tutao/otest"
|
|||
import {
|
||||
CalendarEvent,
|
||||
CalendarEventAttendeeTypeRef,
|
||||
CalendarEventsRefTypeRef,
|
||||
CalendarEventTypeRef,
|
||||
CalendarEventUpdateTypeRef,
|
||||
CalendarGroupRoot,
|
||||
|
|
@ -12,7 +11,7 @@ import {
|
|||
} from "../../../src/common/api/entities/tutanota/TypeRefs.js"
|
||||
import { downcast, hexToUint8Array, neverNull, stringToUtf8Uint8Array } from "@tutao/tutanota-utils"
|
||||
import { CalendarModel } from "../../../src/calendar-app/calendar/model/CalendarModel.js"
|
||||
import { CalendarAttendeeStatus, CalendarMethod, OperationType } from "../../../src/common/api/common/TutanotaConstants.js"
|
||||
import { CalendarAttendeeStatus, CalendarMethod, GroupType, OperationType } from "../../../src/common/api/common/TutanotaConstants.js"
|
||||
import { DateTime } from "luxon"
|
||||
import { EntityEventsListener, EventController } from "../../../src/common/api/main/EventController.js"
|
||||
import { Notifications } from "../../../src/common/gui/Notifications.js"
|
||||
|
|
@ -35,7 +34,7 @@ import { verify } from "@tutao/tutanota-test-utils"
|
|||
import type { WorkerClient } from "../../../src/common/api/main/WorkerClient.js"
|
||||
import { FileController } from "../../../src/common/file/FileController.js"
|
||||
import { func, instance, matchers, object, when } from "testdouble"
|
||||
import { elementIdPart, getElementId, getListId, listIdPart } from "../../../src/common/api/common/utils/EntityUtils.js"
|
||||
import { elementIdPart, getElementId, listIdPart } from "../../../src/common/api/common/utils/EntityUtils.js"
|
||||
import { createDataFile } from "../../../src/common/api/common/DataFile.js"
|
||||
import { SessionKeyNotFoundError } from "../../../src/common/api/common/error/SessionKeyNotFoundError.js"
|
||||
import { createTestEntity } from "../TestUtils.js"
|
||||
|
|
@ -58,16 +57,15 @@ o.spec("CalendarModel", function () {
|
|||
o.spec("calendar event updates", function () {
|
||||
let restClientMock: EntityRestClientMock
|
||||
let groupRoot: CalendarGroupRoot
|
||||
const loginController = makeLoginController()
|
||||
const loginControllerMock = makeLoginController()
|
||||
|
||||
const alarmsListId = neverNull(loginController.getUserController().user.alarmInfoList).alarms
|
||||
const alarmsListId = neverNull(loginControllerMock.getUserController().user.alarmInfoList).alarms
|
||||
|
||||
o.beforeEach(function () {
|
||||
groupRoot = createTestEntity(CalendarGroupRootTypeRef, {
|
||||
_id: "groupRootId",
|
||||
longEvents: "longEvents",
|
||||
shortEvents: "shortEvents",
|
||||
pendingEvents: createTestEntity(CalendarEventsRefTypeRef, { list: "pendingEvents" }),
|
||||
})
|
||||
restClientMock = new EntityRestClientMock()
|
||||
restClientMock.addElementInstances(groupRoot)
|
||||
|
|
@ -213,25 +211,23 @@ o.spec("CalendarModel", function () {
|
|||
|
||||
o.spec("CalendarMethod.REQUEST", function () {
|
||||
o.spec("Pending events", function () {
|
||||
const groupInfoId: IdTuple = ["groupInfoList", "groupInfoId"]
|
||||
const groupId = "groupId"
|
||||
|
||||
o("New invite", async function () {
|
||||
// Arrange
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
const restClientMock = new EntityRestClientMock()
|
||||
const workerClient = makeWorkerClient()
|
||||
const calendarFacade = makeCalendarFacade(
|
||||
const workerClientMock = makeWorkerClient()
|
||||
const calendarFacadeMock = makeCalendarFacade(
|
||||
{
|
||||
getEventsByUid: (_loadUid) => Promise.resolve(null),
|
||||
},
|
||||
restClientMock,
|
||||
)
|
||||
|
||||
restClientMock.addElementInstances(groupRoot)
|
||||
|
||||
const model = init({
|
||||
workerClient,
|
||||
workerClient: workerClientMock,
|
||||
restClientMock,
|
||||
calendarFacade,
|
||||
})
|
||||
|
||||
// Act
|
||||
|
|
@ -240,7 +236,7 @@ o.spec("CalendarModel", function () {
|
|||
contents: [
|
||||
{
|
||||
event: createTestEntity(CalendarEventTypeRef, {
|
||||
uid,
|
||||
uid: "uid",
|
||||
attendees: [
|
||||
createTestEntity(CalendarEventAttendeeTypeRef, {
|
||||
address: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
|
|
@ -257,109 +253,23 @@ o.spec("CalendarModel", function () {
|
|||
|
||||
// ASSERT
|
||||
// checks that update route was not taken
|
||||
verify(calendarFacade.updateCalendarEvent(matchers.anything(), matchers.anything(), matchers.anything()), { times: 0 })
|
||||
verify(calendarFacadeMock.updateCalendarEvent(matchers.anything(), matchers.anything(), matchers.anything()), { times: 0 })
|
||||
|
||||
// get pending list ID for calendar
|
||||
const defaultCalGroup = await model.getDefaultCalendarGroupRoot()
|
||||
const pendingListId = defaultCalGroup.pendingEvents?.list ?? ""
|
||||
|
||||
// capture list ID from created event
|
||||
// capture created event
|
||||
const eventCaptor = matchers.captor()
|
||||
verify(calendarFacade.saveCalendarEvent(eventCaptor.capture(), matchers.anything(), matchers.anything()))
|
||||
verify(calendarFacadeMock.saveCalendarEvent(eventCaptor.capture(), matchers.anything(), matchers.anything()))
|
||||
|
||||
const capturedEventInput: CalendarEvent = eventCaptor.value
|
||||
o.check(getListId(capturedEventInput)).equals(pendingListId)
|
||||
o.check(capturedEventInput.pendingInvitation).equals(true)
|
||||
})
|
||||
|
||||
o("Update any field but startTime of an event that hasn't been interacted yet", async function () {
|
||||
o("Update any field but startTime", async function () {
|
||||
/*
|
||||
Simple updates for pending events are treated the same way as a invitation that was already replied.
|
||||
Simple updates for pending events are treated the same way as an invitation that was already replied.
|
||||
The event doesn't have to be deleted and recreated
|
||||
*/
|
||||
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
const pendingListId = groupRoot.pendingEvents?.list || ""
|
||||
|
||||
const startTime = new Date()
|
||||
const existingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_id: [pendingListId, "eventId"], // list ID is being set arbitrarily.
|
||||
_ownerGroup: groupRoot._id,
|
||||
summary: "v1",
|
||||
sequence: "1",
|
||||
uid,
|
||||
organizer: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
address: sender,
|
||||
}),
|
||||
startTime,
|
||||
})
|
||||
|
||||
const workerClient = makeWorkerClient()
|
||||
const calendarFacade = makeCalendarFacade(
|
||||
{
|
||||
getEventsByUid: (loadUid) =>
|
||||
uid === loadUid
|
||||
? Promise.resolve({
|
||||
progenitor: existingEvent,
|
||||
alteredInstances: [],
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
},
|
||||
restClientMock,
|
||||
)
|
||||
const model = init({
|
||||
workerClient,
|
||||
restClientMock,
|
||||
calendarFacade,
|
||||
})
|
||||
const sentEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
summary: "v2",
|
||||
uid,
|
||||
sequence: "2",
|
||||
organizer: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
address: sender,
|
||||
}),
|
||||
startTime,
|
||||
})
|
||||
await model.processCalendarData(sender, {
|
||||
method: CalendarMethod.REQUEST,
|
||||
contents: [
|
||||
{
|
||||
event: sentEvent as CalendarEventProgenitor,
|
||||
alarms: [],
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const eventCaptor = matchers.captor()
|
||||
const alarmsCaptor = matchers.captor()
|
||||
const oldEventCaptor = matchers.captor()
|
||||
verify(calendarFacade.updateCalendarEvent(eventCaptor.capture(), alarmsCaptor.capture(), oldEventCaptor.capture()), { times: 1 })
|
||||
|
||||
const updatedEvent: CalendarEvent = eventCaptor.value
|
||||
o(updatedEvent.summary).equals(sentEvent.summary)
|
||||
o(updatedEvent.sequence).equals(sentEvent.sequence)
|
||||
|
||||
const oldEvent = oldEventCaptor.value
|
||||
o(oldEvent).deepEquals(existingEvent)
|
||||
|
||||
o(getElementId(updatedEvent)[0]).deepEquals(getElementId(oldEvent)[0])
|
||||
o(getElementId(updatedEvent)[1]).deepEquals(getElementId(oldEvent)[1])
|
||||
o.check(getListId(updatedEvent)).equals(pendingListId)
|
||||
})
|
||||
|
||||
o("Update to already replied event", async function () {
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
const alarm = createTestEntity(AlarmInfoTypeRef, {
|
||||
_id: "alarm-id",
|
||||
})
|
||||
restClientMock.addListInstances(
|
||||
createTestEntity(UserAlarmInfoTypeRef, {
|
||||
_id: [alarmsListId, alarm._id],
|
||||
alarmInfo: alarm,
|
||||
}),
|
||||
)
|
||||
const startTime = new Date()
|
||||
const existingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_id: ["listId", "eventId"],
|
||||
|
|
@ -370,8 +280,8 @@ o.spec("CalendarModel", function () {
|
|||
organizer: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
address: sender,
|
||||
}),
|
||||
alarmInfos: [[alarmsListId, alarm._id]],
|
||||
startTime,
|
||||
pendingInvitation: true,
|
||||
})
|
||||
const workerClient = makeWorkerClient()
|
||||
const calendarFacade = makeCalendarFacade(
|
||||
|
|
@ -400,6 +310,7 @@ o.spec("CalendarModel", function () {
|
|||
}),
|
||||
startTime,
|
||||
})
|
||||
|
||||
await model.processCalendarData(sender, {
|
||||
method: CalendarMethod.REQUEST,
|
||||
contents: [
|
||||
|
|
@ -409,27 +320,28 @@ o.spec("CalendarModel", function () {
|
|||
},
|
||||
],
|
||||
})
|
||||
|
||||
const eventCaptor = matchers.captor()
|
||||
const alarmsCaptor = matchers.captor()
|
||||
const oldEventCaptor = matchers.captor()
|
||||
verify(calendarFacade.updateCalendarEvent(eventCaptor.capture(), alarmsCaptor.capture(), oldEventCaptor.capture()))
|
||||
const updatedEvent = eventCaptor.value
|
||||
const updatedAlarms = alarmsCaptor.value
|
||||
const oldEvent = oldEventCaptor.value
|
||||
o(updatedEvent.summary).equals(sentEvent.summary)
|
||||
o(updatedEvent.sequence).equals(sentEvent.sequence)
|
||||
o(updatedAlarms).deepEquals([alarm])
|
||||
o(oldEvent).deepEquals(existingEvent)
|
||||
verify(calendarFacade.updateCalendarEvent(eventCaptor.capture(), matchers.anything(), oldEventCaptor.capture()), { times: 1 })
|
||||
|
||||
const oldEvent: CalendarEvent = oldEventCaptor.value
|
||||
o.check(oldEvent).deepEquals(existingEvent)
|
||||
|
||||
const updatedEvent: CalendarEvent = eventCaptor.value
|
||||
o.check(updatedEvent._id).deepEquals(oldEvent._id)
|
||||
o.check(updatedEvent.summary).equals(sentEvent.summary)
|
||||
o.check(updatedEvent.sequence).equals(sentEvent.sequence)
|
||||
o.check(updatedEvent.pendingInvitation).equals(true)
|
||||
})
|
||||
|
||||
o("CalendarModel.createPendingEvent is called when unaccepted pending event start time is updated", async function () {
|
||||
o("Change to event start time should create a new pending event and delete the old one", async function () {
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
const pendingListId = groupRoot.pendingEvents?.list || ""
|
||||
|
||||
const startTime = new Date()
|
||||
const existingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_id: [pendingListId, "eventId"], // list ID is being set arbitrarily.
|
||||
_id: ["listId", "eventId"],
|
||||
_ownerGroup: groupRoot._id,
|
||||
summary: "v1",
|
||||
sequence: "1",
|
||||
|
|
@ -438,6 +350,7 @@ o.spec("CalendarModel", function () {
|
|||
address: sender,
|
||||
}),
|
||||
startTime,
|
||||
pendingInvitation: true,
|
||||
})
|
||||
|
||||
const workerClient = makeWorkerClient()
|
||||
|
|
@ -461,7 +374,6 @@ o.spec("CalendarModel", function () {
|
|||
|
||||
const newStartTime = new Date(startTime)
|
||||
newStartTime.setMinutes(newStartTime.getMinutes() + 42)
|
||||
|
||||
const sentEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
summary: "v2",
|
||||
uid,
|
||||
|
|
@ -483,17 +395,92 @@ o.spec("CalendarModel", function () {
|
|||
|
||||
const newEventCaptor = matchers.captor()
|
||||
const oldEventCaptor = matchers.captor()
|
||||
|
||||
verify(calendarFacade.saveCalendarEvent(newEventCaptor.capture(), matchers.anything(), oldEventCaptor.capture()), { times: 1 })
|
||||
|
||||
const oldEvent: CalendarEvent = oldEventCaptor.value
|
||||
const newEvent: CalendarEvent = newEventCaptor.value
|
||||
|
||||
o(getListId(newEvent)).equals(pendingListId)
|
||||
o(oldEvent).deepEquals(existingEvent)
|
||||
o.check(oldEvent).deepEquals(existingEvent)
|
||||
o.check(oldEvent._id).notEquals(newEvent._id)
|
||||
o.check(oldEvent.uid).equals(newEvent.uid)
|
||||
o.check(newEvent.pendingInvitation).equals(true)
|
||||
})
|
||||
})
|
||||
|
||||
o("Update to already replied event", async function () {
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
const alarm = createTestEntity(AlarmInfoTypeRef, {
|
||||
_id: "alarm-id",
|
||||
})
|
||||
restClientMock.addListInstances(
|
||||
createTestEntity(UserAlarmInfoTypeRef, {
|
||||
_id: [alarmsListId, alarm._id],
|
||||
alarmInfo: alarm,
|
||||
}),
|
||||
)
|
||||
const startTime = new Date()
|
||||
const existingEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
_id: ["listId", "eventId"],
|
||||
_ownerGroup: groupRoot._id,
|
||||
summary: "v1",
|
||||
sequence: "1",
|
||||
uid,
|
||||
organizer: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
address: sender,
|
||||
}),
|
||||
alarmInfos: [[alarmsListId, alarm._id]],
|
||||
startTime,
|
||||
})
|
||||
const workerClient = makeWorkerClient()
|
||||
const calendarFacade = makeCalendarFacade(
|
||||
{
|
||||
getEventsByUid: (loadUid) =>
|
||||
uid === loadUid
|
||||
? Promise.resolve({
|
||||
progenitor: existingEvent,
|
||||
alteredInstances: [],
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
},
|
||||
restClientMock,
|
||||
)
|
||||
const model = init({
|
||||
workerClient,
|
||||
restClientMock,
|
||||
calendarFacade,
|
||||
})
|
||||
const sentEvent = createTestEntity(CalendarEventTypeRef, {
|
||||
summary: "v2",
|
||||
uid,
|
||||
sequence: "2",
|
||||
organizer: createTestEntity(EncryptedMailAddressTypeRef, {
|
||||
address: sender,
|
||||
}),
|
||||
startTime,
|
||||
})
|
||||
await model.processCalendarData(sender, {
|
||||
method: CalendarMethod.REQUEST,
|
||||
contents: [
|
||||
{
|
||||
event: sentEvent as CalendarEventProgenitor,
|
||||
alarms: [],
|
||||
},
|
||||
],
|
||||
})
|
||||
const eventCaptor = matchers.captor()
|
||||
const alarmsCaptor = matchers.captor()
|
||||
const oldEventCaptor = matchers.captor()
|
||||
verify(calendarFacade.updateCalendarEvent(eventCaptor.capture(), alarmsCaptor.capture(), oldEventCaptor.capture()))
|
||||
const updatedEvent = eventCaptor.value
|
||||
const updatedAlarms = alarmsCaptor.value
|
||||
const oldEvent = oldEventCaptor.value
|
||||
o(updatedEvent.summary).equals(sentEvent.summary)
|
||||
o(updatedEvent.sequence).equals(sentEvent.sequence)
|
||||
o(updatedAlarms).deepEquals([alarm])
|
||||
o(oldEvent).deepEquals(existingEvent)
|
||||
})
|
||||
|
||||
o("event is re-created when the start time changes", async function () {
|
||||
const uid = "uid"
|
||||
const sender = "sender@example.com"
|
||||
|
|
@ -799,7 +786,7 @@ function makeWorkerClient(): WorkerClient {
|
|||
return downcast({})
|
||||
}
|
||||
|
||||
function makeLoginController(): LoginController {
|
||||
function makeLoginController(groupId: Id = "groupId", groupInfoId: IdTuple = ["list", "elementId"]): LoginController {
|
||||
const alarmInfoList = createTestEntity(UserAlarmInfoListTypeTypeRef, {
|
||||
alarms: "alarms",
|
||||
})
|
||||
|
|
@ -810,13 +797,8 @@ function makeLoginController(): LoginController {
|
|||
alarmInfoList,
|
||||
})
|
||||
|
||||
// As any because we can't modify userSettingsGroupRoot as it is read-ony
|
||||
// and "when" is not working correctly
|
||||
;(userController as any).userSettingsGroupRoot = object({
|
||||
defaultCalendar: "groupRootId",
|
||||
})
|
||||
|
||||
when(userController.getCalendarMemberships()).thenReturn([])
|
||||
const groupMembership = createTestEntity(GroupMembershipTypeRef, { group: groupId, groupType: GroupType.Calendar, groupInfo: groupInfoId })
|
||||
when(userController.getCalendarMemberships()).thenReturn([groupMembership])
|
||||
|
||||
const contactGroupMembership = createTestEntity(GroupMembershipTypeRef, { group: "contactGroup" })
|
||||
when(userController.getContactGroupMemberships()).thenReturn([contactGroupMembership])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue