diff --git a/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.test.ts b/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.test.ts
new file mode 100644
index 00000000000..2cd87a61879
--- /dev/null
+++ b/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.test.ts
@@ -0,0 +1,111 @@
+import { describe, expect, it } from 'vitest'
+
+import { DatabaseFormSchema } from './PerformanceSettingsForm'
+
+describe('DatabaseFormSchema', () => {
+ describe('percent unit', () => {
+ it('accepts 1 (lower bound)', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 1,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('accepts 100 (upper bound)', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 100,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('accepts a mid-range value', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 50,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('rejects 0', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 0,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(false)
+ })
+
+ it('rejects 101', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 101,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(false)
+ if (!result.success) {
+ const issue = result.error.issues.find((i) => i.path[0] === 'DB_MAX_POOL_SIZE')
+ expect(issue?.message).toBe('Percentage must be between 1 and 100')
+ }
+ })
+
+ it('rejects negative values', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: -5,
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(false)
+ })
+
+ it('coerces string input', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: '75',
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('rejects out-of-range string input', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: '150',
+ DB_MAX_POOL_SIZE_UNIT: 'percent',
+ })
+ expect(result.success).toBe(false)
+ })
+ })
+
+ describe('connections unit', () => {
+ it('accepts 1 (min)', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 1,
+ DB_MAX_POOL_SIZE_UNIT: 'connections',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('accepts values above 100 (no upper schema bound for absolute)', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 500,
+ DB_MAX_POOL_SIZE_UNIT: 'connections',
+ })
+ expect(result.success).toBe(true)
+ })
+
+ it('rejects 0', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 0,
+ DB_MAX_POOL_SIZE_UNIT: 'connections',
+ })
+ expect(result.success).toBe(false)
+ })
+ })
+
+ describe('unit enum', () => {
+ it('rejects unknown unit values', () => {
+ const result = DatabaseFormSchema.safeParse({
+ DB_MAX_POOL_SIZE: 10,
+ DB_MAX_POOL_SIZE_UNIT: 'bytes',
+ })
+ expect(result.success).toBe(false)
+ })
+ })
+})
diff --git a/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.tsx b/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.tsx
index 71fe409e6e3..9235a5f47e8 100644
--- a/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.tsx
+++ b/apps/studio/components/interfaces/Auth/PerformanceSettingsForm.tsx
@@ -46,6 +46,23 @@ const FormSchema = z.object({
DB_MAX_POOL_SIZE_UNIT: z.enum(['percent', 'connections']),
})
+export const DatabaseFormSchema = z
+ .object({
+ DB_MAX_POOL_SIZE: z.coerce.number().min(1),
+ DB_MAX_POOL_SIZE_UNIT: z.enum(['percent', 'connections']),
+ })
+ .superRefine((data, ctx) => {
+ if (data.DB_MAX_POOL_SIZE_UNIT === 'percent') {
+ if (data.DB_MAX_POOL_SIZE < 1 || data.DB_MAX_POOL_SIZE > 100) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ path: ['DB_MAX_POOL_SIZE'],
+ message: 'Percentage must be between 1 and 100',
+ })
+ }
+ }
+ })
+
export const PerformanceSettingsForm = () => {
const { data: project } = useSelectedProjectQuery()
const { hasAccess: hasAccessToPerformance, isLoading: isLoadingEntitlement } =
@@ -87,12 +104,7 @@ export const PerformanceSettingsForm = () => {
})
const databaseForm = useForm({
- resolver: zodResolver(
- z.object({
- DB_MAX_POOL_SIZE: FormSchema.shape.DB_MAX_POOL_SIZE,
- DB_MAX_POOL_SIZE_UNIT: FormSchema.shape.DB_MAX_POOL_SIZE_UNIT,
- })
- ),
+ resolver: zodResolver(DatabaseFormSchema),
defaultValues: {
DB_MAX_POOL_SIZE: 10,
DB_MAX_POOL_SIZE_UNIT: 'connections',
@@ -375,12 +387,6 @@ export const PerformanceSettingsForm = () => {