Skip to content

Commit fd249bd

Browse files
committed
test(vnext): harden column catalog boundaries
1 parent dd5a8f3 commit fd249bd

3 files changed

Lines changed: 411 additions & 57 deletions

File tree

src/vnext/__tests__/column-catalog-batch-coordinator.test.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,30 @@ describe("column catalog batch coordinator", () => {
152152
});
153153
});
154154

155+
it("separates quoted relation and search-path cache identities", async () => {
156+
let calls = 0;
157+
const { owner } = setup((request) => {
158+
calls += 1;
159+
return ready(request);
160+
});
161+
const quoted = {
162+
expectedEpoch: epoch,
163+
relations: [{
164+
path: [{ quoted: true, value: "users" }],
165+
requestKey: "quoted",
166+
}],
167+
searchPaths: [[{ quoted: true, value: "main" }]],
168+
};
169+
170+
await expect(owner.request(quoted).result).resolves.toMatchObject({
171+
status: "usable",
172+
});
173+
await expect(owner.request(quoted).result).resolves.toMatchObject({
174+
status: "usable",
175+
});
176+
expect(calls).toBe(1);
177+
});
178+
155179
it("supports cold null epochs and reuses the observed response epoch", async () => {
156180
const expected: Array<SqlCatalogEpoch | null> = [];
157181
const { owner } = setup((request) => {
@@ -262,6 +286,17 @@ describe("column catalog batch coordinator", () => {
262286
await expect(ticket.result).resolves.toEqual({ status: "cancelled" });
263287
});
264288

289+
it("ignores a provider rejection after its consumer is cancelled", async () => {
290+
const work = deferred<unknown>();
291+
const { owner } = setup(() => work.promise);
292+
const ticket = owner.request(input());
293+
294+
ticket.cancel();
295+
work.reject(new Error("late rejection"));
296+
await expect(ticket.result).resolves.toEqual({ status: "cancelled" });
297+
await Promise.resolve();
298+
});
299+
265300
it("supersedes prior owner work but isolates other owners", async () => {
266301
const pending: Array<ReturnType<typeof deferred<unknown>>> = [];
267302
const { coordinator, owner } = setup(() => {
@@ -301,6 +336,7 @@ describe("column catalog batch coordinator", () => {
301336
});
302337
const ownerTicket = owner.request(input());
303338
owner.dispose();
339+
owner.dispose();
304340
await expect(ownerTicket.result).resolves.toEqual({
305341
reason: "disposed",
306342
status: "unavailable",
@@ -376,6 +412,12 @@ describe("column catalog batch coordinator", () => {
376412
reason: "invalid-provider",
377413
status: "unavailable",
378414
});
415+
expect(createSqlColumnCatalogBatchCoordinator({
416+
provider: {},
417+
})).toEqual({
418+
reason: "invalid-provider",
419+
status: "unavailable",
420+
});
379421
expect(createSqlColumnCatalogBatchCoordinator({
380422
maxCacheEntries: 0,
381423
provider: { id: "catalog", loadColumns: vi.fn() },
@@ -396,10 +438,33 @@ describe("column catalog batch coordinator", () => {
396438
scope: "scope",
397439
});
398440
if (prepared.status !== "prepared") throw new Error("Expected owner");
399-
await expect(prepared.owner.request({
441+
const invalidTicket = prepared.owner.request({
400442
expectedEpoch: epoch,
401443
relations: [],
402444
searchPaths: [],
445+
});
446+
invalidTicket.cancel();
447+
await expect(invalidTicket.result).resolves.toEqual({
448+
reason: "invalid-request",
449+
status: "unavailable",
450+
});
451+
});
452+
453+
it("contains hostile and missing request properties", async () => {
454+
const { owner } = setup(vi.fn());
455+
const hostile = new Proxy({}, {
456+
getOwnPropertyDescriptor() {
457+
throw new Error("hostile descriptor");
458+
},
459+
});
460+
461+
await expect(owner.request(hostile).result).resolves.toEqual({
462+
reason: "invalid-request",
463+
status: "unavailable",
464+
});
465+
await expect(owner.request({
466+
expectedEpoch: epoch,
467+
relations: [reference("users")],
403468
}).result).resolves.toEqual({
404469
reason: "invalid-request",
405470
status: "unavailable",

0 commit comments

Comments
 (0)