@@ -578,3 +578,168 @@ func TestSchemaMigrationIdempotency(t *testing.T) {
578
578
}
579
579
})
580
580
}
581
+
582
+ // TestMigrationBug19RC1 tests a bug that was present in the migration code
583
+ // at the v0.19.0-rc1 release.
584
+ // The bug was fixed in: https://github.com/lightningnetwork/lnd/pull/9647
585
+ // NOTE: This test may be removed once the final version of 0.19.0 is released.
586
+ func TestMigrationSucceedsAfterDirtyStateMigrationFailure19RC1 (t * testing.T ) {
587
+ // setMigrationTrackerVersion is a helper function that
588
+ // updates the migration tracker table to a specific version that
589
+ // simulates the conditions of the bug.
590
+ setMigrationTrackerVersion := func (t * testing.T , db * BaseDB ) {
591
+ _ , err := db .Exec (`
592
+ DELETE FROM migration_tracker;
593
+ INSERT INTO migration_tracker (version, migration_time)
594
+ VALUES (2, CURRENT_TIMESTAMP);
595
+ ` )
596
+ require .NoError (t , err )
597
+ }
598
+
599
+ const (
600
+ maxSchemaVersionBefore19RC1 = 4
601
+ failingSchemaVersion = 3
602
+ )
603
+
604
+ ctxb := context .Background ()
605
+ migrations := GetMigrations ()
606
+ migrations = migrations [:maxSchemaVersionBefore19RC1 ]
607
+ lastMigration := migrations [len (migrations )- 1 ]
608
+
609
+ // Make sure that the last migration is the one we expect.
610
+ require .Equal (
611
+ t , maxSchemaVersionBefore19RC1 , lastMigration .SchemaVersion ,
612
+ )
613
+
614
+ t .Run ("SQLite" , func (t * testing.T ) {
615
+ // First instantiate the database and run the migrations
616
+ // including the custom migrations.
617
+ t .Logf ("Creating new SQLite DB for testing migrations" )
618
+
619
+ dbFileName := filepath .Join (t .TempDir (), "tmp.db" )
620
+ var (
621
+ db * SqliteStore
622
+ err error
623
+ )
624
+
625
+ db , err = NewSqliteStore (& SqliteConfig {
626
+ SkipMigrations : false ,
627
+ }, dbFileName )
628
+ require .NoError (t , err )
629
+
630
+ dbToCleanup := db .DB
631
+ t .Cleanup (func () {
632
+ require .NoError (t , dbToCleanup .Close ())
633
+ })
634
+
635
+ require .NoError (t , db .ApplyAllMigrations (ctxb , migrations ))
636
+
637
+ version , dirty , err := db .GetSchemaVersion ()
638
+ require .NoError (t , err )
639
+
640
+ // Now reset the schema version to 0 and make sure that
641
+ // we can apply the migrations again.
642
+ require .Equal (t , lastMigration .SchemaVersion , version )
643
+ require .False (t , dirty )
644
+
645
+ // Set the schema version to the failing version and
646
+ // make make the version dirty which essentially tells
647
+ // golang-migrate that the migration failed.
648
+ require .NoError (
649
+ t , db .SetSchemaVersion (failingSchemaVersion , true ),
650
+ )
651
+
652
+ // Set the migration tracker to the failing version.
653
+ setMigrationTrackerVersion (t , db .BaseDB )
654
+
655
+ // Close the DB so we can reopen it and apply the
656
+ // migrations again.
657
+ db .DB .Close ()
658
+
659
+ db , err = NewSqliteStore (& SqliteConfig {
660
+ SkipMigrations : false ,
661
+ }, dbFileName )
662
+ require .NoError (t , err )
663
+
664
+ dbToCleanup2 := db .DB
665
+ t .Cleanup (func () {
666
+ require .NoError (t , dbToCleanup2 .Close ())
667
+ })
668
+
669
+ require .NoError (t , db .ApplyAllMigrations (ctxb , migrations ))
670
+
671
+ version , dirty , err = db .GetSchemaVersion ()
672
+ require .NoError (t , err )
673
+ require .Equal (t , lastMigration .SchemaVersion , version )
674
+ require .False (t , dirty )
675
+ })
676
+
677
+ t .Run ("Postgres" , func (t * testing.T ) {
678
+ // First create a temporary Postgres database to run
679
+ // the migrations on.
680
+ fixture := NewTestPgFixture (
681
+ t , DefaultPostgresFixtureLifetime ,
682
+ )
683
+ t .Cleanup (func () {
684
+ fixture .TearDown (t )
685
+ })
686
+
687
+ dbName := randomDBName (t )
688
+
689
+ // Next instantiate the database and run the migrations
690
+ // including the custom migrations.
691
+ t .Logf ("Creating new Postgres DB '%s' for testing " +
692
+ "migrations" , dbName )
693
+
694
+ _ , err := fixture .db .ExecContext (
695
+ context .Background (), "CREATE DATABASE " + dbName ,
696
+ )
697
+ require .NoError (t , err )
698
+
699
+ cfg := fixture .GetConfig (dbName )
700
+ var db * PostgresStore
701
+
702
+ cfg .SkipMigrations = false
703
+ db , err = NewPostgresStore (cfg )
704
+ require .NoError (t , err )
705
+
706
+ require .NoError (t , db .ApplyAllMigrations (ctxb , migrations ))
707
+
708
+ version , dirty , err := db .GetSchemaVersion ()
709
+ require .NoError (t , err )
710
+
711
+ // Now reset the schema version to 0 and make sure that
712
+ // we can apply the migrations again.
713
+ require .Equal (t , lastMigration .SchemaVersion , version )
714
+ require .False (t , dirty )
715
+
716
+ // Set the schema version to the failing version and
717
+ // make make the version dirty which essentially tells
718
+ // golang-migrate that the migration failed.
719
+ require .NoError (
720
+ t , db .SetSchemaVersion (failingSchemaVersion , true ),
721
+ )
722
+
723
+ // Set the migration tracker to the failing version.
724
+ setMigrationTrackerVersion (t , db .BaseDB )
725
+
726
+ // Close the DB so we can reopen it and apply the
727
+ // migrations again.
728
+ db .DB .Close ()
729
+
730
+ db , err = NewPostgresStore (cfg )
731
+ require .NoError (t , err )
732
+
733
+ dbToCleanup2 := db .DB
734
+ t .Cleanup (func () {
735
+ require .NoError (t , dbToCleanup2 .Close ())
736
+ })
737
+
738
+ require .NoError (t , db .ApplyAllMigrations (ctxb , migrations ))
739
+
740
+ version , dirty , err = db .GetSchemaVersion ()
741
+ require .NoError (t , err )
742
+ require .Equal (t , lastMigration .SchemaVersion , version )
743
+ require .False (t , dirty )
744
+ })
745
+ }
0 commit comments