s3api_object_retention_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  1. package s3api
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  10. )
  11. func TestValidateRetention(t *testing.T) {
  12. tests := []struct {
  13. name string
  14. retention *ObjectRetention
  15. expectError bool
  16. errorMsg string
  17. }{
  18. {
  19. name: "Valid GOVERNANCE retention",
  20. retention: &ObjectRetention{
  21. Mode: s3_constants.RetentionModeGovernance,
  22. RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
  23. },
  24. expectError: false,
  25. },
  26. {
  27. name: "Valid COMPLIANCE retention",
  28. retention: &ObjectRetention{
  29. Mode: s3_constants.RetentionModeCompliance,
  30. RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
  31. },
  32. expectError: false,
  33. },
  34. {
  35. name: "Missing Mode",
  36. retention: &ObjectRetention{
  37. RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
  38. },
  39. expectError: true,
  40. errorMsg: "retention configuration must specify Mode",
  41. },
  42. {
  43. name: "Missing RetainUntilDate",
  44. retention: &ObjectRetention{
  45. Mode: s3_constants.RetentionModeGovernance,
  46. },
  47. expectError: true,
  48. errorMsg: "retention configuration must specify RetainUntilDate",
  49. },
  50. {
  51. name: "Invalid Mode",
  52. retention: &ObjectRetention{
  53. Mode: "INVALID_MODE",
  54. RetainUntilDate: timePtr(time.Now().Add(24 * time.Hour)),
  55. },
  56. expectError: true,
  57. errorMsg: "invalid retention mode",
  58. },
  59. {
  60. name: "Past RetainUntilDate",
  61. retention: &ObjectRetention{
  62. Mode: s3_constants.RetentionModeGovernance,
  63. RetainUntilDate: timePtr(time.Now().Add(-24 * time.Hour)),
  64. },
  65. expectError: true,
  66. errorMsg: "retain until date must be in the future",
  67. },
  68. {
  69. name: "Empty retention",
  70. retention: &ObjectRetention{},
  71. expectError: true,
  72. errorMsg: "retention configuration must specify Mode",
  73. },
  74. }
  75. for _, tt := range tests {
  76. t.Run(tt.name, func(t *testing.T) {
  77. err := ValidateRetention(tt.retention)
  78. if tt.expectError {
  79. if err == nil {
  80. t.Errorf("Expected error but got none")
  81. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  82. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  83. }
  84. } else {
  85. if err != nil {
  86. t.Errorf("Unexpected error: %v", err)
  87. }
  88. }
  89. })
  90. }
  91. }
  92. func TestValidateLegalHold(t *testing.T) {
  93. tests := []struct {
  94. name string
  95. legalHold *ObjectLegalHold
  96. expectError bool
  97. errorMsg string
  98. }{
  99. {
  100. name: "Valid ON status",
  101. legalHold: &ObjectLegalHold{
  102. Status: s3_constants.LegalHoldOn,
  103. },
  104. expectError: false,
  105. },
  106. {
  107. name: "Valid OFF status",
  108. legalHold: &ObjectLegalHold{
  109. Status: s3_constants.LegalHoldOff,
  110. },
  111. expectError: false,
  112. },
  113. {
  114. name: "Invalid status",
  115. legalHold: &ObjectLegalHold{
  116. Status: "INVALID_STATUS",
  117. },
  118. expectError: true,
  119. errorMsg: "invalid legal hold status",
  120. },
  121. {
  122. name: "Empty status",
  123. legalHold: &ObjectLegalHold{
  124. Status: "",
  125. },
  126. expectError: true,
  127. errorMsg: "invalid legal hold status",
  128. },
  129. {
  130. name: "Lowercase on",
  131. legalHold: &ObjectLegalHold{
  132. Status: "on",
  133. },
  134. expectError: true,
  135. errorMsg: "invalid legal hold status",
  136. },
  137. {
  138. name: "Lowercase off",
  139. legalHold: &ObjectLegalHold{
  140. Status: "off",
  141. },
  142. expectError: true,
  143. errorMsg: "invalid legal hold status",
  144. },
  145. }
  146. for _, tt := range tests {
  147. t.Run(tt.name, func(t *testing.T) {
  148. err := ValidateLegalHold(tt.legalHold)
  149. if tt.expectError {
  150. if err == nil {
  151. t.Errorf("Expected error but got none")
  152. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  153. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  154. }
  155. } else {
  156. if err != nil {
  157. t.Errorf("Unexpected error: %v", err)
  158. }
  159. }
  160. })
  161. }
  162. }
  163. func TestParseObjectRetention(t *testing.T) {
  164. tests := []struct {
  165. name string
  166. xmlBody string
  167. expectError bool
  168. errorMsg string
  169. expectedResult *ObjectRetention
  170. }{
  171. {
  172. name: "Valid retention XML",
  173. xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  174. <Mode>GOVERNANCE</Mode>
  175. <RetainUntilDate>2024-12-31T23:59:59Z</RetainUntilDate>
  176. </Retention>`,
  177. expectError: false,
  178. expectedResult: &ObjectRetention{
  179. Mode: "GOVERNANCE",
  180. RetainUntilDate: timePtr(time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)),
  181. },
  182. },
  183. {
  184. name: "Valid compliance retention XML",
  185. xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  186. <Mode>COMPLIANCE</Mode>
  187. <RetainUntilDate>2025-01-01T00:00:00Z</RetainUntilDate>
  188. </Retention>`,
  189. expectError: false,
  190. expectedResult: &ObjectRetention{
  191. Mode: "COMPLIANCE",
  192. RetainUntilDate: timePtr(time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)),
  193. },
  194. },
  195. {
  196. name: "Empty XML body",
  197. xmlBody: "",
  198. expectError: true,
  199. errorMsg: "error parsing XML",
  200. },
  201. {
  202. name: "Invalid XML",
  203. xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Mode>GOVERNANCE</Mode><RetainUntilDate>invalid-date</RetainUntilDate></Retention>`,
  204. expectError: true,
  205. errorMsg: "cannot parse",
  206. },
  207. {
  208. name: "Malformed XML",
  209. xmlBody: "<Retention><Mode>GOVERNANCE</Mode><RetainUntilDate>2024-12-31T23:59:59Z</Retention>",
  210. expectError: true,
  211. errorMsg: "error parsing XML",
  212. },
  213. {
  214. name: "Missing Mode",
  215. xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  216. <RetainUntilDate>2024-12-31T23:59:59Z</RetainUntilDate>
  217. </Retention>`,
  218. expectError: false,
  219. expectedResult: &ObjectRetention{
  220. Mode: "",
  221. RetainUntilDate: timePtr(time.Date(2024, 12, 31, 23, 59, 59, 0, time.UTC)),
  222. },
  223. },
  224. {
  225. name: "Missing RetainUntilDate",
  226. xmlBody: `<Retention xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  227. <Mode>GOVERNANCE</Mode>
  228. </Retention>`,
  229. expectError: false,
  230. expectedResult: &ObjectRetention{
  231. Mode: "GOVERNANCE",
  232. RetainUntilDate: nil,
  233. },
  234. },
  235. }
  236. for _, tt := range tests {
  237. t.Run(tt.name, func(t *testing.T) {
  238. // Create a mock HTTP request with XML body
  239. req := &http.Request{
  240. Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
  241. }
  242. result, err := parseObjectRetention(req)
  243. if tt.expectError {
  244. if err == nil {
  245. t.Errorf("Expected error but got none")
  246. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  247. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  248. }
  249. } else {
  250. if err != nil {
  251. t.Errorf("Unexpected error: %v", err)
  252. }
  253. if result == nil {
  254. t.Errorf("Expected result but got nil")
  255. } else {
  256. if result.Mode != tt.expectedResult.Mode {
  257. t.Errorf("Expected Mode %s, got %s", tt.expectedResult.Mode, result.Mode)
  258. }
  259. if tt.expectedResult.RetainUntilDate == nil {
  260. if result.RetainUntilDate != nil {
  261. t.Errorf("Expected RetainUntilDate to be nil, got %v", result.RetainUntilDate)
  262. }
  263. } else if result.RetainUntilDate == nil {
  264. t.Errorf("Expected RetainUntilDate to be %v, got nil", tt.expectedResult.RetainUntilDate)
  265. } else if !result.RetainUntilDate.Equal(*tt.expectedResult.RetainUntilDate) {
  266. t.Errorf("Expected RetainUntilDate %v, got %v", tt.expectedResult.RetainUntilDate, result.RetainUntilDate)
  267. }
  268. }
  269. }
  270. })
  271. }
  272. }
  273. func TestParseObjectLegalHold(t *testing.T) {
  274. tests := []struct {
  275. name string
  276. xmlBody string
  277. expectError bool
  278. errorMsg string
  279. expectedResult *ObjectLegalHold
  280. }{
  281. {
  282. name: "Valid legal hold ON",
  283. xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  284. <Status>ON</Status>
  285. </LegalHold>`,
  286. expectError: false,
  287. expectedResult: &ObjectLegalHold{
  288. Status: "ON",
  289. },
  290. },
  291. {
  292. name: "Valid legal hold OFF",
  293. xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  294. <Status>OFF</Status>
  295. </LegalHold>`,
  296. expectError: false,
  297. expectedResult: &ObjectLegalHold{
  298. Status: "OFF",
  299. },
  300. },
  301. {
  302. name: "Empty XML body",
  303. xmlBody: "",
  304. expectError: true,
  305. errorMsg: "error parsing XML",
  306. },
  307. {
  308. name: "Invalid XML",
  309. xmlBody: "<LegalHold><Status>ON</Status>",
  310. expectError: true,
  311. errorMsg: "error parsing XML",
  312. },
  313. {
  314. name: "Missing Status",
  315. xmlBody: `<LegalHold xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  316. </LegalHold>`,
  317. expectError: false,
  318. expectedResult: &ObjectLegalHold{
  319. Status: "",
  320. },
  321. },
  322. }
  323. for _, tt := range tests {
  324. t.Run(tt.name, func(t *testing.T) {
  325. // Create a mock HTTP request with XML body
  326. req := &http.Request{
  327. Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
  328. }
  329. result, err := parseObjectLegalHold(req)
  330. if tt.expectError {
  331. if err == nil {
  332. t.Errorf("Expected error but got none")
  333. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  334. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  335. }
  336. } else {
  337. if err != nil {
  338. t.Errorf("Unexpected error: %v", err)
  339. }
  340. if result == nil {
  341. t.Errorf("Expected result but got nil")
  342. } else {
  343. if result.Status != tt.expectedResult.Status {
  344. t.Errorf("Expected Status %s, got %s", tt.expectedResult.Status, result.Status)
  345. }
  346. }
  347. }
  348. })
  349. }
  350. }
  351. func TestParseObjectLockConfiguration(t *testing.T) {
  352. tests := []struct {
  353. name string
  354. xmlBody string
  355. expectError bool
  356. errorMsg string
  357. expectedResult *ObjectLockConfiguration
  358. }{
  359. {
  360. name: "Valid object lock configuration",
  361. xmlBody: `<ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  362. <ObjectLockEnabled>Enabled</ObjectLockEnabled>
  363. </ObjectLockConfiguration>`,
  364. expectError: false,
  365. expectedResult: &ObjectLockConfiguration{
  366. ObjectLockEnabled: "Enabled",
  367. },
  368. },
  369. {
  370. name: "Valid object lock configuration with rule",
  371. xmlBody: `<ObjectLockConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  372. <ObjectLockEnabled>Enabled</ObjectLockEnabled>
  373. <Rule>
  374. <DefaultRetention>
  375. <Mode>GOVERNANCE</Mode>
  376. <Days>30</Days>
  377. </DefaultRetention>
  378. </Rule>
  379. </ObjectLockConfiguration>`,
  380. expectError: false,
  381. expectedResult: &ObjectLockConfiguration{
  382. ObjectLockEnabled: "Enabled",
  383. Rule: &ObjectLockRule{
  384. DefaultRetention: &DefaultRetention{
  385. Mode: "GOVERNANCE",
  386. Days: 30,
  387. },
  388. },
  389. },
  390. },
  391. {
  392. name: "Empty XML body",
  393. xmlBody: "",
  394. expectError: true,
  395. errorMsg: "error parsing XML",
  396. },
  397. {
  398. name: "Invalid XML",
  399. xmlBody: "<ObjectLockConfiguration><ObjectLockEnabled>Enabled</ObjectLockEnabled>",
  400. expectError: true,
  401. errorMsg: "error parsing XML",
  402. },
  403. }
  404. for _, tt := range tests {
  405. t.Run(tt.name, func(t *testing.T) {
  406. // Create a mock HTTP request with XML body
  407. req := &http.Request{
  408. Body: io.NopCloser(strings.NewReader(tt.xmlBody)),
  409. }
  410. result, err := parseObjectLockConfiguration(req)
  411. if tt.expectError {
  412. if err == nil {
  413. t.Errorf("Expected error but got none")
  414. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  415. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  416. }
  417. } else {
  418. if err != nil {
  419. t.Errorf("Unexpected error: %v", err)
  420. }
  421. if result == nil {
  422. t.Errorf("Expected result but got nil")
  423. } else {
  424. if result.ObjectLockEnabled != tt.expectedResult.ObjectLockEnabled {
  425. t.Errorf("Expected ObjectLockEnabled %s, got %s", tt.expectedResult.ObjectLockEnabled, result.ObjectLockEnabled)
  426. }
  427. if tt.expectedResult.Rule == nil {
  428. if result.Rule != nil {
  429. t.Errorf("Expected Rule to be nil, got %v", result.Rule)
  430. }
  431. } else if result.Rule == nil {
  432. t.Errorf("Expected Rule to be non-nil")
  433. } else {
  434. if result.Rule.DefaultRetention == nil {
  435. t.Errorf("Expected DefaultRetention to be non-nil")
  436. } else {
  437. if result.Rule.DefaultRetention.Mode != tt.expectedResult.Rule.DefaultRetention.Mode {
  438. t.Errorf("Expected DefaultRetention Mode %s, got %s", tt.expectedResult.Rule.DefaultRetention.Mode, result.Rule.DefaultRetention.Mode)
  439. }
  440. if result.Rule.DefaultRetention.Days != tt.expectedResult.Rule.DefaultRetention.Days {
  441. t.Errorf("Expected DefaultRetention Days %d, got %d", tt.expectedResult.Rule.DefaultRetention.Days, result.Rule.DefaultRetention.Days)
  442. }
  443. }
  444. }
  445. }
  446. }
  447. })
  448. }
  449. }
  450. func TestValidateObjectLockConfiguration(t *testing.T) {
  451. tests := []struct {
  452. name string
  453. config *ObjectLockConfiguration
  454. expectError bool
  455. errorMsg string
  456. }{
  457. {
  458. name: "Valid config with ObjectLockEnabled only",
  459. config: &ObjectLockConfiguration{
  460. ObjectLockEnabled: "Enabled",
  461. },
  462. expectError: false,
  463. },
  464. {
  465. name: "Missing ObjectLockEnabled",
  466. config: &ObjectLockConfiguration{
  467. ObjectLockEnabled: "",
  468. },
  469. expectError: true,
  470. errorMsg: "object lock configuration must specify ObjectLockEnabled",
  471. },
  472. {
  473. name: "Valid config with rule and days",
  474. config: &ObjectLockConfiguration{
  475. ObjectLockEnabled: "Enabled",
  476. Rule: &ObjectLockRule{
  477. DefaultRetention: &DefaultRetention{
  478. Mode: "GOVERNANCE",
  479. Days: 30,
  480. DaysSet: true,
  481. },
  482. },
  483. },
  484. expectError: false,
  485. },
  486. {
  487. name: "Valid config with rule and years",
  488. config: &ObjectLockConfiguration{
  489. ObjectLockEnabled: "Enabled",
  490. Rule: &ObjectLockRule{
  491. DefaultRetention: &DefaultRetention{
  492. Mode: "COMPLIANCE",
  493. Years: 1,
  494. YearsSet: true,
  495. },
  496. },
  497. },
  498. expectError: false,
  499. },
  500. {
  501. name: "Invalid ObjectLockEnabled value",
  502. config: &ObjectLockConfiguration{
  503. ObjectLockEnabled: "InvalidValue",
  504. },
  505. expectError: true,
  506. errorMsg: "invalid object lock enabled value",
  507. },
  508. {
  509. name: "Invalid rule - missing mode",
  510. config: &ObjectLockConfiguration{
  511. ObjectLockEnabled: "Enabled",
  512. Rule: &ObjectLockRule{
  513. DefaultRetention: &DefaultRetention{
  514. Days: 30,
  515. },
  516. },
  517. },
  518. expectError: true,
  519. errorMsg: "default retention must specify Mode",
  520. },
  521. {
  522. name: "Invalid rule - both days and years",
  523. config: &ObjectLockConfiguration{
  524. ObjectLockEnabled: "Enabled",
  525. Rule: &ObjectLockRule{
  526. DefaultRetention: &DefaultRetention{
  527. Mode: "GOVERNANCE",
  528. Days: 30,
  529. Years: 1,
  530. DaysSet: true,
  531. YearsSet: true,
  532. },
  533. },
  534. },
  535. expectError: true,
  536. errorMsg: "default retention cannot specify both Days and Years",
  537. },
  538. {
  539. name: "Invalid rule - neither days nor years",
  540. config: &ObjectLockConfiguration{
  541. ObjectLockEnabled: "Enabled",
  542. Rule: &ObjectLockRule{
  543. DefaultRetention: &DefaultRetention{
  544. Mode: "GOVERNANCE",
  545. },
  546. },
  547. },
  548. expectError: true,
  549. errorMsg: "default retention must specify either Days or Years",
  550. },
  551. {
  552. name: "Invalid rule - invalid mode",
  553. config: &ObjectLockConfiguration{
  554. ObjectLockEnabled: "Enabled",
  555. Rule: &ObjectLockRule{
  556. DefaultRetention: &DefaultRetention{
  557. Mode: "INVALID_MODE",
  558. Days: 30,
  559. DaysSet: true,
  560. },
  561. },
  562. },
  563. expectError: true,
  564. errorMsg: "invalid default retention mode",
  565. },
  566. {
  567. name: "Invalid rule - days out of range",
  568. config: &ObjectLockConfiguration{
  569. ObjectLockEnabled: "Enabled",
  570. Rule: &ObjectLockRule{
  571. DefaultRetention: &DefaultRetention{
  572. Mode: "GOVERNANCE",
  573. Days: 50000,
  574. DaysSet: true,
  575. },
  576. },
  577. },
  578. expectError: true,
  579. errorMsg: fmt.Sprintf("default retention days must be between 0 and %d", MaxRetentionDays),
  580. },
  581. {
  582. name: "Invalid rule - years out of range",
  583. config: &ObjectLockConfiguration{
  584. ObjectLockEnabled: "Enabled",
  585. Rule: &ObjectLockRule{
  586. DefaultRetention: &DefaultRetention{
  587. Mode: "GOVERNANCE",
  588. Years: 200,
  589. YearsSet: true,
  590. },
  591. },
  592. },
  593. expectError: true,
  594. errorMsg: fmt.Sprintf("default retention years must be between 0 and %d", MaxRetentionYears),
  595. },
  596. {
  597. name: "Invalid rule - missing DefaultRetention",
  598. config: &ObjectLockConfiguration{
  599. ObjectLockEnabled: "Enabled",
  600. Rule: &ObjectLockRule{
  601. DefaultRetention: nil,
  602. },
  603. },
  604. expectError: true,
  605. errorMsg: "rule configuration must specify DefaultRetention",
  606. },
  607. }
  608. for _, tt := range tests {
  609. t.Run(tt.name, func(t *testing.T) {
  610. err := ValidateObjectLockConfiguration(tt.config)
  611. if tt.expectError {
  612. if err == nil {
  613. t.Errorf("Expected error but got none")
  614. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  615. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  616. }
  617. } else {
  618. if err != nil {
  619. t.Errorf("Unexpected error: %v", err)
  620. }
  621. }
  622. })
  623. }
  624. }
  625. func TestValidateDefaultRetention(t *testing.T) {
  626. tests := []struct {
  627. name string
  628. retention *DefaultRetention
  629. expectError bool
  630. errorMsg string
  631. }{
  632. {
  633. name: "Valid retention with days",
  634. retention: &DefaultRetention{
  635. Mode: "GOVERNANCE",
  636. Days: 30,
  637. DaysSet: true,
  638. },
  639. expectError: false,
  640. },
  641. {
  642. name: "Valid retention with years",
  643. retention: &DefaultRetention{
  644. Mode: "COMPLIANCE",
  645. Years: 1,
  646. YearsSet: true,
  647. },
  648. expectError: false,
  649. },
  650. {
  651. name: "Missing mode",
  652. retention: &DefaultRetention{
  653. Days: 30,
  654. DaysSet: true,
  655. },
  656. expectError: true,
  657. errorMsg: "default retention must specify Mode",
  658. },
  659. {
  660. name: "Invalid mode",
  661. retention: &DefaultRetention{
  662. Mode: "INVALID",
  663. Days: 30,
  664. DaysSet: true,
  665. },
  666. expectError: true,
  667. errorMsg: "invalid default retention mode",
  668. },
  669. {
  670. name: "Both days and years specified",
  671. retention: &DefaultRetention{
  672. Mode: "GOVERNANCE",
  673. Days: 30,
  674. Years: 1,
  675. DaysSet: true,
  676. YearsSet: true,
  677. },
  678. expectError: true,
  679. errorMsg: "default retention cannot specify both Days and Years",
  680. },
  681. {
  682. name: "Neither days nor years specified",
  683. retention: &DefaultRetention{
  684. Mode: "GOVERNANCE",
  685. },
  686. expectError: true,
  687. errorMsg: "default retention must specify either Days or Years",
  688. },
  689. }
  690. for _, tt := range tests {
  691. t.Run(tt.name, func(t *testing.T) {
  692. err := validateDefaultRetention(tt.retention)
  693. if tt.expectError {
  694. if err == nil {
  695. t.Errorf("Expected error but got none")
  696. } else if !strings.Contains(err.Error(), tt.errorMsg) {
  697. t.Errorf("Expected error message to contain '%s', got: %v", tt.errorMsg, err)
  698. }
  699. } else {
  700. if err != nil {
  701. t.Errorf("Unexpected error: %v", err)
  702. }
  703. }
  704. })
  705. }
  706. }
  707. // Helper function to create a time pointer
  708. func timePtr(t time.Time) *time.Time {
  709. return &t
  710. }