Files
supabase/apps/studio/components/interfaces/Storage/BucketVersioningFields.utils.test.ts
T
Claude 240ccec9af fix(storage): warn when a retention bound is added where none existed
An enabled bucket with no bound keeps noncurrent versions forever, so
introducing one expires everything past it on save. That went unwarned
because the check required a previous bound to compare against. An
undefined bound still means loading, and stays excluded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N3euXsz25sTybsGTcxCFfn
2026-10-05 15:04:10 +02:00

130 lines
3.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
getRetentionTightening,
RETENTION_TIGHTENING_DESCRIPTION,
toNullableNumber,
} from './BucketVersioningFields.utils'
const params = (overrides: Partial<Parameters<typeof getRetentionTightening>[0]> = {}) => ({
initialVersioningState: 'enabled' as const,
isVersioningEnabled: true,
initialRetentionDays: 30,
initialMaxVersions: 10,
nextRetentionDays: 30,
nextMaxVersions: 10,
...overrides,
})
describe('getRetentionTightening', () => {
it('reports no tightening when nothing changed', () => {
expect(getRetentionTightening(params())).toBe('none')
})
it('detects a shorter retention window', () => {
expect(getRetentionTightening(params({ nextRetentionDays: 7 }))).toBe('days')
})
it('detects a lower version cap', () => {
expect(getRetentionTightening(params({ nextMaxVersions: 3 }))).toBe('versions')
})
it('detects both bounds tightening at once', () => {
expect(getRetentionTightening(params({ nextRetentionDays: 7, nextMaxVersions: 3 }))).toBe(
'both'
)
})
it('does not warn when a bound is raised', () => {
expect(getRetentionTightening(params({ nextRetentionDays: 90, nextMaxVersions: 20 }))).toBe(
'none'
)
})
it('does not warn when a bound is cleared, which retains more rather than less', () => {
expect(getRetentionTightening(params({ nextRetentionDays: null, nextMaxVersions: null }))).toBe(
'none'
)
})
it('warns when a bound is newly added where none existed, which was keeping them forever', () => {
expect(
getRetentionTightening(params({ initialRetentionDays: null, nextRetentionDays: 7 }))
).toBe('days')
})
it('warns when a cap is newly added where none existed', () => {
expect(getRetentionTightening(params({ initialMaxVersions: null, nextMaxVersions: 3 }))).toBe(
'versions'
)
})
it('does not warn when both bounds stay unbounded', () => {
expect(
getRetentionTightening(
params({
initialRetentionDays: null,
initialMaxVersions: null,
nextRetentionDays: null,
nextMaxVersions: null,
})
)
).toBe('none')
})
it('does not warn while the initial bounds are still loading', () => {
expect(
getRetentionTightening(
params({ initialRetentionDays: undefined, initialMaxVersions: undefined })
)
).toBe('none')
})
it('does not warn when versioning is being turned off', () => {
// Suspending never expires what is already retained, so there is nothing to lose.
expect(
getRetentionTightening(params({ isVersioningEnabled: false, nextRetentionDays: 7 }))
).toBe('none')
})
it('does not warn when versioning is being enabled for the first time', () => {
expect(
getRetentionTightening(
params({
initialVersioningState: 'disabled',
initialRetentionDays: null,
initialMaxVersions: null,
nextRetentionDays: 7,
})
)
).toBe('none')
})
it('does not warn when re-enabling a suspended bucket', () => {
// A suspended bucket's stored bounds are cleared, so there is nothing to tighten from.
expect(
getRetentionTightening(params({ initialVersioningState: 'suspended', nextRetentionDays: 7 }))
).toBe('none')
})
it('has a description for every tightening kind it can report', () => {
for (const kind of ['days', 'versions', 'both'] as const) {
expect(RETENTION_TIGHTENING_DESCRIPTION[kind], kind).toBeTruthy()
}
})
})
describe('toNullableNumber', () => {
it('maps the empty sentinel to null', () => {
expect(toNullableNumber('')).toBeNull()
})
it('preserves zero rather than treating it as empty', () => {
expect(toNullableNumber(0)).toBe(0)
})
it('passes numbers through', () => {
expect(toNullableNumber(30)).toBe(30)
})
})