Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,31 @@ describe("View contract — invariants across all view kinds", () => {
});


describe("View contract — rowidsInRange clamps a negative start", () => {
// rowidsInRange already clamps end at length() and returns [] for an
// inverted range (see the IdentityView/SortView tests above), so it is
// a defensive range accessor. A negative start must clamp to 0 the same
// way, and all three view kinds must agree. Otherwise a boundary window
// (the renderer asking for e.g. [-3, 2) near row 0) yields nonexistent
// negative rowids from IdentityView but an empty slice from
// SortView/FilterView — a silent divergence the RowStore can't satisfy.
const cases: Array<[string, View]> = [
["IdentityView(8)", new IdentityView(8)],
["SortView", new SortView("k", "asc", Int32Array.from([3, 1, 4, 1, 5, 9, 2, 6]))],
["FilterView", new FilterView("f", Int32Array.from([3, 1, 4, 1, 5, 9, 2, 6]))],
];

test.each(cases)("%s — rowidsInRange(-3, 2) clamps start to 0", (_label, v) => {
expect(v.rowidsInRange(-3, 2)).toStrictEqual(v.rowidsInRange(0, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Implement the negative-start clamp under test

This new contract is added without the corresponding View.rowidsInRange implementation change, so the Views.test.ts suite becomes red: IdentityView.rowidsInRange(-3, 2) currently returns [-3, -2, -1, 0, 1], while SortView/FilterView return an empty slice for this range, so this assertion fails for all three cases. Until Views.ts clamps start to 0, any CI job that runs the package Jest tests will fail on this commit.

Useful? React with 👍 / 👎.

});

test("IdentityView never emits negative rowids for a negative start", () => {
const v = new IdentityView(10);
expect(v.rowidsInRange(-3, 2)).toStrictEqual([0, 1]);
});
});


describe("View — out-of-bounds positionAt", () => {
// Phase 2 contract: callers are expected to clamp via length()
// before calling positionAt. The view does not need to be defensive
Expand Down
17 changes: 10 additions & 7 deletions packages/buckaroo-js-core/src/components/DFViewerParts/Views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ export class IdentityView implements View {
}

public rowidsInRange(start: number, end: number): number[] {
const clampedStart = Math.max(0, start);
const clampedEnd = Math.min(end, this._length);
if (clampedEnd <= start) return [];
const out: number[] = new Array(clampedEnd - start);
for (let i = 0; i < out.length; i++) out[i] = start + i;
if (clampedEnd <= clampedStart) return [];
const out: number[] = new Array(clampedEnd - clampedStart);
for (let i = 0; i < out.length; i++) out[i] = clampedStart + i;
return out;
}

Expand Down Expand Up @@ -86,9 +87,10 @@ export class SortView implements View {
}

public rowidsInRange(start: number, end: number): number[] {
const clampedStart = Math.max(0, start);
const clampedEnd = Math.min(end, this.rowidOrder.length);
if (clampedEnd <= start) return [];
return Array.from(this.rowidOrder.subarray(start, clampedEnd));
if (clampedEnd <= clampedStart) return [];
return Array.from(this.rowidOrder.subarray(clampedStart, clampedEnd));
}

public viewKey(): string {
Expand All @@ -115,9 +117,10 @@ export class FilterView implements View {
}

public rowidsInRange(start: number, end: number): number[] {
const clampedStart = Math.max(0, start);
const clampedEnd = Math.min(end, this.rowidSubset.length);
if (clampedEnd <= start) return [];
return Array.from(this.rowidSubset.subarray(start, clampedEnd));
if (clampedEnd <= clampedStart) return [];
return Array.from(this.rowidSubset.subarray(clampedStart, clampedEnd));
}

public viewKey(): string {
Expand Down
Loading