diff --git a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts new file mode 100644 index 0000000000000..8ad91319fa524 --- /dev/null +++ b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts @@ -0,0 +1,53 @@ +import { DailyAutomaticBackupStartTime } from './daily-automatic-backup-start-time'; +import { UnscopedValidationError } from '../../core'; + +describe('DailyAutomaticBackupStartTime', () => { + describe('constructor', () => { + it('should use default values when none provided', () => { + const time = new DailyAutomaticBackupStartTime(); + expect(time.toTimestamp()).toBe('22:00'); + }); + + it('should accept valid time values', () => { + const time = new DailyAutomaticBackupStartTime({ hour: 9, minute: 30 }); + expect(time.toTimestamp()).toBe('09:30'); + }); + + it('should pad single-digit values', () => { + const time = new DailyAutomaticBackupStartTime({ hour: 5, minute: 7 }); + expect(time.toTimestamp()).toBe('05:07'); + }); + + it('should throw for invalid hour', () => { + expect(() => new DailyAutomaticBackupStartTime({ hour: 24 })).toThrow(UnscopedValidationError); + expect(() => new DailyAutomaticBackupStartTime({ hour: -1 })).toThrow(UnscopedValidationError); + }); + + it('should throw for invalid minute', () => { + expect(() => new DailyAutomaticBackupStartTime({ minute: 60 })).toThrow(UnscopedValidationError); + expect(() => new DailyAutomaticBackupStartTime({ minute: -1 })).toThrow(UnscopedValidationError); + }); + }); + + describe('fromString', () => { + it('should parse valid time strings', () => { + expect(DailyAutomaticBackupStartTime.fromString('00:00').toTimestamp()).toBe('00:00'); + expect(DailyAutomaticBackupStartTime.fromString('23:59').toTimestamp()).toBe('23:59'); + expect(DailyAutomaticBackupStartTime.fromString('09:05').toTimestamp()).toBe('09:05'); + }); + + it('should throw for invalid formats', () => { + expect(() => DailyAutomaticBackupStartTime.fromString('24:00')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('12:60')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('abc')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('12:00:00')).toThrow(UnscopedValidationError); + }); + }); + + describe('toTimestamp', () => { + it('should return properly formatted string', () => { + expect(new DailyAutomaticBackupStartTime({ hour: 1, minute: 2 }).toTimestamp()).toBe('01:02'); + expect(new DailyAutomaticBackupStartTime({ hour: 10, minute: 20 }).toTimestamp()).toBe('10:20'); + }); + }); +}); diff --git a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts index ef9229a62ccf8..5c7373ec9c767 100644 --- a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts +++ b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts @@ -6,12 +6,14 @@ import { UnscopedValidationError } from '../../core'; export interface DailyAutomaticBackupStartTimeProps { /** * The hour of the day (from 0-23) for automatic backup starts. + * @default 22 */ - readonly hour: number; + readonly hour?: number; /** * The minute of the hour (from 0-59) for automatic backup starts. + * @default 0 */ - readonly minute: number; + readonly minute?: number; } /** @@ -21,50 +23,71 @@ export class DailyAutomaticBackupStartTime { /** * The start hour of the automatic backup time in Coordinated Universal Time (UTC), using 24-hour time. * For example, 17 refers to 5:00 P.M. UTC. - * - * @default - 22 */ private readonly hour: string; /** * The start minute of the automatic backup time, in UTC. - * - * @default - 0 */ private readonly minute: string; - constructor(props: DailyAutomaticBackupStartTimeProps) { - this.validate(props.hour, props.minute); + /** + * Creates a new DailyAutomaticBackupStartTime instance. + * @param props Configuration properties (defaults to hour:22, minute:0) + */ + constructor(props: DailyAutomaticBackupStartTimeProps = {}) { + const hour = props.hour ?? 22; + const minute = props.minute ?? 0; + this.validate(hour, minute); + + this.hour = this.getTwoDigitString(hour); + this.minute = this.getTwoDigitString(minute); + } + + /** + * Creates an instance from a "HH:MM" formatted string. + * @param timeString Time string in "HH:MM" format (24-hour) + * @returns New DailyAutomaticBackupStartTime instance + * @throws UnscopedValidationError if format is invalid + */ + public static fromString(timeString: string): DailyAutomaticBackupStartTime { + const timeRegex = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/; + if (!timeRegex.test(timeString)) { + throw new UnscopedValidationError(`Time must be in "HH:MM" format (24-hour). Received: ${timeString}`); + } - this.hour = this.getTwoDigitString(props.hour); - this.minute = this.getTwoDigitString(props.minute); + const [hour, minute] = timeString.split(':').map(Number); + return new DailyAutomaticBackupStartTime({ hour, minute }); } + /** - * Converts an hour, and minute into HH:MM string. + * Converts the time to "HH:MM" string format. + * @returns Time string in "HH:MM" format */ public toTimestamp(): string { return `${this.hour}:${this.minute}`; } /** - * Pad an integer so that it always contains at least 2 digits. Assumes the number is a positive integer. + * Pad an integer to 2 digits. Assumes the number is a positive integer. + * @param n Number to pad + * @returns 2-digit string representation */ private getTwoDigitString(n: number): string { - const numberString = n.toString(); - if (numberString.length === 1) { - return `0${n}`; - } - return numberString; + return n.toString().padStart(2, '0'); } /** - * Validation needed for the values of the daily automatic backup time. + * Validates hour and minute values. + * @param hour Hour value (0-23) + * @param minute Minute value (0-59) + * @throws UnscopedValidationError if validation fails */ - private validate(hour: number, minute: number) { + private validate(hour: number, minute: number): void { if (!Number.isInteger(hour) || hour < 0 || hour > 23) { - throw new UnscopedValidationError(`dailyAutomaticBackupStartTime hour must be an integer between 0 and 24. received: ${hour}`); + throw new UnscopedValidationError(`hour must be an integer between 0 and 23. Received: ${hour}`); } if (!Number.isInteger(minute) || minute < 0 || minute > 59) { - throw new UnscopedValidationError(`dailyAutomaticBackupStartTime minute must be an integer between 0 and 59. received: ${minute}`); + throw new UnscopedValidationError(`minute must be an integer between 0 and 59. Received: ${minute}`); } } }