@htlkg/data 0.0.19 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +257 -1
- package/dist/client/index.js +59 -1
- package/dist/client/index.js.map +1 -1
- package/dist/common-DSxswsZ3.d.ts +40 -0
- package/dist/hooks/index.d.ts +162 -10
- package/dist/hooks/index.js +191 -33
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +789 -13
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +342 -4
- package/dist/mutations/index.js +486 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/{productInstances-BA3cNsYc.d.ts → productInstances-BpQv1oLS.d.ts} +2 -40
- package/dist/queries/index.d.ts +113 -2
- package/dist/queries/index.js +192 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/reservations-C0FNm__0.d.ts +154 -0
- package/dist/reservations-CdDfkcZ_.d.ts +172 -0
- package/package.json +14 -13
- package/src/client/index.ts +18 -0
- package/src/client/reservations.ts +336 -0
- package/src/hooks/createDataHook.test.ts +534 -0
- package/src/hooks/createDataHook.ts +20 -13
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useContacts.test.ts +159 -0
- package/src/hooks/useContacts.ts +176 -0
- package/src/hooks/useReservations.ts +145 -0
- package/src/mutations/contacts.test.ts +604 -0
- package/src/mutations/contacts.ts +554 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/productInstances/productInstances.test.ts +3 -3
- package/src/mutations/reservations.test.ts +459 -0
- package/src/mutations/reservations.ts +452 -0
- package/src/queries/contacts.test.ts +505 -0
- package/src/queries/contacts.ts +237 -0
- package/src/queries/index.ts +21 -0
- package/src/queries/reservations.test.ts +374 -0
- package/src/queries/reservations.ts +247 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contact Query Functions
|
|
3
|
+
*
|
|
4
|
+
* Provides query functions for fetching contact data from the GraphQL API.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Contact } from "@htlkg/core/types";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get a single contact by ID
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { getContact } from '@htlkg/data/queries';
|
|
15
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
16
|
+
*
|
|
17
|
+
* const client = generateClient<Schema>();
|
|
18
|
+
* const contact = await getContact(client, 'contact-123');
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export async function getContact<TClient = any>(
|
|
22
|
+
client: TClient,
|
|
23
|
+
id: string,
|
|
24
|
+
): Promise<Contact | null> {
|
|
25
|
+
try {
|
|
26
|
+
const { data, errors } = await (client as any).models.Contact.get({ id });
|
|
27
|
+
|
|
28
|
+
if (errors) {
|
|
29
|
+
console.error("[getContact] GraphQL errors:", errors);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return data as Contact;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error("[getContact] Error fetching contact:", error);
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* List all contacts with optional filtering
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { listContacts } from '@htlkg/data/queries';
|
|
46
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
47
|
+
*
|
|
48
|
+
* const client = generateClient<Schema>();
|
|
49
|
+
* const contacts = await listContacts(client, {
|
|
50
|
+
* filter: { gdprConsent: { eq: true } },
|
|
51
|
+
* limit: 50
|
|
52
|
+
* });
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export async function listContacts<TClient = any>(
|
|
56
|
+
client: TClient,
|
|
57
|
+
options?: {
|
|
58
|
+
filter?: any;
|
|
59
|
+
limit?: number;
|
|
60
|
+
nextToken?: string;
|
|
61
|
+
},
|
|
62
|
+
): Promise<{ items: Contact[]; nextToken?: string }> {
|
|
63
|
+
try {
|
|
64
|
+
const { data, errors, nextToken } = await (
|
|
65
|
+
client as any
|
|
66
|
+
).models.Contact.list(options);
|
|
67
|
+
|
|
68
|
+
if (errors) {
|
|
69
|
+
console.error("[listContacts] GraphQL errors:", errors);
|
|
70
|
+
return { items: [], nextToken: undefined };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
items: (data || []) as Contact[],
|
|
75
|
+
nextToken,
|
|
76
|
+
};
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("[listContacts] Error fetching contacts:", error);
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* List contacts by brand ID
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```typescript
|
|
88
|
+
* import { listContactsByBrand } from '@htlkg/data/queries';
|
|
89
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
90
|
+
*
|
|
91
|
+
* const client = generateClient<Schema>();
|
|
92
|
+
* const contacts = await listContactsByBrand(client, 'brand-123');
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export async function listContactsByBrand<TClient = any>(
|
|
96
|
+
client: TClient,
|
|
97
|
+
brandId: string,
|
|
98
|
+
options?: {
|
|
99
|
+
limit?: number;
|
|
100
|
+
nextToken?: string;
|
|
101
|
+
},
|
|
102
|
+
): Promise<{ items: Contact[]; nextToken?: string }> {
|
|
103
|
+
return listContacts(client, {
|
|
104
|
+
filter: { brandId: { eq: brandId } },
|
|
105
|
+
...options,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get a contact by email within a brand
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* import { getContactByEmail } from '@htlkg/data/queries';
|
|
115
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
116
|
+
*
|
|
117
|
+
* const client = generateClient<Schema>();
|
|
118
|
+
* const contact = await getContactByEmail(client, 'guest@example.com', 'brand-123');
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export async function getContactByEmail<TClient = any>(
|
|
122
|
+
client: TClient,
|
|
123
|
+
email: string,
|
|
124
|
+
brandId: string,
|
|
125
|
+
): Promise<Contact | null> {
|
|
126
|
+
try {
|
|
127
|
+
const { data, errors } = await (client as any).models.Contact.list({
|
|
128
|
+
filter: {
|
|
129
|
+
and: [{ email: { eq: email } }, { brandId: { eq: brandId } }],
|
|
130
|
+
},
|
|
131
|
+
limit: 1,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (errors) {
|
|
135
|
+
console.error("[getContactByEmail] GraphQL errors:", errors);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return data?.[0] as Contact | null;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error("[getContactByEmail] Error fetching contact:", error);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get a contact by phone within a brand
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* import { getContactByPhone } from '@htlkg/data/queries';
|
|
152
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
153
|
+
*
|
|
154
|
+
* const client = generateClient<Schema>();
|
|
155
|
+
* const contact = await getContactByPhone(client, '+1234567890', 'brand-123');
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export async function getContactByPhone<TClient = any>(
|
|
159
|
+
client: TClient,
|
|
160
|
+
phone: string,
|
|
161
|
+
brandId: string,
|
|
162
|
+
): Promise<Contact | null> {
|
|
163
|
+
try {
|
|
164
|
+
const { data, errors } = await (client as any).models.Contact.list({
|
|
165
|
+
filter: {
|
|
166
|
+
and: [{ phone: { eq: phone } }, { brandId: { eq: brandId } }],
|
|
167
|
+
},
|
|
168
|
+
limit: 1,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (errors) {
|
|
172
|
+
console.error("[getContactByPhone] GraphQL errors:", errors);
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return data?.[0] as Contact | null;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error("[getContactByPhone] Error fetching contact:", error);
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Search contacts by query string within a brand
|
|
185
|
+
* Searches across email, firstName, and lastName fields
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* import { searchContacts } from '@htlkg/data/queries';
|
|
190
|
+
* import { generateClient } from '@htlkg/data/client';
|
|
191
|
+
*
|
|
192
|
+
* const client = generateClient<Schema>();
|
|
193
|
+
* const contacts = await searchContacts(client, 'john', 'brand-123');
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
export async function searchContacts<TClient = any>(
|
|
197
|
+
client: TClient,
|
|
198
|
+
query: string,
|
|
199
|
+
brandId: string,
|
|
200
|
+
options?: {
|
|
201
|
+
limit?: number;
|
|
202
|
+
nextToken?: string;
|
|
203
|
+
},
|
|
204
|
+
): Promise<{ items: Contact[]; nextToken?: string }> {
|
|
205
|
+
try {
|
|
206
|
+
const { data, errors, nextToken } = await (
|
|
207
|
+
client as any
|
|
208
|
+
).models.Contact.list({
|
|
209
|
+
filter: {
|
|
210
|
+
and: [
|
|
211
|
+
{ brandId: { eq: brandId } },
|
|
212
|
+
{
|
|
213
|
+
or: [
|
|
214
|
+
{ email: { contains: query } },
|
|
215
|
+
{ firstName: { contains: query } },
|
|
216
|
+
{ lastName: { contains: query } },
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
...options,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (errors) {
|
|
225
|
+
console.error("[searchContacts] GraphQL errors:", errors);
|
|
226
|
+
return { items: [], nextToken: undefined };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
items: (data || []) as Contact[],
|
|
231
|
+
nextToken,
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error("[searchContacts] Error searching contacts:", error);
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
}
|
package/src/queries/index.ts
CHANGED
|
@@ -52,3 +52,24 @@ export {
|
|
|
52
52
|
SYSTEM_SETTINGS_KEY,
|
|
53
53
|
DEFAULT_SOFT_DELETE_RETENTION_DAYS,
|
|
54
54
|
} from "./systemSettings";
|
|
55
|
+
|
|
56
|
+
// Reservation queries
|
|
57
|
+
export {
|
|
58
|
+
getReservation,
|
|
59
|
+
listReservations,
|
|
60
|
+
listReservationsByBrand,
|
|
61
|
+
listReservationsByContact,
|
|
62
|
+
listReservationsByDateRange,
|
|
63
|
+
getReservationByConfirmation,
|
|
64
|
+
} from "./reservations";
|
|
65
|
+
export type { Reservation } from "./reservations";
|
|
66
|
+
|
|
67
|
+
// Contact queries
|
|
68
|
+
export {
|
|
69
|
+
getContact,
|
|
70
|
+
listContacts,
|
|
71
|
+
listContactsByBrand,
|
|
72
|
+
getContactByEmail,
|
|
73
|
+
getContactByPhone,
|
|
74
|
+
searchContacts,
|
|
75
|
+
} from "./contacts";
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reservation Query Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for reservation query functions including filtering,
|
|
5
|
+
* pagination, and specialized queries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
9
|
+
import {
|
|
10
|
+
getReservation,
|
|
11
|
+
listReservations,
|
|
12
|
+
listReservationsByBrand,
|
|
13
|
+
listReservationsByContact,
|
|
14
|
+
listReservationsByDateRange,
|
|
15
|
+
getReservationByConfirmation,
|
|
16
|
+
type Reservation,
|
|
17
|
+
} from "./reservations";
|
|
18
|
+
|
|
19
|
+
describe("Reservation Queries", () => {
|
|
20
|
+
const mockReservation: Reservation = {
|
|
21
|
+
id: "reservation-001",
|
|
22
|
+
brandId: "brand-123",
|
|
23
|
+
visitId: "visit-456",
|
|
24
|
+
confirmationCode: "ABC123",
|
|
25
|
+
checkIn: "2024-06-01",
|
|
26
|
+
checkOut: "2024-06-05",
|
|
27
|
+
status: "confirmed",
|
|
28
|
+
source: "direct",
|
|
29
|
+
room: "101",
|
|
30
|
+
nights: 4,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe("getReservation", () => {
|
|
34
|
+
it("should return a reservation by ID", async () => {
|
|
35
|
+
const mockGet = vi.fn().mockResolvedValue({
|
|
36
|
+
data: mockReservation,
|
|
37
|
+
errors: null,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const mockClient = {
|
|
41
|
+
models: {
|
|
42
|
+
Reservation: { get: mockGet },
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const result = await getReservation(mockClient, "reservation-001");
|
|
47
|
+
|
|
48
|
+
expect(result).toEqual(mockReservation);
|
|
49
|
+
expect(mockGet).toHaveBeenCalledWith({ id: "reservation-001" });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should return null when reservation not found", async () => {
|
|
53
|
+
const mockGet = vi.fn().mockResolvedValue({
|
|
54
|
+
data: null,
|
|
55
|
+
errors: [{ message: "Not found" }],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const mockClient = {
|
|
59
|
+
models: {
|
|
60
|
+
Reservation: { get: mockGet },
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const result = await getReservation(mockClient, "nonexistent");
|
|
65
|
+
|
|
66
|
+
expect(result).toBeNull();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should throw on unexpected error", async () => {
|
|
70
|
+
const mockGet = vi.fn().mockRejectedValue(new Error("Network error"));
|
|
71
|
+
|
|
72
|
+
const mockClient = {
|
|
73
|
+
models: {
|
|
74
|
+
Reservation: { get: mockGet },
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
await expect(getReservation(mockClient, "reservation-001")).rejects.toThrow(
|
|
79
|
+
"Network error"
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("listReservations", () => {
|
|
85
|
+
const mockReservations = [
|
|
86
|
+
mockReservation,
|
|
87
|
+
{ ...mockReservation, id: "reservation-002", confirmationCode: "DEF456" },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
it("should return a list of reservations", async () => {
|
|
91
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
92
|
+
data: mockReservations,
|
|
93
|
+
errors: null,
|
|
94
|
+
nextToken: null,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const mockClient = {
|
|
98
|
+
models: {
|
|
99
|
+
Reservation: { list: mockList },
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const result = await listReservations(mockClient);
|
|
104
|
+
|
|
105
|
+
expect(result.items).toHaveLength(2);
|
|
106
|
+
expect(result.items[0].id).toBe("reservation-001");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should pass filter options to the query", async () => {
|
|
110
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
111
|
+
data: [mockReservation],
|
|
112
|
+
errors: null,
|
|
113
|
+
nextToken: null,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const mockClient = {
|
|
117
|
+
models: {
|
|
118
|
+
Reservation: { list: mockList },
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const filter = { status: { eq: "confirmed" } };
|
|
123
|
+
await listReservations(mockClient, { filter, limit: 50 });
|
|
124
|
+
|
|
125
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
126
|
+
filter,
|
|
127
|
+
limit: 50,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("should return nextToken for pagination", async () => {
|
|
132
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
133
|
+
data: [mockReservation],
|
|
134
|
+
errors: null,
|
|
135
|
+
nextToken: "token-123",
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const mockClient = {
|
|
139
|
+
models: {
|
|
140
|
+
Reservation: { list: mockList },
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const result = await listReservations(mockClient, { limit: 1 });
|
|
145
|
+
|
|
146
|
+
expect(result.nextToken).toBe("token-123");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should return empty array on GraphQL error", async () => {
|
|
150
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
151
|
+
data: null,
|
|
152
|
+
errors: [{ message: "Query failed" }],
|
|
153
|
+
nextToken: undefined,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const mockClient = {
|
|
157
|
+
models: {
|
|
158
|
+
Reservation: { list: mockList },
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = await listReservations(mockClient);
|
|
163
|
+
|
|
164
|
+
expect(result.items).toHaveLength(0);
|
|
165
|
+
expect(result.nextToken).toBeUndefined();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe("listReservationsByBrand", () => {
|
|
170
|
+
it("should filter reservations by brand ID", async () => {
|
|
171
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
172
|
+
data: [mockReservation],
|
|
173
|
+
errors: null,
|
|
174
|
+
nextToken: null,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const mockClient = {
|
|
178
|
+
models: {
|
|
179
|
+
Reservation: { list: mockList },
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
await listReservationsByBrand(mockClient, "brand-123");
|
|
184
|
+
|
|
185
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
186
|
+
filter: { brandId: { eq: "brand-123" } },
|
|
187
|
+
limit: undefined,
|
|
188
|
+
nextToken: undefined,
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("should combine brand filter with additional filters", async () => {
|
|
193
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
194
|
+
data: [mockReservation],
|
|
195
|
+
errors: null,
|
|
196
|
+
nextToken: null,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const mockClient = {
|
|
200
|
+
models: {
|
|
201
|
+
Reservation: { list: mockList },
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
await listReservationsByBrand(mockClient, "brand-123", {
|
|
206
|
+
filter: { status: { eq: "confirmed" } },
|
|
207
|
+
limit: 50,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
211
|
+
filter: {
|
|
212
|
+
and: [
|
|
213
|
+
{ brandId: { eq: "brand-123" } },
|
|
214
|
+
{ status: { eq: "confirmed" } },
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
limit: 50,
|
|
218
|
+
nextToken: undefined,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("listReservationsByContact", () => {
|
|
224
|
+
it("should filter reservations by contact/visit ID", async () => {
|
|
225
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
226
|
+
data: [mockReservation],
|
|
227
|
+
errors: null,
|
|
228
|
+
nextToken: null,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const mockClient = {
|
|
232
|
+
models: {
|
|
233
|
+
Reservation: { list: mockList },
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
await listReservationsByContact(mockClient, "visit-456");
|
|
238
|
+
|
|
239
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
240
|
+
filter: { visitId: { eq: "visit-456" } },
|
|
241
|
+
limit: undefined,
|
|
242
|
+
nextToken: undefined,
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("listReservationsByDateRange", () => {
|
|
248
|
+
it("should filter reservations by date range and brand", async () => {
|
|
249
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
250
|
+
data: [mockReservation],
|
|
251
|
+
errors: null,
|
|
252
|
+
nextToken: null,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const mockClient = {
|
|
256
|
+
models: {
|
|
257
|
+
Reservation: { list: mockList },
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
await listReservationsByDateRange(
|
|
262
|
+
mockClient,
|
|
263
|
+
"brand-123",
|
|
264
|
+
"2024-06-01",
|
|
265
|
+
"2024-06-30"
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
269
|
+
filter: {
|
|
270
|
+
and: [
|
|
271
|
+
{ brandId: { eq: "brand-123" } },
|
|
272
|
+
{ checkIn: { ge: "2024-06-01" } },
|
|
273
|
+
{ checkIn: { le: "2024-06-30" } },
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
limit: undefined,
|
|
277
|
+
nextToken: undefined,
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("should support pagination for date range queries", async () => {
|
|
282
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
283
|
+
data: [mockReservation],
|
|
284
|
+
errors: null,
|
|
285
|
+
nextToken: "next-page",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const mockClient = {
|
|
289
|
+
models: {
|
|
290
|
+
Reservation: { list: mockList },
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const result = await listReservationsByDateRange(
|
|
295
|
+
mockClient,
|
|
296
|
+
"brand-123",
|
|
297
|
+
"2024-06-01",
|
|
298
|
+
"2024-06-30",
|
|
299
|
+
{ limit: 10 }
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
expect(result.nextToken).toBe("next-page");
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe("getReservationByConfirmation", () => {
|
|
307
|
+
it("should return reservation matching confirmation code and brand", async () => {
|
|
308
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
309
|
+
data: [mockReservation],
|
|
310
|
+
errors: null,
|
|
311
|
+
nextToken: null,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const mockClient = {
|
|
315
|
+
models: {
|
|
316
|
+
Reservation: { list: mockList },
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const result = await getReservationByConfirmation(
|
|
321
|
+
mockClient,
|
|
322
|
+
"ABC123",
|
|
323
|
+
"brand-123"
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
expect(result).toEqual(mockReservation);
|
|
327
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
328
|
+
filter: {
|
|
329
|
+
and: [
|
|
330
|
+
{ confirmationCode: { eq: "ABC123" } },
|
|
331
|
+
{ brandId: { eq: "brand-123" } },
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
limit: 1,
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("should return null when no matching reservation found", async () => {
|
|
339
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
340
|
+
data: [],
|
|
341
|
+
errors: null,
|
|
342
|
+
nextToken: null,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const mockClient = {
|
|
346
|
+
models: {
|
|
347
|
+
Reservation: { list: mockList },
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const result = await getReservationByConfirmation(
|
|
352
|
+
mockClient,
|
|
353
|
+
"NONEXISTENT",
|
|
354
|
+
"brand-123"
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
expect(result).toBeNull();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should throw on unexpected error", async () => {
|
|
361
|
+
const mockList = vi.fn().mockRejectedValue(new Error("Database error"));
|
|
362
|
+
|
|
363
|
+
const mockClient = {
|
|
364
|
+
models: {
|
|
365
|
+
Reservation: { list: mockList },
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
await expect(
|
|
370
|
+
getReservationByConfirmation(mockClient, "ABC123", "brand-123")
|
|
371
|
+
).rejects.toThrow("Database error");
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
});
|