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