|
| 1 | +/** |
| 2 | + * @vitest-environment node |
| 3 | + * |
| 4 | + * Verified against https://vault.googleapis.com/$discovery/rest?version=v1: |
| 5 | + * |
| 6 | + * - `AddMatterPermissionsRequest`: "Add an account with the permission specified. The role cannot |
| 7 | + * be owner." `OWNER` is on the `MatterPermission.role` resource enum (the read side) only, so |
| 8 | + * the request surface must not offer it. |
| 9 | + * - `matters.delete`: "Deletes the specified matter. Returns the matter with updated state." The |
| 10 | + * response is `$ref: Matter`, `Matter.state` includes `DELETED`, and `matters.undelete` exists |
| 11 | + * — so it is a reversible soft delete, not a permanent one. |
| 12 | + */ |
| 13 | +import { describe, expect, it } from 'vitest' |
| 14 | +import { GoogleVaultBlock } from '@/blocks/blocks/google_vault' |
| 15 | +import { addMattersPermissionsTool } from '@/tools/google_vault/add_matters_permissions' |
| 16 | +import { deleteMattersTool } from '@/tools/google_vault/delete_matters' |
| 17 | + |
| 18 | +/** Every string the block renders in the canvas sentence for one operation. */ |
| 19 | +function canvasSentenceFor(operation: string): string { |
| 20 | + const byOperation = (GoogleVaultBlock.canvasPresentation?.sentences as any)?.byOperation ?? {} |
| 21 | + const entries = byOperation[operation] ?? [] |
| 22 | + expect(entries.length).toBeGreaterThan(0) |
| 23 | + return entries |
| 24 | + .map((entry: unknown) => (typeof entry === 'string' ? entry : ((entry as any)?.text ?? ''))) |
| 25 | + .join(' ') |
| 26 | +} |
| 27 | + |
| 28 | +describe('the matter-permission surface does not offer the role the API refuses', () => { |
| 29 | + it('the block role dropdown lists COLLABORATOR only', () => { |
| 30 | + const subBlock = GoogleVaultBlock.subBlocks.find((entry) => entry.id === 'role') |
| 31 | + expect(subBlock).toBeDefined() |
| 32 | + |
| 33 | + const ids = (subBlock?.options as Array<{ id: string }>).map((option) => option.id) |
| 34 | + expect(ids).toEqual(['COLLABORATOR']) |
| 35 | + }) |
| 36 | + |
| 37 | + it('the role param description does not advertise OWNER', () => { |
| 38 | + const description = addMattersPermissionsTool.params.role.description ?? '' |
| 39 | + |
| 40 | + expect(description).toContain('COLLABORATOR') |
| 41 | + expect(description.toUpperCase()).not.toContain('OWNER') |
| 42 | + }) |
| 43 | + |
| 44 | + it('the block role input description does not advertise OWNER', () => { |
| 45 | + const description = (GoogleVaultBlock.inputs as any).role?.description ?? '' |
| 46 | + |
| 47 | + expect(description.toUpperCase()).not.toContain('OWNER') |
| 48 | + }) |
| 49 | + |
| 50 | + it('the tool description does not promise ownership transfer', () => { |
| 51 | + expect(addMattersPermissionsTool.description.toLowerCase()).not.toContain('owner') |
| 52 | + }) |
| 53 | +}) |
| 54 | + |
| 55 | +describe('deleting a matter is described as the reversible soft delete it is', () => { |
| 56 | + it('the tool description does not call the delete permanent', () => { |
| 57 | + const description = deleteMattersTool.description.toLowerCase() |
| 58 | + |
| 59 | + expect(description).not.toContain('permanent') |
| 60 | + }) |
| 61 | + |
| 62 | + it('the block canvas sentence does not call the delete permanent', () => { |
| 63 | + expect(canvasSentenceFor('delete_matters').toLowerCase()).not.toContain('permanent') |
| 64 | + }) |
| 65 | + |
| 66 | + it('the block still exposes the undelete operation that makes it reversible', () => { |
| 67 | + const operation = GoogleVaultBlock.subBlocks.find((entry) => entry.id === 'operation') |
| 68 | + const ids = (operation?.options as Array<{ id: string }>).map((option) => option.id) |
| 69 | + |
| 70 | + expect(ids).toContain('undelete_matters') |
| 71 | + }) |
| 72 | +}) |
0 commit comments