mirror of
https://github.com/supabase/supabase.git
synced 2026-10-16 14:49:10 +03:00
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
130 lines
3.8 KiB
TypeScript
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)
|
|
})
|
|
})
|