-
Notifications
You must be signed in to change notification settings - Fork 326
fix(booking): use the standard visibility rule in the booking pickers #2792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f9bcacb
fix(booking): use the standard visibility rule in the booking pickers
b53014b
Merge branch 'main' into fix/booking-picker-visibility-pattern
DonKoko 8fa6b55
fix(booking): scope the add-to-booking pickers to what the action acc…
DonKoko d139914
fix(booking): make the picker write scope fail closed
DonKoko b3da334
Merge branch 'main' into fix/booking-picker-visibility-pattern
DonKoko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
108 changes: 108 additions & 0 deletions
108
apps/webapp/app/modules/booking/service.server.load-bookings-data.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /** | ||
| * Visibility regression tests for `loadBookingsData`. | ||
| * | ||
| * This is the loader that seeds the "Add to existing booking" pickers. It has | ||
| * to apply the SAME booking-visibility rule as the search endpoint those | ||
| * pickers switch to once the user types (`/api/model-filters`), or the list | ||
| * silently changes the moment someone types into the search box. | ||
| * | ||
| * The rule is the standard one: SELF_SERVICE / BASE users see only bookings | ||
| * they are custodian of, unless the workspace has switched | ||
| * `selfServiceCanSeeBookings` / `baseUserCanSeeBookings` on. `requirePermission` | ||
| * resolves that into `canSeeAllBookings`, which is what this loader now takes. | ||
| * It previously gated on the role alone, so the workspace override never | ||
| * reached these two dialogs. | ||
| * | ||
| * Asserts on the `where` handed to Prisma, because the `where` is the boundary. | ||
| * | ||
| * @see {@link file://./service.server.ts} — `loadBookingsData` | ||
| * @see {@link file://./../../routes/api+/model-filters.ts} | ||
| */ | ||
| import { db } from "~/database/db.server"; | ||
| import { loadBookingsData } from "./service.server"; | ||
|
|
||
| // @vitest-environment node | ||
|
|
||
| // why: the subject is the `where` the loader ends up building, not what a | ||
| // database returns. `count` is mocked because `getBookings` issues it alongside | ||
| // `findMany`; `teamMember.findMany` because `resolveCustodianScope` reads it. | ||
| vitest.mock("~/database/db.server", () => ({ | ||
| db: { | ||
| booking: { | ||
| findMany: vitest.fn().mockResolvedValue([]), | ||
| count: vitest.fn().mockResolvedValue(0), | ||
| }, | ||
| teamMember: { | ||
| findMany: vitest.fn().mockResolvedValue([{ id: "tm-1" }]), | ||
| }, | ||
| }, | ||
| })); | ||
|
|
||
| const findManyMock = db.booking.findMany as unknown as ReturnType< | ||
| typeof vitest.fn | ||
| >; | ||
|
|
||
| const ORGANIZATION_ID = "org-1"; | ||
| const USER_ID = "user-1"; | ||
|
|
||
| /** The restriction `resolveCustodianScope` produces for our fixture user. */ | ||
| const CUSTODIAN_RESTRICTION = { | ||
| OR: [ | ||
| { custodianUserId: USER_ID }, | ||
| { custodianTeamMemberId: { in: ["tm-1"] } }, | ||
| ], | ||
| }; | ||
|
|
||
| /** | ||
| * Runs the loader and returns the `where` Prisma was asked for. | ||
| * | ||
| * @param canSeeAllBookings - The resolved visibility flag under test. | ||
| * @returns The Prisma `where` from the resulting booking query. | ||
| */ | ||
| async function whereFor(canSeeAllBookings: boolean) { | ||
| findManyMock.mockClear(); | ||
|
|
||
| await loadBookingsData({ | ||
| request: new Request( | ||
| "http://localhost/assets/a1/overview/add-to-existing-booking" | ||
| ), | ||
| organizationId: ORGANIZATION_ID, | ||
| userId: USER_ID, | ||
| canSeeAllBookings, | ||
| ids: ["a1"], | ||
| }); | ||
|
|
||
| return findManyMock.mock.calls.at(-1)?.[0]?.where; | ||
| } | ||
|
|
||
| describe("loadBookingsData booking visibility", () => { | ||
| it("restricts to the caller's own bookings when they may not see all", async () => { | ||
| const where = await whereFor(false); | ||
|
|
||
| expect(where.AND).toEqual( | ||
| expect.arrayContaining([expect.objectContaining(CUSTODIAN_RESTRICTION)]) | ||
| ); | ||
| }); | ||
|
|
||
| it("does not restrict when the caller may see all bookings", async () => { | ||
| const where = await whereFor(true); | ||
|
|
||
| const restrictions = (where.AND ?? []).filter( | ||
| (clause: Record<string, unknown>) => | ||
| JSON.stringify(clause).includes("custodianTeamMemberId") || | ||
| JSON.stringify(clause).includes("custodianUserId") | ||
| ); | ||
|
|
||
| expect(restrictions).toEqual([]); | ||
| }); | ||
|
|
||
| it("never drops the draft-visibility clause, whichever way the flag goes", async () => { | ||
| for (const flag of [true, false]) { | ||
| const where = await whereFor(flag); | ||
| const serialized = JSON.stringify(where.AND ?? []); | ||
|
|
||
| expect(serialized).toContain("DRAFT"); | ||
| expect(serialized).toContain("creatorId"); | ||
| } | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.