Files
supabase/apps/studio/components/interfaces/TableGridEditor/TableEntity.utils.test.ts
Joshen Lim 1de298ff31 Reinstate https://github.com/supabase/supabase/pull/45143 into latest master (#47433)
## Context

Previous PR was [here](https://github.com/supabase/supabase/pull/45143)
but it got stale with lots of conflicts so figured it'll be easier redo
it off the latest master

Moves policies page from Auth to Database under an Access Control
section along with Roles. This moves all existing files, applies
redirects, and updates urls to point to the new route

<img width="274" height="412" alt="image"
src="https://github.com/user-attachments/assets/7952c185-64ae-4355-ba36-45397efe1787"
/>

<img width="453" height="471" alt="image"
src="https://github.com/user-attachments/assets/04b3dcb3-48a5-4049-9893-d01109fb46a9"
/>


## To test
- [ ] Verify that policies now live under Database correctly

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a quick navigation shortcut to open **Database > Policies
(RLS)**.
* **Bug Fixes**
* Updated Policies and RLS-related links across the product to open the
**Database policies** area (menus, command palette, context actions,
alerts, and link-outs).
* Added a permanent redirect from the old **auth policies** URL to the
new **database policies** URL.
* **Documentation**
* Updated RLS Dashboard and security checklist instructions to reference
**Database > Policies**.
* **Tests**
  * Adjusted automated tests to validate the new Policies route.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-30 18:49:33 +08:00

209 lines
7.1 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { formatTableRowsToSQL, getTablePoliciesUrl } from './TableEntity.utils'
import type { SupaTable } from '@/components/grid/types'
import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants'
describe('TableEntity.utils: formatTableRowsToSQL', () => {
it('should format rows into a single SQL INSERT statement', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'bigint', format: 'int8', position: 0 },
{ name: 'name', dataType: 'text', format: 'text', position: 1 },
],
name: 'people',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const rows = [
{ id: 1, name: 'Person 1' },
{ id: 2, name: 'Person 2' },
{ id: 3, name: 'Person 3' },
]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "public"."people" ("id", "name") VALUES (1, 'Person 1'), (2, 'Person 2'), (3, 'Person 3');`
expect(result).toBe(expected)
})
it('should not stringify null values', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'bigint', format: 'int8', position: 0 },
{ name: 'name', dataType: 'text', format: 'text', position: 1 },
],
name: 'people',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const rows = [
{ id: 1, name: 'Person 1' },
{ id: 2, name: null },
{ id: 3, name: 'Person 3' },
]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "public"."people" ("id", "name") VALUES (1, 'Person 1'), (2, null), (3, 'Person 3');`
expect(result).toBe(expected)
})
it('should handle PG JSON and array columns', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'bigint', format: 'int8', position: 0 },
{ name: 'name', dataType: 'text', format: 'text', position: 1 },
{ name: 'tags', dataType: 'ARRAY', format: '_text', position: 2 },
{ name: 'metadata', dataType: 'jsonb', format: 'jsonb', position: 3 },
],
name: 'demo',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const rows = [
{
idx: 1,
id: 2,
name: 'Person 1',
tags: ['tag-a', 'tag-c'],
metadata: '{"version": 1}',
},
{
idx: 2,
id: 3,
name: 'ONeil',
tags: ['tag-a'],
metadata: `{"version": 1, "name": "O'Neil"}`,
},
]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "public"."demo" ("id", "name", "tags", "metadata") VALUES (2, 'Person 1', ARRAY['tag-a','tag-c'], '{"version": 1}'), (3, 'ONeil', ARRAY['tag-a'], '{"version": 1, "name": "O''Neil"}');`
expect(result).toBe(expected)
})
it('should emit valid Postgres literals for booleans, numbers and text arrays', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'text', format: 'text', position: 0 },
{ name: 'public', dataType: 'bool', format: 'bool', position: 1 },
{ name: 'avif_autodetection', dataType: 'bool', format: 'bool', position: 2 },
{ name: 'file_size_limit', dataType: 'int8', format: 'int8', position: 3 },
{ name: 'allowed_mime_types', dataType: 'ARRAY', format: '_text', position: 4 },
],
name: 'buckets',
schema: 'storage',
comment: undefined,
estimateRowCount: 1,
}
const rows = [
{
id: 'emails',
public: true,
avif_autodetection: false,
file_size_limit: 10485760,
allowed_mime_types: ['image/*', "image/o'neil"],
},
]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "storage"."buckets" ("id", "public", "avif_autodetection", "file_size_limit", "allowed_mime_types") VALUES ('emails', true, false, 10485760, ARRAY['image/*','image/o''neil']);`
expect(result).toBe(expected)
})
it('should escape fallback string formats outside text and varchar', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [{ name: 'email', dataType: 'USER-DEFINED', format: 'citext', position: 0 }],
name: 'users',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const rows = [{ email: "o'neil@example.com" }]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "public"."users" ("email") VALUES ('o''neil@example.com');`
expect(result).toBe(expected)
})
it('should return an empty string for empty rows', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'bigint', format: 'int8', position: 0 },
{ name: 'name', dataType: 'text', format: 'text', position: 1 },
],
name: 'people',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const result = formatTableRowsToSQL(table, [])
expect(result).toBe('')
})
it('should remove the idx property', () => {
const table: SupaTable = {
id: 1,
type: ENTITY_TYPE.TABLE,
columns: [
{ name: 'id', dataType: 'bigint', format: 'int8', position: 0 },
{ name: 'name', dataType: 'text', format: 'text', position: 1 },
],
name: 'people',
schema: 'public',
comment: undefined,
estimateRowCount: 1,
}
const rows = [
{ idx: 0, id: 1, name: 'Person 1' },
{ idx: 1, id: 2, name: 'Person 2' },
]
const result = formatTableRowsToSQL(table, rows)
const expected = `INSERT INTO "public"."people" ("id", "name") VALUES (1, 'Person 1'), (2, 'Person 2');`
expect(result).toBe(expected)
})
})
describe('TableEntity.utils: getTablePoliciesUrl', () => {
it('builds the policies url for plain schema and name values', () => {
expect(getTablePoliciesUrl('abc', 'public', 'users')).toBe(
'/project/abc/database/policies?search=users&schema=public'
)
})
it('preserves special characters in the table name', () => {
const url = getTablePoliciesUrl('abc', 'public', 'user_data&secret=1')
const parsed = new URL(url, 'http://example.com')
expect(parsed.searchParams.get('search')).toBe('user_data&secret=1')
expect(parsed.searchParams.get('schema')).toBe('public')
})
it('preserves special characters in the schema', () => {
const url = getTablePoliciesUrl('abc', 'my schema+x', 'users')
const parsed = new URL(url, 'http://example.com')
expect(parsed.searchParams.get('schema')).toBe('my schema+x')
expect(parsed.searchParams.get('search')).toBe('users')
})
it('encodes both the table name and schema together', () => {
const url = getTablePoliciesUrl('abc', 'a&b=c', 'd e+f')
const parsed = new URL(url, 'http://example.com')
expect(parsed.searchParams.get('search')).toBe('d e+f')
expect(parsed.searchParams.get('schema')).toBe('a&b=c')
})
})