Skip to content

Commit 5babcde

Browse files
committed
Fix tasks test, improve utils
1 parent c6e39ac commit 5babcde

File tree

8 files changed

+325
-323
lines changed

8 files changed

+325
-323
lines changed

tests/tasks-and-batches.test.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { randomUUID } from "node:crypto";
22
import { beforeAll, describe, test, vi } from "vitest";
33
import type { TasksOrBatchesQuery } from "../src/types/index.js";
4-
import { getClient, objectEntries } from "./utils/meilisearch-test-utils.js";
54
import {
5+
getClient,
6+
objectEntries,
67
assert,
7-
possibleTaskTypes,
8+
} from "./utils/meilisearch-test-utils.js";
9+
import {
810
possibleTaskStatuses,
9-
} from "./utils/tasks-and-batches.js";
11+
possibleTaskTypes,
12+
} from "./utils/assertions/tasks-and-batches.js";
1013

1114
const INDEX_UID = randomUUID();
1215
const ms = await getClient("Master");
@@ -188,11 +191,11 @@ describe.for(objectEntries(testValuesRecord))("%s", ([key, testValues]) => {
188191
test.for(testValues)(
189192
`${ms.tasks.getTasks.name} method%s`,
190193
async ([, value]) => {
191-
const { results, ...r } = await ms.tasks.getTasks({ [key]: value });
194+
const tasksResults = await ms.tasks.getTasks({ [key]: value });
192195

193-
assert.isResult(r);
196+
assert.isTasksOrBatchesResults(tasksResults);
194197

195-
for (const task of results) {
198+
for (const task of tasksResults.results) {
196199
assert.isTask(task);
197200
}
198201
},
@@ -201,11 +204,11 @@ describe.for(objectEntries(testValuesRecord))("%s", ([key, testValues]) => {
201204
test.for(testValues)(
202205
`${ms.batches.getBatches.name} method%s`,
203206
async ([, value]) => {
204-
const { results, ...r } = await ms.batches.getBatches({ [key]: value });
207+
const batchesResults = await ms.batches.getBatches({ [key]: value });
205208

206-
assert.isResult(r);
209+
assert.isTasksOrBatchesResults(batchesResults);
207210

208-
for (const batch of results) {
211+
for (const batch of batchesResults.results) {
209212
assert.isBatch(batch);
210213
}
211214
},

tests/utils/assert.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { assert } from "vitest";
2+
import { errorAssertions } from "./assertions/error.js";
3+
import { promiseAssertions } from "./assertions/promise.js";
4+
import { tasksAndBatchesAssertions } from "./assertions/tasks-and-batches.js";
5+
6+
const source = {
7+
...errorAssertions,
8+
...promiseAssertions,
9+
...tasksAndBatchesAssertions,
10+
};
11+
12+
const customAssert: typeof assert & typeof source = Object.assign(
13+
assert,
14+
source,
15+
);
16+
17+
// needs to be named assert to satisfy Vitest ESLint plugin in tests
18+
export { customAssert as assert };

tests/utils/assertions/error.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { assert } from "vitest";
2+
import type { MeiliSearchErrorResponse } from "../../../src/index.js";
3+
4+
export const errorAssertions = {
5+
isErrorResponse(error: MeiliSearchErrorResponse) {
6+
assert.lengthOf(Object.keys(error), 4);
7+
const { message, code, type, link } = error;
8+
for (const val of Object.values({ message, code, type, link })) {
9+
assert.typeOf(val, "string");
10+
}
11+
},
12+
};

tests/utils/assertions/promise.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { assert } from "vitest";
2+
3+
const NOT_RESOLVED = Symbol("<not resolved>");
4+
const RESOLVED = Symbol("<resolved>");
5+
6+
export const promiseAssertions = {
7+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8+
async rejects<T extends { new (...args: any[]): any }>(
9+
promise: Promise<unknown>,
10+
errorConstructor: T,
11+
errMsgMatcher?: RegExp | string,
12+
): Promise<InstanceType<T>> {
13+
let resolvedValue;
14+
15+
try {
16+
resolvedValue = await promise;
17+
} catch (error) {
18+
assert.instanceOf(error, errorConstructor);
19+
20+
if (errMsgMatcher !== undefined) {
21+
const { message } = error as Error;
22+
if (typeof errMsgMatcher === "string") {
23+
assert.strictEqual(message, errMsgMatcher);
24+
} else {
25+
assert.match(message, errMsgMatcher);
26+
}
27+
}
28+
29+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
30+
return error as InstanceType<T>;
31+
}
32+
33+
assert.fail(resolvedValue, NOT_RESOLVED, "expected value to not resolve");
34+
},
35+
36+
async resolves(promise: Promise<unknown>): Promise<void> {
37+
try {
38+
await promise;
39+
} catch (error) {
40+
assert.fail(error, RESOLVED, "expected value to not reject");
41+
}
42+
},
43+
};
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import type {
2+
TasksResults,
3+
Batch,
4+
TaskType,
5+
TaskStatus,
6+
EnqueuedTask,
7+
BatchesResults,
8+
Task,
9+
} from "../../../src/types/index.js";
10+
import { assert } from "vitest";
11+
import { objectKeys } from "../object.js";
12+
import { errorAssertions } from "./error.js";
13+
14+
export const possibleTaskStatuses = objectKeys<TaskStatus>({
15+
enqueued: null,
16+
processing: null,
17+
succeeded: null,
18+
failed: null,
19+
canceled: null,
20+
});
21+
22+
export const possibleTaskTypes = objectKeys<TaskType>({
23+
documentAdditionOrUpdate: null,
24+
documentEdition: null,
25+
documentDeletion: null,
26+
settingsUpdate: null,
27+
indexCreation: null,
28+
indexDeletion: null,
29+
indexUpdate: null,
30+
indexSwap: null,
31+
taskCancelation: null,
32+
taskDeletion: null,
33+
dumpCreation: null,
34+
snapshotCreation: null,
35+
upgradeDatabase: null,
36+
});
37+
38+
export const tasksAndBatchesAssertions = {
39+
isEnqueuedTask(enqueuedTask: EnqueuedTask) {
40+
assert.lengthOf(Object.keys(enqueuedTask), 5);
41+
42+
const { taskUid, indexUid, status, type, enqueuedAt } = enqueuedTask;
43+
44+
assert.typeOf(taskUid, "number");
45+
assert(
46+
indexUid === null || typeof indexUid === "string",
47+
`expected ${indexUid} to be null or string`,
48+
);
49+
assert.oneOf(status, possibleTaskStatuses);
50+
assert.oneOf(type, possibleTaskTypes);
51+
assert.typeOf(enqueuedAt, "string");
52+
},
53+
54+
isBatch(batch: Batch) {
55+
assert.lengthOf(Object.keys(batch), 8);
56+
57+
const { uid, progress, details, stats, duration, startedAt, finishedAt } =
58+
batch;
59+
60+
assert.typeOf(uid, "number");
61+
assert(
62+
typeof progress === "object",
63+
"expected progress to be of type object or null",
64+
);
65+
66+
if (progress !== null) {
67+
assert.lengthOf(Object.keys(progress), 2);
68+
const { steps, percentage } = progress;
69+
70+
for (const step of steps) {
71+
assert.lengthOf(Object.keys(step), 3);
72+
73+
const { currentStep, finished, total } = step;
74+
75+
assert.typeOf(currentStep, "string");
76+
assert.typeOf(finished, "number");
77+
assert.typeOf(total, "number");
78+
}
79+
80+
assert.typeOf(percentage, "number");
81+
}
82+
83+
assert.typeOf(details, "object");
84+
85+
const { length } = Object.keys(stats);
86+
87+
assert.isAtLeast(length, 4);
88+
assert.isAtMost(length, 7);
89+
90+
const {
91+
totalNbTasks,
92+
status,
93+
types,
94+
indexUids,
95+
progressTrace,
96+
writeChannelCongestion,
97+
internalDatabaseSizes,
98+
} = stats;
99+
100+
assert.typeOf(totalNbTasks, "number");
101+
102+
for (const [key, val] of Object.entries(status)) {
103+
assert.oneOf(key, possibleTaskStatuses);
104+
assert.typeOf(val, "number");
105+
}
106+
107+
for (const [key, val] of Object.entries(types)) {
108+
assert.oneOf(key, possibleTaskTypes);
109+
assert.typeOf(val, "number");
110+
}
111+
112+
for (const val of Object.values(indexUids)) {
113+
assert.typeOf(val, "number");
114+
}
115+
116+
assert(
117+
progressTrace === undefined ||
118+
(progressTrace !== null && typeof progressTrace === "object"),
119+
"expected progressTrace to be undefined or an object",
120+
);
121+
122+
assert(
123+
writeChannelCongestion === undefined ||
124+
(writeChannelCongestion !== null &&
125+
typeof writeChannelCongestion === "object"),
126+
"expected writeChannelCongestion to be undefined or an object",
127+
);
128+
129+
assert(
130+
internalDatabaseSizes === undefined ||
131+
(internalDatabaseSizes !== null &&
132+
typeof internalDatabaseSizes === "object"),
133+
"expected internalDatabaseSizes to be undefined or an object",
134+
);
135+
136+
assert(
137+
duration === null || typeof duration === "string",
138+
"expected duration to be null or string",
139+
);
140+
141+
assert.typeOf(startedAt, "string");
142+
143+
assert(
144+
finishedAt === null || typeof finishedAt === "string",
145+
"expected finishedAt to be null or string",
146+
);
147+
},
148+
149+
isTasksOrBatchesResults(value: TasksResults | BatchesResults) {
150+
assert.lengthOf(Object.keys(value), 5);
151+
152+
const { results, total, limit, from, next } = value;
153+
154+
// it's up to individual tests to assert the exact type of each element
155+
assert.isArray(results);
156+
157+
assert.typeOf(total, "number");
158+
assert.typeOf(limit, "number");
159+
160+
if (from !== null) {
161+
assert.typeOf(from, "number");
162+
}
163+
164+
if (next !== null) {
165+
assert.typeOf(next, "number");
166+
}
167+
},
168+
169+
isTask(task: Task) {
170+
const { length } = Object.keys(task);
171+
172+
assert.isAtLeast(length, 11);
173+
assert.isAtMost(length, 13);
174+
175+
const {
176+
indexUid,
177+
status,
178+
type,
179+
enqueuedAt,
180+
uid,
181+
batchUid,
182+
canceledBy,
183+
details,
184+
error,
185+
duration,
186+
startedAt,
187+
finishedAt,
188+
network,
189+
} = task;
190+
191+
assert(indexUid === null || typeof indexUid === "string");
192+
193+
assert.oneOf(status, possibleTaskStatuses);
194+
195+
assert.oneOf(type, possibleTaskTypes);
196+
197+
assert.typeOf(enqueuedAt, "string");
198+
assert.typeOf(uid, "number");
199+
assert(batchUid === null || typeof batchUid === "number");
200+
assert(canceledBy === null || typeof canceledBy === "number");
201+
202+
// it's up to individual tests to assert the exact shape of this property
203+
assert(
204+
details === undefined ||
205+
(details !== null && typeof details === "object"),
206+
);
207+
208+
assert(typeof error === "object");
209+
if (error !== null) {
210+
errorAssertions.isErrorResponse(error);
211+
}
212+
213+
assert(duration === null || typeof duration === "string");
214+
assert(startedAt === null || typeof startedAt === "string");
215+
assert(finishedAt === null || typeof finishedAt === "string");
216+
217+
// it's up to individual tests to assert the exact shape of this property
218+
assert(
219+
network === undefined ||
220+
(network !== null && typeof network === "object"),
221+
);
222+
},
223+
224+
isTaskSuccessful(task: Task) {
225+
this.isTask(task);
226+
assert.isNull(task.error);
227+
assert.strictEqual(task.status, "succeeded");
228+
},
229+
};

0 commit comments

Comments
 (0)