@@ -672,6 +672,96 @@ func TestRollbackTo(t *testing.T) {
672672 })
673673}
674674
675+ func TestMigrateOnly (t * testing.T ) {
676+ testEachURL (t , func (t * testing.T , u * url.URL ) {
677+ const v2 = "20200227231541" // posts
678+
679+ db := newTestDB (t , u )
680+ drv , err := db .Driver ()
681+ require .NoError (t , err )
682+
683+ require .NoError (t , db .Drop ())
684+ require .NoError (t , db .Create ())
685+
686+ // Migrate only v2, without applying v1 first (should work if not strict)
687+ migrations , err := db .FindMigrations ()
688+ require .NoError (t , err )
689+ require .NoError (t , db .MigrateOnly (migrations , v2 ))
690+
691+ sqlDB , err := drv .Open ()
692+ require .NoError (t , err )
693+ defer dbutil .MustClose (sqlDB )
694+
695+ applied , err := drv .SelectMigrations (sqlDB , - 1 )
696+ require .NoError (t , err )
697+ require .Equal (t , map [string ]bool {v2 : true }, applied )
698+
699+ // Check posts table exists, users table does not
700+ var cnt int
701+ require .NoError (t , sqlDB .QueryRow ("select count(*) from posts" ).Scan (& cnt ))
702+ require .Error (t , sqlDB .QueryRow ("select count(*) from users" ).Scan (& cnt ))
703+
704+ // Attempt applying already applied migration (should be no-op)
705+ migrations , err = db .FindMigrations ()
706+ require .NoError (t , err )
707+ require .NoError (t , db .MigrateOnly (migrations , v2 ))
708+
709+ // Attempt applying nonexistent migration
710+ err = db .MigrateOnly (migrations , "99999999999999" )
711+ require .ErrorIs (t , err , dbmate .ErrMigrationNotFound )
712+ })
713+ }
714+
715+ func TestRollbackOnly (t * testing.T ) {
716+ testEachURL (t , func (t * testing.T , u * url.URL ) {
717+ const v1 = "20151129054053" // users
718+ const v2 = "20200227231541" // posts
719+
720+ db := newTestDB (t , u )
721+ drv , err := db .Driver ()
722+ require .NoError (t , err )
723+
724+ require .NoError (t , db .Drop ())
725+ require .NoError (t , db .Create ())
726+
727+ // Apply all migrations first
728+ require .NoError (t , db .Migrate ())
729+
730+ sqlDB , err := drv .Open ()
731+ require .NoError (t , err )
732+ defer dbutil .MustClose (sqlDB )
733+
734+ applied , err := drv .SelectMigrations (sqlDB , - 1 )
735+ require .NoError (t , err )
736+ require .Equal (t , map [string ]bool {v1 : true , v2 : true }, applied )
737+
738+ migrations , err := db .FindMigrations ()
739+ require .NoError (t , err )
740+
741+ // Rollback only v2 migration
742+ require .NoError (t , db .RollbackOnly (migrations , v2 ))
743+
744+ applied , err = drv .SelectMigrations (sqlDB , - 1 )
745+ require .NoError (t , err )
746+ require .Equal (t , map [string ]bool {v1 : true }, applied )
747+
748+ // Ensure "posts" table no longer exists
749+ var cnt int
750+ require .Error (t , sqlDB .QueryRow ("select count(*) from posts" ).Scan (& cnt ))
751+ require .NoError (t , sqlDB .QueryRow ("select count(*) from users" ).Scan (& cnt ))
752+
753+ // Attempt rolling back migration that's already rolled back (should fail)
754+ migrations , err = db .FindMigrations ()
755+ require .NoError (t , err )
756+ err = db .RollbackOnly (migrations , v2 )
757+ require .ErrorContains (t , err , "is not applied" )
758+
759+ // Attempt rolling back nonexistent migration
760+ err = db .RollbackOnly (migrations , "99999999999999" )
761+ require .ErrorIs (t , err , dbmate .ErrMigrationNotFound )
762+ })
763+ }
764+
675765func TestFindMigrations (t * testing.T ) {
676766 testEachURL (t , func (t * testing.T , u * url.URL ) {
677767 db := newTestDB (t , u )
0 commit comments