// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors
// SPDX-License-Identifier: Apache-2.0
import type { WithId } from '@medplum/core';
import { ContentType, createReference } from '@medplum/core';
import type { Bundle, Extension, Location, Practitioner, Project, Schedule, Slot, Timing } from '@medplum/fhirtypes';
import express from 'express';
import supertest from 'supertest';
import { initApp, shutdownApp } from '../../app';
import { loadTestConfig } from '../../config/loader';
import type { SystemRepository } from '../../fhir/repo';
import { createTestProject } from '../../test.setup';
const app = express();
const request = supertest(app);
// indicator that availability should be created without specific service-type matching
const wildcard = '__WILDCARD__';
type AvailabilityOptions = {
bufferBefore?: number;
bufferAfter?: number;
alignmentInterval?: number;
alignmentOffset?: number;
duration?: number;
availability: Timing['repeat'];
timezone?: string;
};
const fourDayWorkWeek: Timing['repeat'] = {
dayOfWeek: ['mon', 'tue', 'wed', 'thu'],
timeOfDay: ['09:30:00', '13:15:00'],
duration: 3,
durationUnit: 'h',
};
const twoDaySchedule: Timing['repeat'] = {
dayOfWeek: ['thu', 'fri'],
timeOfDay: ['12:00:00'],
duration: 8,
durationUnit: 'h',
};
const fridayOnly: Timing['repeat'] = {
dayOfWeek: ['fri'],
timeOfDay: ['10:30:00'],
duration: 8,
durationUnit: 'h',
};
describe('Schedule/:id/$find', () => {
let location: Location;
let practitioner: Practitioner;
let project: WithId<Project>;
let accessToken: string;
let systemRepo: SystemRepository;
beforeAll(async () => {
const config = await loadTestConfig();
await initApp(app, config);
const projectResult = await createTestProject({ withAccessToken: true, withRepo: true });
project = projectResult.project;
accessToken = projectResult.accessToken;
systemRepo = projectResult.repo.getSystemRepo();
practitioner = await systemRepo.createResource<Practitioner>({
resourceType: 'Practitioner',
meta: { project: project.id },
extension: [{ url: 'http://hl7.org/fhir/StructureDefinition/timezone', valueCode: 'America/New_York' }],
});
location = await systemRepo.createResource<Location>({
resourceType: 'Location',
meta: { project: project.id },
extension: [{ url: 'http://hl7.org/fhir/StructureDefinition/timezone', valueCode: 'America/Phoenix' }],
});
});
afterAll(async () => {
await shutdownApp();
});
async function makeSchedule(
availability: Record<string, AvailabilityOptions>,
opts?: { actor?: Schedule['actor'] }
): Promise<Schedule> {
const extension = Object.entries(availability).map(([serviceType, options]) => {
const { availability, timezone, ...durations } = options;
const extension = {
url: 'https://medplum.com/fhir/StructureDefinition/SchedulingParameters',
extension: [
{
url: 'availability',
valueTiming: { repeat: availability },
},
] as Extension[],
} satisfies Extension;
if (timezone) {
extension.extension.push({
url: 'timezone',
valueCode: timezone,
});
}
if (serviceType !== wildcard) {
extension.extension.push({
url: 'serviceType',
valueCodeableConcept: {
coding: [{ code: serviceType, system: 'http://example.com' }],
},
});
}
Object.entries(durations).forEach(([key, value]) =>
extension.extension.push({ url: key, valueDuration: { value, unit: 'min' } })
);
return extension;
});
return systemRepo.createResource<Schedule>({
resourceType: 'Schedule',
meta: { project: project.id },
actor: opts?.actor ?? [createReference(practitioner)],
extension,
});
}
async function makeSlot(params: {
start: Date;
end: Date;
status: 'busy' | 'free' | 'busy-unavailable';
schedule: Schedule;
}): Promise<Slot> {
return systemRepo.createResource<Slot>({
resourceType: 'Slot',
meta: { project: project.id },
start: params.start.toISOString(),
end: params.end.toISOString(),
status: params.status,
schedule: createReference(params.schedule),
});
}
test('searching an interval not overlapping availability returns an empty bundle', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00'),
end: new Date('2025-12-01T09:00:00.000-05:00'),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
});
expect(response.body).not.toHaveProperty('entry');
});
test('finds slots that overlap with the availability in the range', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00'),
end: new Date('2025-12-01T14:00:00.000-05:00'),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T10:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T10:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T12:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('discovers and removes busy slots from the search results', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
await makeSlot({
start: new Date('2025-12-01T11:10:00.000-05:00'),
end: new Date('2025-12-01T11:40:00.000-05:00'),
status: 'busy',
schedule,
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T14:00:00.000-05:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T10:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T10:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
// 11:10 - 11:40 is busy, so the 11:00-11:20 slot is not returned here
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T12:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('discovers and adds "free" slots into the search results', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
await makeSlot({
start: new Date('2025-12-01T09:00:00.000-05:00'),
end: new Date('2025-12-01T10:00:00.000-05:00'),
status: 'free',
schedule,
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T14:00:00.000-05:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T09:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T09:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T10:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T10:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T12:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('resolving availability with bufferBefore and bufferAfter', async () => {
const schedule = await makeSchedule({
[wildcard]: {
availability: fourDayWorkWeek,
bufferBefore: 15,
bufferAfter: 5,
duration: 20,
},
});
// Mark busy directly after the 12:00 slot, blocking it
await makeSlot({
start: new Date('2025-12-01T12:20:00.000-05:00'),
end: new Date('2025-12-01T12:40:00.000-05:00'),
status: 'busy',
schedule,
});
// Mark busy 5min after the 11am slot, leaving bufferAfter free
await makeSlot({
start: new Date('2025-12-01T11:25:00.000-05:00'),
end: new Date('2025-12-01T11:45:00.000-05:00'),
status: 'busy',
schedule,
});
// Mark busy 10min before the 10am slot, blocking it
await makeSlot({
start: new Date('2025-12-01T09:20:00.000-05:00'),
end: new Date('2025-12-01T09:50:00.000-05:00'),
status: 'busy',
schedule,
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T14:00:00.000-05:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
// 9:20 - 9:50 is busy, so the 10:00-10:20 slot is not returned here (it would not have the 15min buffer before)
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
// 12:20 - 12:40 is busy, so the 12:00-12:20 slot is not returned here (it would not have the 5min buffer after)
],
});
});
test('search input in another timezone finds appropriate overlap', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T14:00:00.000-05:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-01T12:20:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('uses default search page size of 20 results', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 60 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2026-01-01T00:00:00.000-05:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body.entry).toHaveLength(20);
});
test('can override search page size with `_count`', async () => {
const schedule = await makeSchedule({
[wildcard]: {
// Alignment slot options to every-two-minutes means that there are
// lots of results so we can test the maximum page size of 1000
alignmentInterval: 2,
availability: fourDayWorkWeek,
duration: 60,
},
});
const smallResponse = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2026-01-01T00:00:00.000-05:00').toISOString(),
_count: 10,
});
expect(smallResponse.body).not.toHaveProperty('issue');
expect(smallResponse.status).toBe(200);
expect(smallResponse.body.entry).toHaveLength(10);
const largeResponse = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2026-01-01T00:00:00.000-05:00').toISOString(),
_count: 1000,
});
expect(largeResponse.body).not.toHaveProperty('issue');
expect(largeResponse.status).toBe(200);
expect(largeResponse.body.entry).toHaveLength(1000);
});
test('errors if _count is too low', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 60 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2026-01-01T00:00:00.000-05:00').toISOString(),
_count: 0,
});
expect(response.status).toBe(400);
expect(response.body.issue).toEqual([
{
code: 'invalid',
severity: 'error',
details: {
text: 'Invalid _count, minimum required is 1',
},
},
]);
});
test('errors if _count is too high', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 60 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-05:00').toISOString(),
end: new Date('2026-01-01T00:00:00.000-05:00').toISOString(),
_count: 1001,
});
expect(response.status).toBe(400);
expect(response.body.issue).toEqual([
{
code: 'invalid',
severity: 'error',
details: {
text: 'Invalid _count, maximum allowed is 1000',
},
},
]);
});
test("gets timezone data from the schedule's actor", async () => {
// `location` has timezone set to America/Phoenix, which is always at offset -07:00
const schedule = await makeSchedule(
{
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
},
{ actor: [createReference(location)] }
);
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-07:00').toISOString(),
end: new Date('2025-12-01T12:00:00.000-07:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T10:00:00.000-07:00').toISOString(),
end: new Date('2025-12-01T10:20:00.000-07:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-07:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-07:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test("timezone in scheduling parameters overrides the schedule's actor's timezone", async () => {
// `location` has timezone set to America/Phoenix, which is always at offset -07:00
// scheduling params have timezone set to Pacific/Honolulu, which is always at offset -10:00
const schedule = await makeSchedule(
{
[wildcard]: { availability: fourDayWorkWeek, duration: 20, timezone: 'Pacific/Honolulu' },
},
{ actor: [createReference(location)] }
);
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-01T00:00:00.000-10:00').toISOString(),
end: new Date('2025-12-01T12:00:00.000-10:00').toISOString(),
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T10:00:00.000-10:00').toISOString(),
end: new Date('2025-12-01T10:20:00.000-10:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-01T11:00:00.000-10:00').toISOString(),
end: new Date('2025-12-01T11:20:00.000-10:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('with a service-type parameter', async () => {
const schedule = await makeSchedule({
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
'new-patient': { availability: twoDaySchedule, duration: 30 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-04T10:00:00.000-05:00'),
end: new Date('2025-12-04T14:00:00.000-05:00'),
'service-type': 'http://example.com|new-patient,http://example.com|other',
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-04T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-04T12:30:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'new-patient', system: 'http://example.com' }] }],
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-04T13:00:00.000-05:00').toISOString(),
end: new Date('2025-12-04T13:30:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'new-patient', system: 'http://example.com' }] }],
},
},
],
});
});
test('when no matching serviceType is found, uses any wildcard availability', async () => {
const schedule = await makeSchedule({
'new-patient': { availability: fridayOnly, duration: 45, bufferBefore: 15 },
'office-visit': { availability: twoDaySchedule, duration: 30, alignmentOffset: 15, alignmentInterval: 30 },
[wildcard]: { availability: fourDayWorkWeek, duration: 20, alignmentOffset: 30 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-04T10:00:00.000-05:00'),
end: new Date('2025-12-04T14:00:00.000-05:00'),
'service-type': 'http://example.com|new-patient-visit',
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
// wildcard slots are 20 minutes long, start on the half-hour, and have no `serviceType` attribute
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-04T10:30:00.000-05:00').toISOString(),
end: new Date('2025-12-04T10:50:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-04T11:30:00.000-05:00').toISOString(),
end: new Date('2025-12-04T11:50:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
// No entry at 12:30; there is a lunch gap in the scheduled availability
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-04T13:30:00.000-05:00').toISOString(),
end: new Date('2025-12-04T13:50:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
},
},
],
});
});
test('returns appropriate slots for each serviceType passed', async () => {
const schedule = await makeSchedule({
'new-patient': { availability: fridayOnly, duration: 45, bufferBefore: 15 },
'office-visit': { availability: twoDaySchedule, duration: 30, alignmentOffset: 15, alignmentInterval: 30 },
[wildcard]: { availability: fourDayWorkWeek, duration: 20 },
});
const response = await request
.get(`/fhir/R4/Schedule/${schedule.id}/$find`)
.set('Authorization', `Bearer ${accessToken}`)
.set('Content-Type', ContentType.FHIR_JSON)
.query({
start: new Date('2025-12-05T10:00:00.000-05:00').toISOString(),
end: new Date('2025-12-05T14:00:00.000-05:00').toISOString(),
'service-type': 'http://example.com|new-patient,http://example.com|office-visit',
});
expect(response.body).not.toHaveProperty('issue');
expect(response.status).toBe(200);
expect(response.body).toMatchObject<Bundle>({
resourceType: 'Bundle',
type: 'searchset',
entry: [
// new-patient slots are 45 minutes long and start on the hour
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T11:00:00.000-05:00').toISOString(),
end: new Date('2025-12-05T11:45:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'new-patient', system: 'http://example.com' }] }],
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T12:00:00.000-05:00').toISOString(),
end: new Date('2025-12-05T12:45:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'new-patient', system: 'http://example.com' }] }],
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T13:00:00.000-05:00').toISOString(),
end: new Date('2025-12-05T13:45:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'new-patient', system: 'http://example.com' }] }],
},
},
// office-visit slots are 30 minutes long and start at X:15 or X:45
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T12:15:00.000-05:00').toISOString(),
end: new Date('2025-12-05T12:45:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'office-visit', system: 'http://example.com' }] }],
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T12:45:00.000-05:00').toISOString(),
end: new Date('2025-12-05T13:15:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'office-visit', system: 'http://example.com' }] }],
},
},
{
resource: {
resourceType: 'Slot',
start: new Date('2025-12-05T13:15:00.000-05:00').toISOString(),
end: new Date('2025-12-05T13:45:00.000-05:00').toISOString(),
status: 'free',
schedule: createReference(schedule),
serviceType: [{ coding: [{ code: 'office-visit', system: 'http://example.com' }] }],
},
},
// no wildcard slots returned because we had exact matches
],
});
});
});