@@ -274,3 +274,123 @@ func TestExportImportRoundTrip(t *testing.T) {
274274 t .Error ("History should contain all versions" )
275275 }
276276}
277+
278+ func TestRestoreCommand (t * testing.T ) {
279+ cleanup := SetupTestDB (t )
280+ defer cleanup ()
281+
282+ t .Run ("restore fails when no backup exists" , func (t * testing.T ) {
283+ output := RunKVFailure (t , "db" , "restore" )
284+ if ! strings .Contains (output , "No backup file found" ) {
285+ t .Errorf ("Expected 'No backup file found' error, got: %s" , output )
286+ }
287+ })
288+
289+ // Setup test data and create a backup via import
290+ RunKVSuccess (t , "set" , "original1" , "value1" )
291+ RunKVSuccess (t , "set" , "original2" , "value2" , "--password" , "pass" )
292+ RunKVSuccess (t , "set" , "original3" , "value3" )
293+ RunKVSuccess (t , "hide" , "original3" )
294+
295+ tmpDir := t .TempDir ()
296+ exportPath := filepath .Join (tmpDir , "backup.db" )
297+ RunKVSuccess (t , "db" , "export" , exportPath )
298+
299+ // Import to create a backup
300+ RunKVSuccess (t , "db" , "import" , exportPath )
301+
302+ t .Run ("restore fails with invalid backup file" , func (t * testing.T ) {
303+ // Get DB path and backup path
304+ dbPath := common .GetDBPath ()
305+ backupPath := dbPath + ".backup"
306+
307+ // Save current backup
308+ validBackupPath := backupPath + ".valid"
309+ err := common .CopyFile (backupPath , validBackupPath )
310+ if err != nil {
311+ t .Fatal (err )
312+ }
313+ defer func () {
314+ // Restore valid backup
315+ _ = os .Remove (backupPath )
316+ _ = common .CopyFile (validBackupPath , backupPath )
317+ _ = os .Remove (validBackupPath )
318+ }()
319+
320+ // Replace backup with invalid content
321+ err = os .WriteFile (backupPath , []byte ("not a database" ), 0o644 )
322+ if err != nil {
323+ t .Fatal (err )
324+ }
325+
326+ output := RunKVFailure (t , "db" , "restore" )
327+ if ! strings .Contains (output , "Invalid backup" ) {
328+ t .Errorf ("Expected 'Invalid backup' error, got: %s" , output )
329+ }
330+ })
331+
332+ t .Run ("restore successfully restores from backup" , func (t * testing.T ) {
333+ // Modify database
334+ RunKVSuccess (t , "set" , "new-key" , "new-value" )
335+ RunKVSuccess (t , "delete" , "original1" )
336+
337+ // Verify changes
338+ output := RunKVSuccess (t , "get" , "new-key" )
339+ if output != "new-value" {
340+ t .Error ("new-key should exist before restore" )
341+ }
342+
343+ RunKVFailure (t , "get" , "original1" )
344+
345+ // Restore
346+ output = RunKVSuccess (t , "db" , "restore" )
347+ if ! strings .Contains (output , "restored from" ) {
348+ t .Errorf ("Expected success message, got: %s" , output )
349+ }
350+
351+ // Verify restoration
352+ output = RunKVSuccess (t , "get" , "original1" )
353+ if output != "value1" {
354+ t .Errorf ("Expected 'value1', got: %s" , output )
355+ }
356+
357+ output = RunKVFailure (t , "get" , "new-key" )
358+ if ! strings .Contains (output , "does not exist" ) {
359+ t .Error ("new-key should not exist after restore" )
360+ }
361+ })
362+
363+ t .Run ("restore preserves backup file" , func (t * testing.T ) {
364+ dbPath := common .GetDBPath ()
365+ backupPath := dbPath + ".backup"
366+
367+ // Verify backup still exists
368+ if _ , err := os .Stat (backupPath ); os .IsNotExist (err ) {
369+ t .Error ("Backup file should still exist after restore" )
370+ }
371+
372+ // Verify it's still valid
373+ if err := common .ValidateSqliteFile (backupPath ); err != nil {
374+ t .Errorf ("Backup file should still be valid: %v" , err )
375+ }
376+ })
377+
378+ t .Run ("restore preserves all data attributes" , func (t * testing.T ) {
379+ // Verify locked key
380+ output := RunKVSuccess (t , "list" , "original2" )
381+ if ! strings .Contains (output , "[Locked]" ) {
382+ t .Error ("original2 should be locked" )
383+ }
384+
385+ output = RunKVSuccess (t , "get" , "original2" , "--password" , "pass" )
386+ if output != "value2" {
387+ t .Errorf ("Expected 'value2', got: %s" , output )
388+ }
389+
390+ // Verify hidden key
391+ output = RunKVSuccess (t , "list" , "original3" )
392+ if ! strings .Contains (output , "[Hidden]" ) {
393+ t .Error ("original3 should be hidden" )
394+ }
395+ })
396+ }
0 commit comments