@@ -102,6 +102,188 @@ class CalendarsTest {
102102 }
103103 }
104104
105+ @Nested
106+ inner class SpecificTimeAvailabilityTests {
107+ @Test
108+ fun `SpecificTimeAvailability serializes properly` () {
109+ val adapter = JsonHelper .moshi().adapter(SpecificTimeAvailability ::class .java)
110+ val jsonBuffer = Buffer ().writeUtf8(
111+ """
112+ {
113+ "date": "2026-03-18",
114+ "start": "09:00",
115+ "end": "17:00"
116+ }
117+ """ .trimIndent(),
118+ )
119+
120+ val specificTimeAvailability = adapter.fromJson(jsonBuffer)!!
121+ assertIs<SpecificTimeAvailability >(specificTimeAvailability)
122+ assertEquals(" 2026-03-18" , specificTimeAvailability.date)
123+ assertEquals(" 09:00" , specificTimeAvailability.start)
124+ assertEquals(" 17:00" , specificTimeAvailability.end)
125+ }
126+
127+ @Test
128+ fun `SpecificTimeAvailability serializes to JSON correctly` () {
129+ val adapter = JsonHelper .moshi().adapter(SpecificTimeAvailability ::class .java)
130+ val specificTimeAvailability = SpecificTimeAvailability (
131+ date = " 2026-03-18" ,
132+ start = " 09:00" ,
133+ end = " 17:00" ,
134+ )
135+
136+ val json = adapter.toJson(specificTimeAvailability)
137+ assertEquals(""" {"date":"2026-03-18","start":"09:00","end":"17:00"}""" , json)
138+ }
139+
140+ @Test
141+ fun `SpecificTimeAvailability round-trip serialization` () {
142+ val adapter = JsonHelper .moshi().adapter(SpecificTimeAvailability ::class .java)
143+ val original = SpecificTimeAvailability (
144+ date = " 2026-03-18" ,
145+ start = " 09:00" ,
146+ end = " 17:00" ,
147+ )
148+
149+ val json = adapter.toJson(original)
150+ val deserialized = adapter.fromJson(json)!!
151+ assertEquals(original.date, deserialized.date)
152+ assertEquals(original.start, deserialized.start)
153+ assertEquals(original.end, deserialized.end)
154+ }
155+
156+ @Test
157+ fun `SpecificTimeAvailability Builder works correctly` () {
158+ val specificTimeAvailability = SpecificTimeAvailability .Builder (
159+ date = " 2026-03-18" ,
160+ start = " 09:00" ,
161+ end = " 17:00" ,
162+ ).build()
163+
164+ assertEquals(" 2026-03-18" , specificTimeAvailability.date)
165+ assertEquals(" 09:00" , specificTimeAvailability.start)
166+ assertEquals(" 17:00" , specificTimeAvailability.end)
167+ }
168+
169+ @Test
170+ fun `AvailabilityParticipant serializes with specificTimeAvailability` () {
171+ val adapter = JsonHelper .moshi().adapter(AvailabilityParticipant ::class .java)
172+ val participant = AvailabilityParticipant (
173+ email = " test@nylas.com" ,
174+ calendarIds = listOf (" primary" ),
175+ specificTimeAvailability = listOf (
176+ SpecificTimeAvailability (
177+ date = " 2026-03-18" ,
178+ start = " 09:00" ,
179+ end = " 17:00" ,
180+ ),
181+ ),
182+ )
183+
184+ val json = adapter.toJson(participant)
185+ val deserialized = adapter.fromJson(json)!!
186+ assertEquals(" test@nylas.com" , deserialized.email)
187+ assertEquals(1 , deserialized.specificTimeAvailability?.size)
188+ assertEquals(" 2026-03-18" , deserialized.specificTimeAvailability?.get(0 )?.date)
189+ assertEquals(" 09:00" , deserialized.specificTimeAvailability?.get(0 )?.start)
190+ assertEquals(" 17:00" , deserialized.specificTimeAvailability?.get(0 )?.end)
191+ }
192+
193+ @Test
194+ fun `AvailabilityParticipant serializes without specificTimeAvailability for backward compatibility` () {
195+ val adapter = JsonHelper .moshi().adapter(AvailabilityParticipant ::class .java)
196+ val participant = AvailabilityParticipant (
197+ email = " test@nylas.com" ,
198+ calendarIds = listOf (" calendar-123" ),
199+ )
200+
201+ val json = adapter.toJson(participant)
202+ val deserialized = adapter.fromJson(json)!!
203+ assertEquals(" test@nylas.com" , deserialized.email)
204+ assertEquals(null , deserialized.specificTimeAvailability)
205+ assertEquals(null , deserialized.openHours)
206+ }
207+
208+ @Test
209+ fun `AvailabilityParticipant deserializes JSON with specific_time_availability` () {
210+ val adapter = JsonHelper .moshi().adapter(AvailabilityParticipant ::class .java)
211+ val jsonBuffer = Buffer ().writeUtf8(
212+ """
213+ {
214+ "email": "test@nylas.com",
215+ "calendar_ids": ["primary"],
216+ "specific_time_availability": [
217+ {
218+ "date": "2026-03-18",
219+ "start": "09:00",
220+ "end": "17:00"
221+ }
222+ ]
223+ }
224+ """ .trimIndent(),
225+ )
226+
227+ val participant = adapter.fromJson(jsonBuffer)!!
228+ assertEquals(" test@nylas.com" , participant.email)
229+ assertEquals(listOf (" primary" ), participant.calendarIds)
230+ assertEquals(1 , participant.specificTimeAvailability?.size)
231+ assertEquals(" 2026-03-18" , participant.specificTimeAvailability?.get(0 )?.date)
232+ assertEquals(" 09:00" , participant.specificTimeAvailability?.get(0 )?.start)
233+ assertEquals(" 17:00" , participant.specificTimeAvailability?.get(0 )?.end)
234+ }
235+
236+ @Test
237+ fun `AvailabilityParticipant deserializes JSON without specific_time_availability for backward compatibility` () {
238+ val adapter = JsonHelper .moshi().adapter(AvailabilityParticipant ::class .java)
239+ val jsonBuffer = Buffer ().writeUtf8(
240+ """
241+ {
242+ "email": "test@nylas.com",
243+ "calendar_ids": ["calendar-123"]
244+ }
245+ """ .trimIndent(),
246+ )
247+
248+ val participant = adapter.fromJson(jsonBuffer)!!
249+ assertEquals(" test@nylas.com" , participant.email)
250+ assertEquals(null , participant.specificTimeAvailability)
251+ }
252+
253+ @Test
254+ fun `AvailabilityParticipant Builder works with specificTimeAvailability` () {
255+ val participant = AvailabilityParticipant .Builder (" test@nylas.com" )
256+ .calendarIds(listOf (" primary" ))
257+ .specificTimeAvailability(
258+ listOf (
259+ SpecificTimeAvailability (
260+ date = " 2026-03-18" ,
261+ start = " 09:00" ,
262+ end = " 17:00" ,
263+ ),
264+ ),
265+ )
266+ .build()
267+
268+ assertEquals(" test@nylas.com" , participant.email)
269+ assertEquals(listOf (" primary" ), participant.calendarIds)
270+ assertEquals(1 , participant.specificTimeAvailability?.size)
271+ assertEquals(" 2026-03-18" , participant.specificTimeAvailability?.get(0 )?.date)
272+ assertEquals(" 09:00" , participant.specificTimeAvailability?.get(0 )?.start)
273+ assertEquals(" 17:00" , participant.specificTimeAvailability?.get(0 )?.end)
274+ }
275+
276+ @Test
277+ fun `AvailabilityParticipant Builder works without specificTimeAvailability for backward compatibility` () {
278+ val participant = AvailabilityParticipant .Builder (" test@nylas.com" )
279+ .calendarIds(listOf (" calendar-123" ))
280+ .build()
281+
282+ assertEquals(" test@nylas.com" , participant.email)
283+ assertEquals(null , participant.specificTimeAvailability)
284+ }
285+ }
286+
105287 @Nested
106288 inner class CrudTests {
107289 private lateinit var grantId: String
@@ -341,6 +523,47 @@ class CalendarsTest {
341523 assertEquals(adapter.toJson(getAvailabilityRequest), requestBodyCaptor.firstValue)
342524 }
343525
526+ @Test
527+ fun `getting availability with specificTimeAvailability calls requests with the correct params` () {
528+ val adapter = JsonHelper .moshi().adapter(GetAvailabilityRequest ::class .java)
529+ val getAvailabilityRequest = GetAvailabilityRequest (
530+ startTime = 1737540000 ,
531+ endTime = 1737712800 ,
532+ participants = listOf (
533+ AvailabilityParticipant (
534+ email = " nylastest8@gmail.com" ,
535+ calendarIds = listOf (" primary" ),
536+ specificTimeAvailability = listOf (
537+ SpecificTimeAvailability (
538+ date = " 2026-03-18" ,
539+ start = " 09:00" ,
540+ end = " 17:00" ,
541+ ),
542+ ),
543+ ),
544+ ),
545+ durationMinutes = 30 ,
546+ )
547+
548+ calendars.getAvailability(getAvailabilityRequest)
549+ val pathCaptor = argumentCaptor<String >()
550+ val typeCaptor = argumentCaptor<Type >()
551+ val requestBodyCaptor = argumentCaptor<String >()
552+ val queryParamCaptor = argumentCaptor<ListCalendersQueryParams >()
553+ val overrideParamCaptor = argumentCaptor<RequestOverrides >()
554+ verify(mockNylasClient).executePost<ListResponse <Calendar >>(
555+ pathCaptor.capture(),
556+ typeCaptor.capture(),
557+ requestBodyCaptor.capture(),
558+ queryParamCaptor.capture(),
559+ overrideParamCaptor.capture(),
560+ )
561+
562+ assertEquals(" v3/calendars/availability" , pathCaptor.firstValue)
563+ assertEquals(Types .newParameterizedType(Response ::class .java, GetAvailabilityResponse ::class .java), typeCaptor.firstValue)
564+ assertEquals(adapter.toJson(getAvailabilityRequest), requestBodyCaptor.firstValue)
565+ }
566+
344567 @Test
345568 fun `getting free busy calls requests with the correct params` () {
346569 val grantId = " abc-123-grant-id"
0 commit comments